Wiki-учебник по веб-технологиям: PHP/КлассыОбъекты ...

Главная | Каталог |

Классы и объекты


Оглавление документа

1. Введение


Начнем с основных понятий объектно-ориентированного программирования – класса и объекта. Существует множество определений этих понятий. Мы дадим следующее: объект – это структурированная переменная, содержащая всю информацию о некотором физическом предмете или реализуемом в программе понятии, класс – это описание таких объектов и действий, которые можно с ними выполнять.

В PHP класс определяется с помощью следующего синтаксиса:

class Имя_класса{
var $имя_свойства;
/*список свойств*/
function имя_метода( ){
/* определение метода */
}
/*список методов*/

}
Имена свойств объектов класса объявляются с помощью ключевого слова var, методы, применимые к объектам данного класса, описываются функциями. Внутри определения класса можно использовать ключевое слово this для обращения к текущему представителю класса.

Например, нам нужно создать класс, описывающий категорию статей. У каждой статьи имеются такие свойства, как название, автор и краткое содержание. Какие действия мы хотим совершать со статьями? Возможно, нам понадобится задавать значения перечисленным свойствам статьи, отображать статью в браузере. Тогда определение этого класса может выглядеть следующим образом:

<?
class Articles // Создаем класс Статей
var $title;
var 
$author;
var 
$description;
// метод, который присваивает значения
// атрибутам класса
function make_article($t$a$d){
$this->title $t;
$this->author $a;
$this->description $d;
}
//метод для отображения экземпляров класса
function show_article(){
$art $this->title "<br>" .
$this->description .
"<br>Автор: " $this->author;
echo 
$art;
}
}
?>


Итак, для описания физических объектов типа «статья» мы создали класс с именем Articles, состоящий из трех переменных, содержащих характеристики статьи, и двух функций для создания конкретной статьи и для ее отображения.

Как известно, работая с PHP, можно периодически переключаться в режим HTML.

В этом случае программа состоит из нескольких кусков (блоков) кода.

Определение класса нельзя разносить по разным блокам php-кода и тем более по разным файлам. То есть если написать:

<?php
class Articles // Начало описания класса
var $title;
?>
<?php
// продолжение описания класса
function show_article(){
// содержание метода
}
// конец описания класса
?>


то программа не будет работать корректно.

Несколько замечаний по поводу имен классов. Имя класса должно удовлетворять правилам именования объектов в языке PHP, но есть ряд имен, которые зарезервированы разработчиками для своих целей. В первую очередь это имена, начинающиеся с символа подчеркивания «_». Для создания классов и функций нельзя использовать такие имена. Кроме того, зарезервировано имя stdClass, поскольку оно используется внутри движка PHP.

1.1. Инициализация переменных


Часто некоторым атрибутам класса бывает необходимо присваивать значения сразу после создания представителя класса. Когда мы создавали класс статей, для присваивания значений атрибутам (свойствам) класса мы использовали специальную функцию make_article(). Вообще говоря, мы поступили не совсем верно, потому что занялись изобретением велосипеда. Специально для задания начальных значений атрибутам класса существует два стандартных метода. В PHP4 можно инициализировать значения с помощью оператора var или с помощью функции конструктора. С помощью var можно инициализировать только константные значения. Для задания не константных значений используют функцию конструктор, которая вызывается автоматически, когда объект конструируется из класса. Функция-конструктор должна иметь имя, совпадающее с именем всего класса, в котором она определена.

Приведем пример. Допустим, при создании объекта «статья» мы хотим установить его свойства следующим образом: автора – равным строке «Иванов», название и краткое содержание – соответствующим элементам глобального массива $_POST, а дату публикации статьи – текущей дате. Тогда следующее описание класса не является корректным в PHP4:

<?
class Articles // Создаем класс Статей
var $title$_POST["title"];
var 
$author "Иванов";
var 
$description $_POST["description"];
var 
$published date("Y-m-d");
// метод, который присваивает значения
// атрибутам класса
}
?>


А вот такое описание класса в PHP4 будет работать так, как нужно:

<?
class Articles // Создаем класс Статей
var $title;
var 
$author "Иванов";
var 
$description;
var 
$published;
// метод, который присваивает значения
// атрибутам класса
function Articles(){
$this->title $_POST["title"];
$this->description $_POST["description"];
$this ->published date("Y-m-d");
}
}
?>


Отметим, что в PHP3 и PHP4 конструкторы работают по-разному. В PHP3 функция становилась конструктором, если она имела то же имя, что и класс, а в PHP4 – если она имеет то же имя, что и класс, в котором она определена. Разница в подходах видна, когда один класс расширяет другой и происходит наследование свойств и методов базового класса. Но об этом мы поговорим чуть позже. В PHP5 конструктор класса именуется _construct. Кроме того, в PHP5 появились и деструкторы – функции, которые вызываются автоматически перед уничтожением объекта. В PHP5 функция-деструктор должна быть названа _destruct.

1.2. Объекты


В одной из первых лекций мы упоминали о существовании в PHP такого типа данных, как объект. Класс – это описание данных одного типа, данных типа объект.

Классы являются как бы шаблонами для реальных переменных. Переменная нужного типа создается из класса с помощью оператора new.

Создав объект, мы можем применять к нему все методы и получать все свойства, определенные в описании класса. Для этого используют такой синтаксис: $имя_объекта- >название_свойства или $имя_объекта->название_метода(список аргументов).

Заметим, что перед названием свойства или метода знака $ не ставят.

<?php
$art 
= new Articles;
// создаем объект $art
echo ($art ->title);
// выводим название объекта $art
$another_art = new Articles;
// создаем объект $another_art
$another_art->show_article();
// вызываем метод для
// отображения объекта в браузер
?>


Пример 6.1. Доступ к методам и свойствам объекта

Каждый из объектов класса имеет одни и те же свойства и методы. Так, у объекта $art и у объекта $another_art есть свойства title, description, author и методы Articles(), show_article(). Но это два разных объекта. Представим себе объект как директорию в файловой системе, а его характеристики – как файлы в этой директории. Очевидно, что в каждой директории могут лежать одинаковые файлы, но тем не менее они считаются различными, поскольку хранятся в разных директориях. Точно так же свойства и методы считаются различными, если они применяются к разным объектам. Чтобы получить нужный файл из директории верхнего уровня, мы пишем полный путь к этому файлу. При работе с классами нужно указывать полное имя функции, которую мы хотим вызвать. Директорией верхнего уровня в PHP будет пространство глобальных переменных, а путь указывается с помощью разделителя ->. Таким образом, имена $art->title и $another_art->title обозначают две разные переменные. Переменная в PHP имеет только один знак доллара перед именем, поэтому нельзя писать $art->$title. Эта конструкция будет рассмотрена не как обращение к свойству title объекта $art, а как обращение к свойству, имя которого задано переменной $title (например, $art- >).

<?php
$art
->title "Введение в Internet";
// так можно установить
// значение свойства объекта
$art->$title "Введение в Internet";
// так нельзя установить
// значение свойства объекта
$property "title";
$art->$property "Введение в Internet";
// так можно установить значение
// свойства объекта
?>


Пример 6.2. Установка значений свойств

Создавая класс, мы не можем знать, какое имя будет иметь объект этого класса, тем более что объектов может быть много и все могут иметь разные имена.

Соответственно мы не знаем, как обращаться к объекту внутри определения класса. Для того чтобы иметь доступ к функциям и переменным внутри определения класса, нужно использовать псевдопеременную $this. Например, $this->title возвращает значение свойства title у текущего объекта данного класса. Иногда эту переменную предлагают читать как «мое собственное» (к примеру, по
отношению к свойству).

2. Наследование

2.1. extends


Механизм наследования – очень важная часть всего объектно-ориентированного подхода. Попытаемся объяснить его суть на примере.

Допустим, мы создаем описание человека. Очевидно, что сделать это мы можем по-разному, в зависимости от того, для чего нужно это описание. Можно описать человека как программиста: он знает такие-то языки программирования, операционные системы, участвовал в стольких-то проектах. Однако если человек программист, то он не перестает быть человеком вообще, т.е. он имеет имя, фамилию, место жительства и т.п. Если перевести наши рассуждения в термины объектно- ориентированного программирования, то можно сказать, что мы описали два класса – класс людей и класс программистов, каждый со своими свойствами и методами. Причем класс программистов, очевидно, обладает всеми свойствами класса людей и при этом имеет свои специфические характеристики, т.е. класс программистов является подклассом класса людей. Так, если у человека вообще есть имя, то у программиста оно тоже должно быть, но не наоборот.

Кроме программистов можно выделить еще множество классов по профессиональной принадлежности людей. И все они будут подклассами класса людей. Часто на практике удобно определять общий класс, который может использоваться сразу в нескольких проектах (например, класс людей или личностей), и адаптировать его для специфических нужд каждого проекта (например, как класс программистов).

Как это можно реализовать? С помощью механизма расширений. Любой класс может быть расширением другого класса. Расширяющий (или производный) класс, кроме тех свойств и методов, которые описаны в его определении, имеет все функции и свойства основного (базового класса). В нашем примере класс программистов – расширяющий, а класс всех людей – базовый. Из класса нельзя удалить никакие существующие свойства и функции, класс можно только расширить. Расширяющий класс в PHP4 всегда зависит только от одного базового класса, поскольку множественное наследование в PHP не поддерживается.

Расширяются классы в PHP с помощью ключевого слова extends.

<?php
class Person // определяем класс Личности
var $first_name// имя личности
var $last_name// фамилия личности
function make_person($t,$a){
// метод устанавливает
// значения имени и фамилии объекта
$this->first_name $t;
$this->last_name $a;
}
function 
show_person(){
// метод отображает информацию о личности
echo ("<h2>" $this->first_name " " .
$this->last_name "</h2>");
}
}
class 
Programmer extends Person{
// определяем класс
// Programmer, расширяющий Person
var $langs = array ("Lisp");
// константным массивом
// задать переменную в var можно
function set_lang($new_lang){
// метод добавляет еще
// один язык к списку известных
$this->langs[] = $new_lang;
}
}
?>


Пример 6.3. Использование механизма наследования

Класс Programmer имеет те же переменные и функции, что и класс Person, плюс переменную $langs, в которой содержится список изученных программистом языков, и функцию set_lang для добавления еще одного языка к списку изученных.

Создать представителя класса программистов можно обычным способом с помощью конструкции new. После этого можно устанавливать и получать список языков, которые знает программист, и в то же время можно использовать функции, заданные для класса Person, т.е. устанавливать и получать имя и фамилию программиста и отображать сведения о нем в браузере:

<?php
$progr 
= new Programmer;
$progr -> set_lang("PHP");
// методы, определенные для
// класса Programmer
print_r ($progr->langs);
// методы, определенные для класса Person
$progr->make_person("Bill","Gates");
$progr->show_person();
?>


Отношения, в которых состоят созданные нами классы Person и Programmer, называют также отношениями родитель–потомок. Класс Person – родитель, а его потомки, такие как класс Programmer, создаются, основываясь на нем, с помощью расширений. Любой класс может стать родительским и соответственно породить потомков.

Порядок определения классов имеет значение. Нельзя сначала определить класс Programmer, расширяющий класс Person, а уже потом сам класс Person. Класс должен быть определен перед тем, как он будет использоваться (расширяться).

3. Конструкторы


Теперь, после знакомства с механизмом наследования в PHP, мы можем прокомментировать различие между конструкторами PHP4 и PHP3 и более подробно рассказать о конструкторах вообще. Напомним, что в PHP3 конструктор – это функция, имя которой совпадает с именем класса. А в PHP4 – функция, имя которой совпадает с именем класса, в котором она определена.

<?php
class Programmer extends Person{
// определяем класс
// Programmer, расширяющий Person
var $langs = array ("Lisp");
function 
Programmer(){
// этот конструктор будет
// работать и в PHP3, и в PHP4
$this->make_person("Иван","Петров");
}
}
?>


Пример 6.4. Использование конструктора

Здесь функция Programmer() является конструктором, т.е. выполняется сразу после создания любого представителя класса Programmer, задавая ему имя «Иван» и фамилию «Петров». Конструкторы, как и любые другие функции, могут иметь аргументы. В этом случае, создавая представителя класса, нужно указать значения этих параметров. Аргументы конструктора могут иметь и значения по умолчанию. Если все аргументы имеют значения по умолчанию, тогда можно создавать экземпляр класса без параметров.

<?php
class Programmer extends Person{
// определяем класс
// Programmer, расширяющий Person
var $langs = array ("Lisp");
function 
Programmer($n "Иван",
$f "Петров"){
// это конструктор
$this->make_person($n,$f);
}
}
$default_progr = new Programmer();
// создаст программиста Ивана Петрова
$new_progr = new Programmer("Вася",
"Сидоров");
// создаст программиста Васю Сидорова
print_r($new_progr);
/* выведет информацию о переменной
$new_progr, т.е. свойства объекта
и их значения */
?>


Пример 6.5. Использование конструктора

Приведенные примеры будут работать и в PHP3, и в PHP4, конечно если дописать в них определение базового класса Person. Допустим, ситуация немного другая: конструктор имеется только у базового класса Person:

<?php
class Person // определяем класс Личности
var $first_name;
var 
$last_name;
function 
Person($t,$a){ // конструктор
$this->first_name $t;
$this->last_name $a;
}
/* ... */
}
class 
Programmer extends Person{
// определяем класс
// Programmer, расширяющий Person
var $langs = array ("Lisp");
function 
set_lang($new_lang){
$this->langs[] = $new_lang;
}
}
$new_progr = new Programmer("Вася",
"Сидоров");
?>


Что произойдет в этом случае при создании объекта класса Programmer, будет ли автоматически вызвана какая-либо функция? В PHP3 ничего не произойдет, поскольку в этом классе нет функции с именем Programmer() (здесь конструктор – это функция, имя которой совпадает с именем класса). В PHP4 будет вызван конструктор базового класса, если он существует, т.е. вызовется функция Person() из класса Person (здесь конструктор – функция, имя которой совпадает с именем класса, в котором она определена).

Еще одна ситуация – в базовом классе есть функция, имя которой совпадает с именем расширяющего класса, а в расширяющем классе нет конструктора.

<?php
class Person // определяем класс Личности
var $first_name;
var 
$last_name;
function 
Person($t,$a){ // конструктор
$this->first_name $t;
$this->last_name $a;
}
function 
Programmer($new_lang){
echo 
"Я – программист";
}
}
class 
Programmer extends Person{
// определяем класс
// Programmer, расширяющий Person
var $langs = array ("Lisp");
function 
set_lang($new_lang){
$this->langs[] = $new_lang;
}
}
$new_progr = new Programmer("Вася",
"Сидоров");
?>


В этом случае PHP3 вызовет в качестве конструктора функцию Programmer() из описания класса Person. Поскольку конструктор – это функция, у которой то же имя, что и у класса. И неважно, определена ли эта функция в самом классе или она наследуется из базового класса. В PHP4 класс Programmer не будет иметь своего конструктора, поэтому вызовется конструктор базового класса.

Ни в PHP 3, ни в PHP 4 конструктор базового класса не вызывается автоматически из конструктора порожденного класса.

4. Оператор ::

Иногда внутри описания класса возникает необходимость сослаться на функции или переменные из базового класса. Бывает, что нужно ссылаться на функции в классе, ни один представитель которого еще не создан. Как быть в таком случае?

В PHP4 для этого существует специальный оператор «::»

Например, вот так можно вызвать в описании класса Programmer функцию show_name() из базового класса Person и функцию say_hello(), заданную в описании класса Programmer, когда ни один объект этого класса еще не был создан:

<?php
class Person // определяем класс Личности
var $first_name;
var 
$last_name;
function 
Person($t,$a){ // конструктор
$this->first_name $t;
$this->last_name $a;
}
function 
show_name(){
// метод отображает информацию о личности
echo ("Меня зовут, " .
$this->first_name " " .
$this->last_name "!<br>");
}
}
class 
Programmer extends Person{
// определяем класс
// Programmer, расширяющий Person
function set_lang($new_lang){
// метод добавляет еще
// один язык к списку известных
$this->langs[] = $new_lang;
Person::show_name();
// вызываем функцию из базового класса
echo "И я знаю теперь еще и " .
$new_lang;
}
function 
show_name(){
echo (
"Я программист, " .
$this->first_name " " .
$this->last_name "!<br>");
}
function 
say_hello(){
echo 
"Привет!<br>";
}
}
Programmer::say_hello();
// вызываем функцию, когда ни
// один объект ее класса еще не создан
$new_progr = new Programmer("Вася","Сидоров");
$new_progr->set_lang("PHP");
?>


В результате работы этой программы получим следующее:
Привет!
Меня зовут Вася Сидоров!
И я знаю теперь еще и PHP

С помощью команды Programmer::say_hello(); мы вызываем функцию say_hello класса Programmer как таковую, а не как метод, применяемый к объекту данного класса. В этот момент переменных класса нет. Поэтому функции, вызываемые до создания объекта, не могут пользоваться переменными класса и конструкцией this, но могут пользоваться локальными и глобальными переменными.

В определении класса Programmer мы переопределили функцию show_name(), поэтому вызвать функцию show_name() из базового класса Person можно только с помощью оператора «::» Вообще говоря, внутри определения класса мы можем вызывать любые методы и свойства, заданные в его базовом классе с помощью обычного $this, если только порожденный класс не переопределяет эти свойства и
методы, как в нашем примере.

5. Оператор parent


В приведенном выше примере, обращаясь к базовому классу, мы использовали его имя (мы писали Person::show_name()). Это не совсем удобно, потому что имя класса или иерархия классов может измениться, и тогда придется переписывать код описаний всех классов с тем, чтобы привести используемые в них имена в соответствие с новой иерархией. Чтобы избежать подобной ситуации, вместо имени базового класса нужно использовать ключевое слово parent (например, parent::show_name()). Parent ссылается на класс, прописанный после extends в объявлении вашего класса. Поэтому если вдруг иерархия классов изменится, то достаточно будет внести изменения в имена, указанные после extends в описаниях классов.