PHP 2016. Уровень 3.1: ООП
Объектно-ориентированное программирование на PHP. конспект-памятка видеокурса 'Программирование на PHP 2016'. Лектор Борисов Игорь Олегович. Учебный Центр «Специалист» при МГТУ им. Н.Э.Баумана.
конспектировал Капустин Яков
оглавление
- 01 Введение
- 02 Классы, свойства, методы и псевдоконстанты
- 03 Лабораторная работа №1.1 Создание класса и его экземпляров
- 04 Магические методы
- 05 Лабораторная работа №1.2 Использование конструктора, деструктора и клона
- 06 Лабораторная работа №1.2/1 Вызов конструктора с именем класса
- 07 Наследование
- 08 Перегрузка методов
- 09 Лабораторная работа №1.3 Реализация наследования классов
- 10 Встроенные классы
- 11 Инкапсуляция
- 12 Полиморфизм
- 13 Абстрактные класс и метод
- 14 Интерфейс
- 15 Лабораторная работа №1.4 Использование абстрактных классов и интерфейсов
- 16 Константы и статические члены класса
- 17 Позднее статическое связывание
- 18 Автоматическая загрузка классов
- 19 Лабораторная работа №1.5-1.6 Использование статических членов и автозагрузки классов
- 20 Модификаторы доступа
- 21 Магические методы __set() и __get()
- 22 Магические методы __call() и __callStatic()
- 23 Магический метод __toString()
- 24 Магический метод __invoke()
- 25 Сериализация
- 26 Финальные классы и методы
- 27 Трейты
- 28 Уточнение типа и полезные функции
- 29 Анонимные классы
01Введение
К недостаткам процедурной парадигмы относят отсутствие целостности и неявность взаимосвязей, необходимость тщательного комментирования кода, трудности в масштабируемости.
PHP поддерживает и процедурное и объектно-ориентированное программирование. Кроме того, в php существует много встроенных решений, выполненных в объектно-ориентированном стиле, и, что еще важнее, в последнее время наблюдается тенденция к отказу от процедурной парадигмы в пользу объектно-ориентированной.
ООП - парадигма программирования, в основу которой заложено понятие объекта, отождествляемого с объектами окружающего нас мира.
Основные концепции ООП
- Система состоит из объектов
- Объекты некоторым образом взаимодействуют между собой
- Каждый объект характеризуется своим состоянием и поведением
Основные принципы ООП
- Инкапсуляция - принцип, согласно которому любой класс, а в более широком смысле любая часть системы, должны рассматриваться как чёрный ящик - пользователь должен видеть только интерфейс (список декларируемых свойств и методов) и не вникать во внутреннюю реализацию. Позволяет (теоретически) минимизировать число связей между подсистемами и упростить независимую реализацию и модификацию классов и подсистем.
- Наследование - возможность создавать класс на основе существующего с сохранением всех свойств и методов класса-предка (базового класса, суперкласса) с добавлением при необходимости собственных свойсв и методов. Наследование отображает иерархичность реального мира.
- Полиморфизм Классы-потомки могут изменять реализацию методов класса-предка, сохраняя его сигнатуру, оставляя тем самым неизменным интерфейс предка. Полиморфизм позволяет обрабатывать объекты классов-потомков как однотипные объекты, не смотря на то, что реализация методов у них может различаться.
02Классы, свойства, методы и псевдоконстанты
Базовым понятием ООП является класс.
В ООП рекомендуется использовать camelCase нотацию. Имена классов в PHP принято дополнительно начинать с заглавной буквы: NameClass.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Pet_57{ // объявление класса Pet_57
public $name; // объявление свойства будущих объектов
public $type = "type"; // объявление свойства будущих объектов со значением по умолчанию
function eol(){ // описание метода eol класса Pet_57
echo "<br>\n";
}
function say($word){ // описание метода say класса Pet_57
echo "{$this->name} sayd \"$word\""; // доступ к свойствам объекта
$this->eol(); // доступ к методам объекта
echo "использование псевдоконстанты __FUNCTION__: '".__FUNCTION__."';";
$this->eol(); // доступ к методам объекта
echo "использование псевдоконстанты __CLASS__: '".__CLASS__."';";
$this->eol(); // доступ к методам объекта
echo "использование псевдоконстанты __METHOD__: '".__METHOD__."';";
$this->eol(); // доступ к методам объекта
}
}
$cat = new Pet_57(); // создание экземпляров класса Pet_57
$dog = new Pet_57(); // создание экземпляров класса Pet_57
$cat->type = "cat"; // присвоение свойств экземплярам класса
$cat->name = "Cat"; // присвоение свойств экземплярам класса
$cat->say("I’m a cat"); // вызов метода экземпляра класса
unset($cat); // принудительный вызов деструктора с помощью удаления переменной
$dog->type = "dog"; // присвоение свойств экземплярам класса
$dog->name = "Dog"; // присвоение свойств экземплярам класса
$dog->say("I’m a dog"); // вызов метода экземпляра класса
unset($dog); // принудительный вызов деструктора с помощью удаления переменной
использование псевдоконстанты __FUNCTION__: 'say';
использование псевдоконстанты __CLASS__: 'Pet_57';
использование псевдоконстанты __METHOD__: 'Pet_57::say';
Dog sayd "I’m a dog"
использование псевдоконстанты __FUNCTION__: 'say';
использование псевдоконстанты __CLASS__: 'Pet_57';
использование псевдоконстанты __METHOD__: 'Pet_57::say';
При создании экземпляров классов (объектов) указание круглых скобок необязательно но желательно.
При объявлении свойств класса необходимо указывать модификаторы доступа (public, privat и пр. см ниже).
Доступ к свойствам объекта осуществляется с помощью конструкции '$obj1->type'.
Обращение к методам объекта осуществляется так-же с помощью конструкции '$obj1->func()'.
Поведение объекта описывается в методах класса. Методы также имеют модификаторы доступа, но по умолчанию они (для методов) равны значению 'public' и их допускается не указывать.
Внутри метода доступны следующие псевдоконстанты __FUNCTION__, __CLASS__ и __METHOD__.
03Лабораторная работа №1.1 Создание класса и его экземпляров
- Создайте класс User_dd;
- В текстовом редакторе откройте файл oop\users.php;
- В классе создайте свойства name, login и password;
- В классе создайте и опишите метод showInfo(), который выводит информацию о пользователе в произвольной форме;
- Создайте три объекта, экземпляра класса 'User_dd': $user1, $user2 и $user3;
- Задайте произвольные значения свойств name, login и password для каждого из объектов;
- Вызовите метод showInfo() для каждого объекта;
- Сохраните файл oop\users.php.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class User_dd{
public $n;
public $l;
public $p;
function showInfo(){
echo "name: '{$this->n}'; login: '{$this->l}'; password: '{$this->p}';<br>\n";
}
}
$user01 = new User_dd;
$user01 -> n = "Ivann";
$user01 -> l = "ivann";
$user01 -> p = "7c2ed4b2b9" ;
$user02 = new User_dd;
$user02 -> n = "Petro";
$user02 -> l = "petro";
$user02 -> p = "81b4fa24b7";
$user03 = new User_dd;
$user03 -> n = "Fedor";
$user03 -> l = "fedor";
$user03 -> p = "8ac0182ec9";
$content.=$user01 -> showInfo();
$content.=$user02 -> showInfo();
$content.=$user03 -> showInfo();
name: 'Petro'; login: 'petro'; password: '81b4fa24b7';
name: 'Fedor'; login: 'fedor'; password: '8ac0182ec9';
04Магические методы
Конструктор - магический метод, автоматически вызываемый при создании объекта. Для описания контруктора применяется служебное слово '__construct'. Удобен для инициализации, когда для каждого объекта необходимо выполнить ряд действий. В параметрах конструктора можно указать свойства объекта.
Деструктор - магический метод, автоматически вызываемый перед удалением объекта. Антипод конструктора. Для вызова метода при удалении объекта применяется '__destruct'. Круглые скобки в деструкторе указываются только из-за особенностей стнтаксиса php. В деструктор нельзя передать параметр. Кроме того, последовательность удаления объектов четко не определена, вследствии чего из деструктора НЕЛЬЗЯ обращаться к другим объектам. Вызывается в двух случаях: при завершении выполнения кода (не совсем) и при удалении объекта.
Клон. В PHP версии 5 и выше знак '=' при создании объекта на самом деле является присваиванием по ссылке. Копирование же объекта осуществляется при помощи служебного слова 'clone'. В клон нельзя передать параметр. При клонировании конструктор не вызывается!
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Pet_db{ // объявление класса Pet_db
public $name; // объявление свойства будущих объектов
public $type = "type"; // объявление свойства будущих объектов со значением по умолчанию
function __construct($t,$n,$w){ // Конструктор
$this->name=$n;
$this->type=$t;
$this->word=$w;
echo "Создан объект класса '".__CLASS__."'. type: {$this->type}; name: {$this->n}<br>\n";
echo "Object name {$this->n} sayd: \"{$this->word}\"<br>\n";
}
function __clone(){ // Клон
echo "Клонирован объект класса '".__CLASS__."'. type: {$this->type}; name: {$this->n}<br>\n";
echo "Object name {$this->n} sayd: \"{$this->word}\"<br>\n";
}
function __destruct(){ // Деструктор
echo "Object name {$this->n} sayd \"{$this->word}, and I’ll be back\"<br>\n";
echo "Удалён объект класса '".__CLASS__."'. type: {$this->type}; name: {$this->n}<br>\n";
}
}
$cat = new Pet_db("cat","Cat","I’m a cat"); // создание экземпляров класса Pet_db
$dog = new Pet_db("dog","Dog","I’m a dog"); // создание экземпляров класса Pet_db
$dog2 = clone $dog; // клонирование экземпляра класса Pet_db
unset($cat); // принудительный вызов деструктора с помощью удаления переменной
unset($dog); // принудительный вызов деструктора с помощью удаления переменной
unset($dog2); // принудительный вызов деструктора с помощью удаления переменной
Object name sayd: "I’m a cat"
Создан объект класса 'Pet_db'. type: dog; name:
Object name sayd: "I’m a dog"
Клонирован объект класса 'Pet_db'. type: dog; name:
Object name sayd: "I’m a dog"
Object name sayd "I’m a cat, and I’ll be back"
Удалён объект класса 'Pet_db'. type: cat; name:
Object name sayd "I’m a dog, and I’ll be back"
Удалён объект класса 'Pet_db'. type: dog; name:
Object name sayd "I’m a dog, and I’ll be back"
Удалён объект класса 'Pet_db'. type: dog; name:
05Лабораторная работа №1.2 Использование конструктора, деструктора и клона
- В текстовом редакторе откройте файл oop\users.php;
- В классе User_d2 создайте и опишите конструктор, который принимает в качестве аргументов имя, логин и пароль пользователя;
- Конструктор должен инициализировать свойства name, login и password;
- Измените код, который инициализирует объекты, передавая нужные параметры в конструктор;
- Удалите те строки кода, в которых задаются значения свойств объектов;
- В классе User_d2 создайте и опишите деструктор;
- Деструктор должен выводить строку Пользователь [логин_пользователя] удален;
- Подставьте вместо подстроки [логин_пользователя] значение свойства login;
- Сохраните файл oop\users.php.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class User_d2{
public $n;
public $l;
public $p;
function __construct($n,$l,$p){ // Конструктор
$this->n=$n;
$this->l=$l;
$this->p=$p;
echo "Создан объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
function __clone(){ // Клон
echo "Клонирован объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
function __destruct(){ // Деструктор
echo "Удалён объект класса '".__CLASS__."'. name: {$this->n};<br>\n";
}
function showInfo(){
echo "Объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
}
$user01 = new User_d2("Ivann","ivann",bin2hex(random_bytes(5)));
$user02 = new User_d2("Petro","petro",bin2hex(random_bytes(5)));
$user03 = new User_d2("Fedor","fedor",bin2hex(random_bytes(5)));
$user04 = clone $user01; // клонирование экземпляра класса User_d2
unset($user01,$user02,$user03,$user04); // принудительный вызов деструктора с помощью удаления объектов
Создан объект класса 'User_d2'. name: 'Petro'; login: 'petro'; password: '466a98efe8';
Создан объект класса 'User_d2'. name: 'Fedor'; login: 'fedor'; password: 'f8f4fca680';
Клонирован объект класса 'User_d2'. name: 'Ivann'; login: 'ivann'; password: '8ec4c4354f';
Удалён объект класса 'User_d2'. name: Ivann;
Удалён объект класса 'User_d2'. name: Petro;
Удалён объект класса 'User_d2'. name: Fedor;
Удалён объект класса 'User_d2'. name: Ivann;
06Лабораторная работа №1.2/1 Вызов конструктора с именем класса
- Вызвать конструктор по имени класса;
- Создать, клонировать и присвоить по ссылке объекты;
- Изменить свойства клонированного и ссыльного объектов;
- Обратить внимание на количество удаленных объектов.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class User_69{
public $n;
public $l;
public $p;
function User_69($n,$l,$p){ // Конструктор с именем класса (устаревшее!)
$this->n=$n;
$this->l=$l;
$this->p=$p;
echo "Создан объект класса '".__CLASS__."'. name: '{$this->n}'; login: '{$this->l}'; password: '{$this->p}';<br>\n";
}
function showInfo(){
echo "Объект класса '".__CLASS__."'. name: '{$this->n}'; login: '{$this->l}'; password: '{$this->p}';<br>\n";
}
function __clone(){
echo "Клонирован объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
function __destruct(){
echo "Удалён объект класса '".__CLASS__."'. name: {$this->n};<br>\n";
}
}
$user01 = new User_69("Ivann","ivann",bin2hex(random_bytes(5)));
$user02 = clone $user01; // клонирование объекта
$user03 = $user01; // присваивание объекта по ссылке
echo "<br>\n";
$user02->n = "Sidor";
$user02->l = "sidor";
$user02->p = bin2hex(random_bytes(5));
$user03->n = "Yakov";
$user03->l = "yakov";
$user03->p = bin2hex(random_bytes(5));
$user01->showInfo();
$user02->showInfo();
$user03->showInfo();
echo "<br>\n";
unset($user01,$user02,$user03);
Клонирован объект класса 'User_69'. name: 'Ivann'; login: 'ivann'; password: '1fcffce26c';
Объект класса 'User_69'. name: 'Yakov'; login: 'yakov'; password: 'fba217c3a4';
Объект класса 'User_69'. name: 'Sidor'; login: 'sidor'; password: '581bcd440a';
Объект класса 'User_69'. name: 'Yakov'; login: 'yakov'; password: 'fba217c3a4';
Удалён объект класса 'User_69'. name: Sidor;
Удалён объект класса 'User_69'. name: Yakov;
07Наследование
Наследование – это свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым или родительским. Новый класс – потомком, наследником или производным классом.
Опишем класс SimpleHouse с свойствами: модель, этажность, площадь этажа, цвет. Создадим объект этого класса.02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class SimpleHouse_05{
public $model="none";
public $square=0;
public $floor=0;
public $color="none";
function __construct($model,$square,$floor){
$this->model=$model;
$this->square=$square;
$this->floor=$floor;
echo "Создан объект класса '".__CLASS__."'.
model: '{$this->model}';
<br>\n";
}
function __clone(){
echo "Клонирован объект класса '".__CLASS__."'.
model: '{$this->model}';
<br>\n";
}
function __destruct(){
echo "Удалён объект класса '".__CLASS__."'.
model: '{$this->model}';
square: '{$this->square}';
floor: '{$this->floor}';
color: '{$this->color}';
<br>\n";
}
function startProject(){
echo "Start. Объект класса '".__CLASS__."'.
model: '{$this->model}';
<br>\n";
}
function stopProject(){
echo "Stop. Объект класса '".__CLASS__."'.
model: '{$this->model}';
<br>\n";
}
function build(){
echo "Build. Объект класса '".__CLASS__."'.
model: '{$this->model}';
square: '{$this->square}';
floor: '{$this->floor}';
color: '{$this->color}';
<br>\n";
}
function paint(){
echo "Paint. Объект класса '".__CLASS__."'.
color: '{$this->color}';
<br>\n";
}
}
Для создания подобного, но видоизменённого объекта (имеющего, к примеру, дополнительные свойства в виде камина и патио) создадим класс-наследник SuperHouse.
Наследование происходит с помощью конструкции extends с указанием класса, свойства и методы которого необходимо унаследовать.
Экземпляр класса-наследника унаследует свойства и методы суперкласса.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class SuperHouse_05 extends SimpleHouse_05{ // создание класса-наследника
public $fireplace = true;
public $patio = true;
function fire(){
if($this->fireplace){
echo "Fueled fireplace<br>\n";
}else{echo "not fireplace<br>\n";}
}
}
// Создание простого дома
$simple = new SimpleHouse_05("A-200-120",120,2);
$simple->startProject();
$simple->build();
$simple->color = "red";
$simple->paint();
$simple->stopProject();
echo "<br>\n";
// создание экземпляра класса-наследника
$super = new SuperHouse_05("S-300-320",320,3);
$super->startProject();
$super->build();
$super->color = "green";
$super->paint();
$super->stopProject();
echo "<br>\n";
$super->fire();
echo "<br>\n";
unset($simple,$super);
Start. Объект класса 'SimpleHouse_05'. model: 'A-200-120';
Build. Объект класса 'SimpleHouse_05'. model: 'A-200-120'; square: '120'; floor: '2'; color: 'none';
Paint. Объект класса 'SimpleHouse_05'. color: 'red';
Stop. Объект класса 'SimpleHouse_05'. model: 'A-200-120';
Создан объект класса 'SimpleHouse_05'. model: 'S-300-320';
Start. Объект класса 'SimpleHouse_05'. model: 'S-300-320';
Build. Объект класса 'SimpleHouse_05'. model: 'S-300-320'; square: '320'; floor: '3'; color: 'none';
Paint. Объект класса 'SimpleHouse_05'. color: 'green';
Stop. Объект класса 'SimpleHouse_05'. model: 'S-300-320';
Fueled fireplace
Удалён объект класса 'SimpleHouse_05'. model: 'A-200-120'; square: '120'; floor: '2'; color: 'red';
Удалён объект класса 'SimpleHouse_05'. model: 'S-300-320'; square: '320'; floor: '3'; color: 'green';
Наследование можно осуществлять только от одного класса. Множественное наследование не поддерживается.
08Перегрузка методов
Для изменения метода, описанного в суперклассе, существует операция перегрузки метода. Опишем класс Fabric, наследующий класс SimpleHouse. Перегрузим в нём метод build.
Для выполнения перегруженного родительского метода в методе наследника применяют ключевое слово 'parent' с указанием через двойное двоеточие имени родительского метода. Соответственно обращение из метода в классе-наследнике к неперегруженному родительскому методу осуществляется как к своему - через $this ($this->nameMethod).
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Fabric_05 extends SimpleHouse_05{
function build(){ // перегрузка родительского метода
echo "Build fabric. Объект класса '".__CLASS__."'.
model: '{$this->model}';
square: '{$this->square}';
floor: '{$this->floor}';
color: '{$this->color}';
<br>\n";
}
function paint(){ // перегрузка с выполнением родительского метода
echo "Fabric ";
parent::paint();
}
}
// создание экземпляра класса-наследника
$fabric = new Fabric_05("F-100-560",560,1);
$fabric->startProject();
$fabric->build();
$fabric->color = "purple";
$fabric->paint();
$fabric->stopProject();
echo "<br>\n";
unset($fabric);
Start. Объект класса 'SimpleHouse_05'. model: 'F-100-560';
Build fabric. Объект класса 'Fabric_05'. model: 'F-100-560'; square: '560'; floor: '1'; color: 'none';
Fabric Paint. Объект класса 'SimpleHouse_05'. color: 'purple';
Stop. Объект класса 'SimpleHouse_05'. model: 'F-100-560';
Удалён объект класса 'SimpleHouse_05'. model: 'F-100-560'; square: '560'; floor: '1'; color: 'purple';
09Лабораторная работа №1.3 Реализация наследования классов
- В текстовом редакторе откройте файл oop\users.php;
- Создайте и опишите класс SuperUser, наследованный от класса User;
- В классе SuperUser создайте свойство role;
- Перегрузите конструктор супер-класса так, чтобы он принимал четвёртым параметром значение для свойства role;
- Вызовите из конструктора родительский конструктор и передайте в него первые три параметра;
- Перегрузите метод супер-класса showInfo() так, чтобы выводилось и значение свойства role;
- Создайте объект $user, экземпляр класса SuperUser;
- Вызовите метод showInfo() объекта $user;
- Сохраните файл oop\users.php.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class User_fe{
public $n;
public $l;
public $p;
function __construct($n,$l,$p){ // Конструктор
$this->n=$n;
$this->l=$l;
$this->p=$p;
echo "Создан объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
function __clone(){ // Клон
echo "Клонирован объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
function __destruct(){ // Деструктор
echo "Удалён объект класса '".__CLASS__."'. name: {$this->n};<br>\n";
}
function showInfo(){
echo "Объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
}
class SuperUser_fe extends User_fe{
public $r;
function __construct($n,$l,$p,$r){
parent::__construct($n,$l,$p);
$this->r = $r;
echo "Создан объект класса '".__CLASS__."'.
role: '{$this->r}';
<br>\n";
}
function showInfo(){
echo "Объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
role: '{$this->r}';
<br>\n";
}
}
$user01 = new User_fe("Ivann","ivann",bin2hex(random_bytes(5)));
$user02 = new User_fe("Petro","petro",bin2hex(random_bytes(5)));
$user03 = new User_fe("Fedor","fedor",bin2hex(random_bytes(5)));
echo "<br>\n";
$user04 = new SuperUser_fe("Sidor","sidor",bin2hex(random_bytes(5)),"main");
echo "<br>\n";
$user04->showInfo();
echo "<br>\n";
unset($user01,$user02,$user03,$user04);
Создан объект класса 'User_fe'. name: 'Petro'; login: 'petro'; password: '013738ecc1';
Создан объект класса 'User_fe'. name: 'Fedor'; login: 'fedor'; password: 'c86e03a621';
Создан объект класса 'User_fe'. name: 'Sidor'; login: 'sidor'; password: 'e6c4fabfdc';
Создан объект класса 'SuperUser_fe'. role: 'main';
Объект класса 'SuperUser_fe'. name: 'Sidor'; login: 'sidor'; password: 'e6c4fabfdc'; role: 'main';
Удалён объект класса 'User_fe'. name: Ivann;
Удалён объект класса 'User_fe'. name: Petro;
Удалён объект класса 'User_fe'. name: Fedor;
Удалён объект класса 'User_fe'. name: Sidor;
10Встроенные классы
Примером встроенного класса в php является класс Exception модели исключений.
Исключения позволяют выявить исключительные ситуации (предусмотренные ошибки), известить о них и продолжить выполнение кода.
Ту часть кода, в которой могут произойти исключения, необходимо заключить в блок 'try{}'. С помощью 'throw' мы возбуждаем исключение и передаём строку '$var is false!' в конструктор класса 'Exception'. Внутри круглых скобок оператора 'catch(Exception $e){}' необходимо указать переменную, которой будет присвоен объект класса 'Exception'.
Каждый блок try должен иметь как минимум один соответствующий ему блок 'catch' или 'finally'.
При генерации исключения код, следующий после описываемого выражения до первого блока 'catch', перехватывающего исключение данного класса, выполнен не будет. Если исключение не будет перехвачено (не найден блок 'catch'), PHP выдаст фатальную ошибку: "Uncaught Exception ..." (Неперехваченное исключение).
В PHP 5.5 и более поздних версиях блок 'finally' также можно использовать после или вместо блока catch. Код в блоке 'finally' всегда будет выполняться после кода в блоках 'try' и 'catch', независимо от того, было ли выброшено исключение, перед тем как продолжится нормальное выполнение кода.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function test_0b($var=false){
try{
echo "Start<br>\n";
if(!$var){
// возбуждение исключения с помощью встроенного класса 'Exception'
throw new Exception("\$var is false!");
}
echo "Continue<br>\n";
}catch(Exception $e){ // перехват исключения с помощью оператора 'catch'
echo "exception: ".$e->getMessage()."<br>\n";
// echo "in file: ".$e->getFile()."<br>\n";
echo "in line: ".$e->getLine()."<br>\n";
}finally{
echo "finally<br>\n";
}
echo "The end<br>\n<br>\n";
}
//var_dump(test_0b(1),test_0b());
test_0b(1);
test_0b();
Continue
finally
The end
Start
exception: $var is false!
in line: 7
finally
The end
11Инкапсуляция
Сокрытие внутренних процессов в ООП носит название инкапсуляции.
Инкапсуляция – это свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе и скрыть детали реализации от пользователя. Инкапсуляция неразрывно связана с понятием интерфейса класса. По сути, всё то, что не входит в интерфейс, инкапсулируется в классе.
12Полиморфизм
Для объектов разных классов возможно использовать один и тот-же код. При этом будут вызываться одноименные методы вызываемых классов.
Полиморфизм – это возможность создавать разные реализации, используя одинаковый интерфейс (в данном случае интерфейс является способом вызова объекта в коде).
13Абстрактные класс и метод
Абстрактный класс - класс, имеющий хотя бы один абстрактный метод.
Для объявления абстрактного класса необходимо указать 'abstract' перед ключевым словом 'class'.
'Что нам это даёт? Ничего! Базовый класс это класс, от которого нельзя создавать объекты. Его можно только наследовать.' - Борисов И.О.
Абстрактные классы могут содержать абстрактные методы, а классы, имеющие абстрактные методы должены быть абстрактными.
Абстрактные методы имеют декларацию (описательный смысл) и не имеют реализации.
Абстрактные методы также предваряет ключевое слово 'abstract'. При наследовании абстрактного класса необходимо описать содержащиеся в нём абстрактные методы.Кроме того, область видимости этих методов должна совпадать или быть менее строгой (если абстрактный метод объявлен как protected, то реализация этого метода должна быть protected или public, но не private). Более того, объявления методов должны совпадать, то есть контроль типов (type hint) и количество обязательных аргументов должно быть одинаковым. К примеру, если в дочернем классе указан необязательный параметр, которого нет в объявлении абстрактного класса, то в данном случае конфликта объявлений методов не будет.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
abstract class AbstractHouse_13{ // Абстрактный класс
public $model="none";
public $square=0;
public $floor=0;
function __construct($model,$square,$floor){
$this->model=$model;
$this->square=$square;
$this->floor=$floor;
echo "Создан объект класса '".__CLASS__."'. model: '{$this->model}';<br>\n";
}
abstract function build();// Абстрактный метод
}
class SimpleHouse_13 extends AbstractHouse_13{
function build(){ // описание абстрактного родительского метода
echo "Build Simple House. Объект класса '".__CLASS__."'.
model: '{$this->model}';
square: '{$this->square}';
floor: '{$this->floor}';
<br>\n";
}
}
$simpleHouse = new SimpleHouse_13("SH-01","70","1");
$simpleHouse -> build();
unset($simpleHouse);
Build Simple House. Объект класса 'SimpleHouse_13'. model: 'SH-01'; square: '70'; floor: '1';
14Интерфейс
Интерфейс - Абстрактный класс, содержащий только абстрактные методы (справедливо для php!).
Так как неабстрактных методов в интерфейсе быть не может, ключевое слово 'abstract' перед объявлением метода не указывается. Интерфейсы не наследуются, а реализуются с помощью ключевого слова 'implements'.
Преимуществом интерфейсов перед абстрактными классами является их неограниченное количество не содержащих пересекающихся методов с различными объявлениями (опять-таки контроль типов и количество обязательных аргументов) при реализации. При реализации в классе нескольких интерфейсов они указываются через запятую.
Один интерфейс - множество реализаций. Программируйте на уровне интерфейсов, реализуйте на уровне классов.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
interface Paintable_a8{ // Интерфейс 1
function paint($color); // Абстрактный метод
}
interface Cleaneable_a8{ // Интерфейс 2
function clean(); // Абстрактный метод
}
abstract class AbstractHouse_a8{ // Абстрактный класс
public $model="none";
public $square=0;
public $floor=0;
function __construct($model,$square,$floor){
$this->model=$model;
$this->square=$square;
$this->floor=$floor;
echo "Создан объект класса '".__CLASS__."'. model: '{$this->model}';<br>\n";
}
abstract function build();// Абстрактный метод
}
class SimpleHouse_a8 extends AbstractHouse_a8 implements Cleaneable_a8, Paintable_a8{
public $color="none";
function build(){ // описание абстрактного родительского метода
echo "Build Simple House. Объект класса '".__CLASS__."'.
model: '{$this->model}';
square: '{$this->square}';
floor: '{$this->floor}';
<br>\n";
}
function paint($color){ // описание абстрактного родительского метода
$this->color = $color;
echo "Paint Simple House. Объект покрашен в {$this->color} цвет.<br>\n";
}
function clean(){ // описание абстрактного родительского метода
echo "Paint Simple House. Объект убран.<br>\n";
}
}
$simpleHouse = new SimpleHouse_a8("SH-01","70","1");
$simpleHouse -> build();
// проверка наличия класса в цепочке предков.
if($simpleHouse instanceOf Paintable_a8){$simpleHouse -> paint("зелёный");}
if($simpleHouse instanceOf Cleaneable_a8){$simpleHouse -> clean();}
unset($simpleHouse);
Build Simple House. Объект класса 'SimpleHouse_a8'. model: 'SH-01'; square: '70'; floor: '1';
Paint Simple House. Объект покрашен в зелёный цвет.
Paint Simple House. Объект убран.
Интерфейсы реализуются при необходимости в методах, описанных в них.
instanceOf - оператор, позволяющий проверить наличие класса в цепочке предков.
15Лабораторная работа №1.4 Использование абстрактных классов и интерфейсов
- В текстовом редакторе откройте файл oop\users.php;
- Создайте и опишите абстрактный класс UserAbstract;
- В классе UserAbstract объявите абстрактный метод showInfo();
- Обновите класс User, унаследовав его от абстрактного класса UserAbstract;
- Если требуется, внесите в класс User необходимые изменения;
- Сохраните файл oop\users.php.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
abstract class UserAbstract_e8{
abstract function showInfo();
}
class User_e8 extends UserAbstract_e8{
public $n;
public $l;
public $p;
function __construct($n,$l,$p){ // Конструктор
$this->n=$n;
$this->l=$l;
$this->p=$p;
echo "Создан объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
function __clone(){ // Клон
echo "Клонирован объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
function __destruct(){ // Деструктор
echo "Удалён объект класса '".__CLASS__."'. name: {$this->n};<br>\n";
}
function showInfo(){
echo "Объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
}
class SuperUser_e8 extends User_e8{
public $r;
function __construct($n,$l,$p,$r){
parent::__construct($n,$l,$p);
$this->r = $r;
echo "Создан объект класса '".__CLASS__."'.
role: '{$this->r}';
<br>\n";
}
function showInfo(){
echo "Объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
role: '{$this->r}';
<br>\n";
}
}
$user01 = new User_e8("Ivann","ivann",bin2hex(random_bytes(5)));
$user02 = new User_e8("Petro","petro",bin2hex(random_bytes(5)));
$user03 = new User_e8("Fedor","fedor",bin2hex(random_bytes(5)));
echo "<br>\n";
$user04 = new SuperUser_e8("Sidor","sidor",bin2hex(random_bytes(5)),"main");
echo "<br>\n";
$user04->showInfo();
echo "<br>\n";
unset($user01,$user02,$user03,$user04);
Создан объект класса 'User_e8'. name: 'Petro'; login: 'petro'; password: 'fca8165478';
Создан объект класса 'User_e8'. name: 'Fedor'; login: 'fedor'; password: '1026f63eba';
Создан объект класса 'User_e8'. name: 'Sidor'; login: 'sidor'; password: '8f5eca8e96';
Создан объект класса 'SuperUser_e8'. role: 'main';
Объект класса 'SuperUser_e8'. name: 'Sidor'; login: 'sidor'; password: '8f5eca8e96'; role: 'main';
Удалён объект класса 'User_e8'. name: Ivann;
Удалён объект класса 'User_e8'. name: Petro;
Удалён объект класса 'User_e8'. name: Fedor;
Удалён объект класса 'User_e8'. name: Sidor;
16Константы и статические члены класса
Константа класса - константа, принадлежащая не экземпляру класса, а классу. К константе класса можно обратиться без создания экземпляра класса.
Статической переменной (в процедурном программировании) называют локальную переменную, сохраняющую своё значение после выхода из функции. Статические свойства присущи и классам. Статическое свойство сохраняет своё значение после выхода из функции. Применяется в основном для реализации счётчиков. Статические свойства не могут быть доступны через объект с помощью оператора '->'.
Для обращения к константе или к статическому свойству класса используются либо ключевое слово 'self' (при обращении из метода класса), либо имя класса (при обращении без создания экземпляра класса) и через двойное двоеточие указывают имя константы 'self::NAME_CONST' или статического свойства 'self::$nameProperties'.
Помимо этого существуют статические методы класса. Вызов статического метода осуществляется через имя класса, двойное двоеточие, имя метода: 'NameClass::nameMethod();'. Так как статические методы вызываются без создания экземпляра класса, то псевдопеременная $this недоступна внутри метода, объявленного как статический.
Объявление свойств и методов класса статическими позволяет обращаться к ним без создания экземпляра класса. Свойство класса, объявленное как статическое, не может быть доступно посредством экземпляра класса (но статический метод может быть вызван).
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class ConstructionCompany_b6{
const NAME = "ИХиО";
public static $view = 0;
static function welcome(){
// никаких "$this" в статическом методе класса!
echo "Спасибо за обращение в нашу компанию.<br>\n";
}
function printName(){
// обращение к константе из метода класса
echo "обращение к константе из метода класса:
ООО '".self::NAME."';<br>\n";
}
function countView(){
++self::$view;
echo "обращение к статическому свойству из метода класса:
просмотров: ".self::$view.";<br>\n";
}
}
// обращение к константе без создания экземпляра класса
echo "обращение к константе без создания экземпляра класса:
ООО '".ConstructionCompany_b6::NAME."';<br>\n";
// обращение к статическому свойству без создания экземпляра класса
echo "обращение к статическому свойству без создания экземпляра класса:
просмотров: ".ConstructionCompany_b6::$view.";<br>\n";
// обращение к статическому методу класса
ConstructionCompany_b6::welcome();
$company = new ConstructionCompany_b6();
$company->printName();
$company->countView();
$company->countView();
$company->countView();
unset($company);
обращение к статическому свойству без создания экземпляра класса: просмотров: 0;
Спасибо за обращение в нашу компанию.
обращение к константе из метода класса: ООО 'ИХиО';
обращение к статическому свойству из метода класса: просмотров: 1;
обращение к статическому свойству из метода класса: просмотров: 2;
обращение к статическому свойству из метода класса: просмотров: 3;
17Позднее статическое связывание
Начиная с версии PHP 5.3.0 появилась особенность, называемая позднее статическое связывание, которая может быть использована для того, чтобы получить ссылку на вызываемый класс в контексте статического наследования.
Само название 'позднее статическое связывание' отражает в себе внутреннюю реализацию этой особенности. 'Позднее связывание' отражает тот факт, что обращения через static:: не будут вычисляться по отношению к классу, в котором вызываемый метод определен, а будут вычисляться на основе информации в ходе исполнения. Также эта особенность была названа 'статическое связывание' потому, что она может быть использована (но не обязательно) в статических методах.
Статические ссылки на текущий класс, такие как self:: или __CLASS__, вычисляются используя класс, к которому эта функция принадлежит, как и в том месте, где она была определена.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
class A_19{
static function who(){
echo __CLASS__."<br>\n";
}
static function identity(){
self::who();
}
}
class B_19 extends A_19{
static function who(){
echo __CLASS__."<br>\n";
}
static function identity_(){
static::who(); // начиная с php 5.3
}
}
B_19::identity(); // должен вывести A_19
B_19::identity_(); // должен вывести B_19
B_19
18Автоматическая загрузка классов
Большинство разработчиков объектно-ориентированных приложений используют такое соглашение именования файлов, в котором каждый класс хранится в отдельно созданном для него файле. Одна из самых больших неприятностей - необходимость писать в начале каждого скрипта длинный список подгружаемых файлов (по одному для каждого класса).
В PHP 5 это делать необязательно. Функция 'spl_autoload_register()' позволяет зарегистрировать необходимое количество автозагрузчиков для автоматической загрузки классов и интерфейсов, если они в настоящее время не определены. Регистрируя автозагрузчики, PHP получает последний шанс для интерпретатора загрузить класс прежде, чем он закончит выполнение скрипта с ошибкой.
В то время как функция '__autoload()' также может быть использована для автоматической загрузки классов и интерфейсов, следует отдать предпочтение 'spl_autoload_register()', потому, что она предоставляет гораздо более гибкую альтернативу, позволяя регистрировать необходимое количество автозагрузчиков, например, для сторонних библиотек. По этой причине использование '__autoload()' объявлено устаревшим, начиная с PHP 7.2.0 и его использование крайне не рекомендовано.
Создаём директорию '_php31' и поместим в неё файлы с описанием классов (каждый класс хранится в отдельно созданном для него файле, названном по имени класса с промежуточным расширением '.class': '_php31/NameClass.class.php') и описываем функцию автоматической загрузки классов 'spl_autoload_register'.
2
3
4
5
6
7
class NewClass_53{
function __construct($n){
echo "hello, i am $n";
}
}
2
3
4
5
6
7
8
9
// Начиная с версии PHP 5.3.0 можно использовать анонимные функции
spl_autoload_register(function($class){
include "/full/path/to/_php31/$class.class.php";
});
$obj=new NewClass_53("Feodor");
unset($obj);
19Лабораторная работа №1.5-1.6 Использование статических членов и автозагрузки классов
- В текстовом редакторе откройте файл oop\users.php;
- Создайте в классах User и SuperUser статические свойства для подсчета количества созданных объектов;
- Присвойте этим свойствам начальное значение 0;
- В конструкторах классов инкрементируйте значения данных свойств;
- В нижней части кода, после создания экземпляров классов, выведите в браузер количество обычных и супер пользователей;
- В текстовом редакторе создайте новый файл;
- Перенесите описание абстрактного класса UserAbstract из файла oop\users.php в новый файл;
- Сохраните новый файл как oop\classes\UserAbstract.class.php;
- В текстовом редакторе создайте новый файл;
- Перенесите описание класса User из файла oop\users.php в новый файл;
- Сохраните новый файл как oop\classes\User.class.php;
- В текстовом редакторе создайте новый файл;
- Перенесите описание интерфейса ISuperUser из файла oop\users.php в новый файл;
- Сохраните новый файл как oop\classes\ISuperUser.class.php;
- В текстовом редакторе создайте новый файл;
- Перенесите описание класса SuperUser из файла oop\users.php в новый файл;
- Сохраните новый файл как oop\classes\SuperUser.class.php;
- Сохраните файл oop\users.php;
- В файле oop\users.php (основной код) создайте и опишите функцию __autoload(), которая производит автозагрузку нужного класса при создании его экземпляра;
- Сохраните файл oop\users.php.
2
3
4
abstract class UserAbstract_be{
abstract function showInfo();
}
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class User_be extends UserAbstract_be{
public $n;//$rel_fp
public $l;//$fp
public $p;
static public $countUser=0;
function __construct($n,$l,$p){ // Конструктор
$this->n=$n;
$this->l=$l;
$this->p=$p;
++self::$countUser;
echo "Создан объект №".self::$countUser." класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
function __clone(){ // Клон
echo "Клонирован объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
function __destruct(){ // Деструктор
echo "Удалён объект класса '".__CLASS__."'. name: {$this->n};<br>\n";
}
function showInfo(){
echo "Объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
<br>\n";
}
}
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class SuperUser_be extends User_be{
public $r;
static public $countSuperUser;
function __construct($n,$l,$p,$r){
parent::__construct($n,$l,$p);
$this->r = $r;
++self::$countSuperUser;
echo "Создан объект №".self::$countSuperUser." класса '".__CLASS__."'.
role: '{$this->r}';
<br>\n";
}
function showInfo(){
echo "Объект класса '".__CLASS__."'.
name: '{$this->n}';
login: '{$this->l}';
password: '{$this->p}';
role: '{$this->r}';
<br>\n";
}
}
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
spl_autoload_register(function($class){
include "/full/path/to/_php31/$class.class.php";
});
$user01 = new User_be("Ivann","ivann",bin2hex(random_bytes(5)));
$user02 = new User_be("Petro","petro",bin2hex(random_bytes(5)));
$user03 = new User_be("Fedor","fedor",bin2hex(random_bytes(5)));
echo "<br>\n";
$user04 = new SuperUser_be("Sidor","sidor",bin2hex(random_bytes(5)),"main");
echo "<br>\n";
$user04->showInfo();
echo "<br>\n";
echo "Всего создано ".User_be::$countUser." объектов класса User_be<br>\n";
echo "Всего создано ".SuperUser_be::$countSuperUser." объектов класса SuperUser_be<br>\n";
echo "<br>\n";
unset($user01,$user02,$user03,$user04);
20Модификаторы доступа
К свойствам и методам классов для повышения градуса абстрактности применимы три модификатора:
Модификатор public (общедоступный) позволяет получить доступ к свойствам и методам класса из любого места.
Модификатор protected (защищенный) позволяет получить доступ из родительского (в котором определен сам член класса), и наследуемых классов. Этот модификатор настолько бесполезен, что многие языки его даже не реализуют (с).
Модификатор private (закрытый) ограничивает область видимости так, что доступ к нему имеет только тот класс, в котором объявлен сам элемент.
Модификаторы protected и private являются чем-то вроде таблички 'для персонала'. Ничего секретного там нет и быть не должно, это просто предупреждение.
Для доступа на чтение описывается функция, возвращающая значение закрытого свойства.
21Магические методы __set() и __get()
В php допускается создание неописанных в классе свойств объектов из глобальной области видимости:
2
3
4
5
6
7
8
9
10
class MyClass_34{
}
$obj = new MyClass_34();
$obj->$illegalProp = "legalProp";
echo "{$obj->$illegalProp}<br>\n";
unset($company);
Для закрытия этого бага можно воспользоваться магическими методами __set() и __get(). Метод __set() будет выполнен при записи данных в недоступные свойства. Метод __get() будет выполнен при чтении данных из недоступных свойств. Данный финт ушами делается для контроля над необъявленными переменными. Теперь к ним можно обратиться через массив $arr.
Если при присвоении свойства экземпляру класса объявления этого свойства в классе не найдено (а - свойство не объявлено; б - ограничена область видимости свойства), то сначала проверяется, описан метод '__set()' (1) или не описан (2).
Если метод '__set()' не описан, то, если свойство не объявлено, (случай 2а) - свойство создается; если ограничена область видимости свойства (2б) - выдаётся сообщение об ошибке; иначе вызывается описанный метод '__set()' (1а, 1б), и там как масть ляжет.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class MyClass_8d{
//private $arr=[];
public $arr=[];
function __set($n,$v){
//echo $n, ";", $v;
$this->arr[$n]=$v;
}
function __get($n){
echo "ERROR! чтение данных из недоступного свойства! \$n='$n'<br>\n";
// return $this->arr[$n];
}
function __getArr(){
return $this->arr;
}
}
$obj = new MyClass_8d();
$obj->illegalProp = "42"; // добавление неописанного в классе свойства
echo "{$obj->illegalProp}<br>\n"; // вызов неописанного в классе свойства
echo "<pre>print_r(\$obj->__getArr()):<br>\n";
print_r($obj->__getArr());
echo "</pre><br>\n";
unset($obj);
print_r($obj->__getArr()):
Array ( [illegalProp] => 42 )
С помощью магического метода '__set' можно позволить классу принимать только те свойства, которые мы явно разрешим (запрет на динамическое создание свойств).
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class MyClass_cb{
private $x,$y;// объявим два приватных свойства
function __set($n,$v){// сюда попадут недоступные (необъявленные или private/protected) свойства
switch($n){
case "x": $this->x=$v; echo "variable '$".$n."' accepted.<br>\n"; break;
case "y": $this->y=$v; echo "variable '$".$n."' accepted.<br>\n"; break;
default: echo "ERROR ".__line__.": illegal variable '$".$n."'.<br>\n";
}
}
function __get($n){
echo "ERROR ".__line__.": чтение данных из недоступного свойства! \$n='$n'.<br>\n";
}
}
$obj = new MyClass_cb();
$obj->x = "42";
$obj->y = "42";
$obj->illegalProp = "42";
echo "{$obj->illegalProp}<br>\n";
unset($obj);
variable '$y' accepted.
ERROR 9: illegal variable '$illegalProp'.
ERROR 14: чтение данных из недоступного свойства! $n='illegalProp'.
Еще один вариант. При возникновении необходимости дополнительной обработки принятого свойства после написания кучи дочерних классов можно поступить как во многих других языках, где свойству по-умолчанию присваиваится 'privat'. Для обработки входящих данных каждому свойству прописывается геттер и сеттер.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
class User_ff{
private $name,$age;// объявим два приватных свойства
function setName($v){$this->name=strtoupper($v);}
function getName(){return $this->name;}
function setAge($v){$this->age=$v;}
function getAge(){return $this->age;}
}
$obj = new User_ff();
$obj->setName("Egor");
$obj->setAge("42");
echo $obj->getName("Egor")."<br>\n";
echo $obj->getAge("42")."<br>\n";
unset($obj);
42
Но в этом случае необходимо переписывать и код родительского класса, и точек присвоения/вызова измененяемого свойства. В php это сделать проще - для обработки принятого свойства изменяется только родительский класс, все остальное остается неизменным. По этому заранее описывать геттер и сеттер здесь нет необходимости - это можно сделать в любой момент.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class User_84{
// public $name,$age;
public $age;
private $name;// объявляем свойство приватным
function __set($n,$v){// добавляем обработку приватного свойства
switch($n){
case "name": $this->name=strtoupper($v); break;
default: echo "ERROR ".__line__.": illegal variable '$".$n."'.<br>\n";
}
}
function __get($n){
switch($n){
case "name": return $this->name; break;
default: echo "ERROR ".__line__.": illegal variable '$".$n."'.<br>\n";
}
}
}
$obj = new User_84();
$obj->name = "Egor";
$obj->age = "42";
echo $obj->name."<br>\n";
echo $obj->age."<br>\n";
unset($obj);
42
22Магические методы __call() и __callStatic()
Метод __call($name,$args) запускается при вызове недоступных методов в контексте Метод __callStatic($name,$args) запускается при вызове недоступных методов в статическом контексте.
Аргумент $name принимает имя вызываемого метода. Аргумент $arguments принимает нумерованный массив, содержащий параметры, переданные в вызываемый метод $name.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class User_50{
// public $name,$age;
public $age;
private $name;// объявляем свойство приватным
function __set($n,$v){// добавляем обработку приватного свойства
switch($n){
case "name": $this->name=strtoupper($v); break;
default: echo "ERROR ".__line__.": illegal variable '$".$n."'.<br>\n";
}
}
function __get($n){
switch($n){
case "name": return $this->name; break;
default: echo "ERROR ".__line__.": illegal variable '$".$n."'.<br>\n";
}
}
function __call($name,$args){
echo "Call undefined method '$name' with arguments: " . implode(", ", $args).".<br>\n";
}
function __callStatic($name,$args){
echo "Call undefined static method '$name' with arguments: " . implode(", ", $args).".<br>\n";
}
}
$obj = new User_50();
$obj->name();
User_50::age("42","Timofey");
unset($obj);
Call undefined static method 'age' with arguments: 42, Timofey.
23Магический метод __toString()
Метод __toString() позволяет кодеру решать, как должен реагировать объект при преобразовании его в строку. Например, что вывести при выполнении 'echo $obj;'. Этот метод должен возвращать строку, иначе произойдёт фатальная ошибка уровня E_RECOVERABLE_ERROR.
Начиная с PHP 5.2.0, преобразование объекта в строку при отсутствии метода __toString() вызывает ошибку E_RECOVERABLE_ERROR.
Внимание! Нельзя выбросить исключение из метода __toString(). Это приведет к фатальной ошибке.
02
03
04
05
06
07
08
09
10
11
12
13
class MyClass_da{
function __toString(){
return "Это объект, экземпляр класса '__CLASS__'";
}
}
$obj = new MyClass_da();
echo "$obj<br>\n";
unset($company);
24Магический метод __invoke()
Метод __invoke() позволяет кодеру решать, как должен реагировать объект при вызове его как функции.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
class MyClass_49{
function __invoke($num,$action){
switch($action){
case "+": return $num+$num;
case "*": return $num*$num;
default: throw new Exception("Неизвестное свойство!");
}
}
}
$obj = new MyClass_49();
echo $obj(5,"+")."<br>\n";
echo $obj(5,"*")."<br>\n";
//echo $obj(5,"-")."<br>\n";// Fatal error: Uncaught Exception: Неизвестное свойство!
unset($company);
25
25Сериализация
В результате сериализации объекта вы получите его строковое представление, которое содержит значение всех переменных, содержащихся в нем. Для того, чтобы восcтановить объект из строки в другом PHP-файле класс A должен быть определен заранее.
Применяется, например, для сохранения объектов между сессиями.
Если в приложении производится сериализация объектов для последующего использования, настоятельно рекомендуется подключать определение класса для этого объекта во всем приложении. Невыполнение этого требования может привести к тому, что объект будет десериализован без определения класса, что приведет к тому, что PHP будет использовать для этого объекта класс __PHP_Incomplete_Class_Name, который не имеет методов и сделает объект бесполезным.
Также можно подключиться к событиям сериализации и десериализации объекта с помощью методов __sleep() и __wakeup(). Метод __sleep() также позволяет сериализовать лишь некоторое подмножество свойств объекта.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// путь героя. сериализуем только то, что необходимо
Class User1_c7{
private $name;
private $login;
private $passw;
public $params=[];
function __construct($n,$l,$p){
$this->name = $n;
$this->login = $l;
$this->passw = $p;
$this->params = $this->getParamsUser(); // !
}
function getParamsUser(){
return [$this->name,$this->login,$this->passw];
}
function shuffleParamsUser(){
$this->params = [str_shuffle($this->name),str_shuffle($this->login),str_shuffle($this->passw)];
}
// метод должен вернуть массив свойств, которые необходимо сериализовать
function __sleep(){
//return ["name"=>$this->name, "login"=>$this->login, "passw"=>$this->passw];
return ["name", "login", "passw"];
}
// аналог конструктора
function __wakeup(){
$this->params = $this->getParamsUser(); // вызов метода, вызываемого в конструкторе
}
}
// путь негероя. методы __sleep() и __wakeup() не описаны
Class User2_c7{
private $name;
private $login;
private $passw;
public $params=[];
function __construct($n,$l,$p){
$this->name = $n;
$this->login = $l;
$this->passw = $p;
$this->params = $this->getParamsUser(); // !
}
function getParamsUser(){
return [$this->name,$this->login,$this->passw];
}
function shuffleParamsUser(){
$this->params = [str_shuffle($this->name),str_shuffle($this->login),str_shuffle($this->passw)];
}
}
$user01 = new User1_c7("Ivann","ivann",bin2hex(random_bytes(5)));
$user02 = new User2_c7("Petro","petro",bin2hex(random_bytes(5)));
$user01->shuffleParamsUser();
$user02->shuffleParamsUser();
$str01=serialize($user01);
$str02=serialize($user02);
unset($user01,$user02);
echo "<br>\n";
$user01=unserialize($str01);
$user02=unserialize($str02);
echo "'{$user01->params[0]}', '{$user01->params[1]}', '{$user01->params[2]}'<br>\n";
echo "'{$user02->params[0]}', '{$user02->params[1]}', '{$user02->params[2]}'<br>\n";
'Ivann', 'ivann', '21d37ebcc3'
'otrPe', 'oerpt', 'ed31fad98c'
26Финальные классы и методы
Класс, объявленный финальным нельзя наследовать. Применяется, например, при неполном описании класса и намерении кардинального изменения его в будущем.
Разместив перед объявлениями методов класса ключевое слово final можно предотвратить их переопределение в дочерних классах.
Свойства не могут быть объявлены окончательными.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Финальный класс
final class Breakfast_3b{
function eatFood($food){
echo "Съели $food";
}
}
class McBreakfast_3b extends Breakfast_3b{}
$obj = new McBreakfast_3b(); // Ошибка!
class Math_3b{
// Финальный метод
final function sum($num1, $num2){
echo 'Сумма: ' . $num1 + $num2;
}
}
class Algebra_3b extends Math_3b{
function sum($num1, $num2){
$result = $num1 + $num2;
echo "Сумма: $num1 и $num2 = $result";
}
}
$obj = new Algebra_3b(); // Ошибка!
27Трейты
Трейт - это механизм обеспечения повторного использования кода в языках с поддержкой только одиночного наследования, таких как PHP. Трейт предназначен для уменьшения некоторых ограничений одиночного наследования, позволяя разработчику повторно использовать наборы методов свободно, в нескольких независимых классах и реализованных с использованием разных архитектур построения классов. Семантика комбинации трейтов и классов определена таким образом, чтобы снизить уровень сложности, а также избежать типичных проблем, связанных с множественным наследованием и смешиванием (mixins).
Трейт очень похож на класс, но предназначен для группирования функционала хорошо структурированым и последовательным образом. Невозможно создать самостоятельный экземпляр трейта. Это дополнение к обычному наследованию и позволяет сделать горизонтальную композицию поведения, то есть применение членов класса без необходимости наследования.
Несколько трейтов могут быть вставлены в класс путем их перечисления в директиве use, разделяя запятыми.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
class Base_8b{
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld_8b{
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld_8b extends Base_8b{
use SayWorld_8b;
}
$o = new MyHelloWorld_8b();
$o->sayHello();
28Уточнение типа и полезные функции
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Базовое использование
class MyClass_a1{
function func(){
return __METHOD__;
}
static function staticFunc(){
return __METHOD__;
}
function __invoke(){
return __METHOD__;
}
}
$my = new MyClass_a1();
$std = new stdClass();
// Ожидается передача объекта, экземпляра класса MyClass_a1
function foo1(MyClass_a1 $obj){}
foo1($my); // Отработает успешно
// foo1($std); // Fatal error: Uncaught TypeError: Argument 1 passed to foo1() must be an instance of MyClass_18
$obj = new MyClass_a1();
// Ожидается то, что можно вызвать
function foo(callable $x){
if(func_num_args() == 2){
$m = func_get_arg(1);
return $x->$m();
}elseif(is_array($x)){
return $x[0]::$m[1]();
}else{
return $x();
}
}
echo foo($obj, "func"); // MyClass::func
echo foo(["MyClass_a1", "staticFunc"]); // MyClass::staticFunc
echo foo($obj); // MyClass_a1::__invoke
29Анонимные классы
В php 7 была добавлена поддержка анонимных классов с помощью new class. Имена анонимным классам присваиваются движком PHP, как показано в примере ниже. Это имя следует рассматривать как особенность реализации, на которую не следует полагаться. Их можно использовать тогда, когда нужен одноразовый класс и создавать полноценный класс, а потом его объект не имеет смысла:
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
interface Logger{
public function log(string $msg);
}
class Application{
private $logger;
public function getLogger(): Logger{
return $this->logger;
}
public function setLogger(Logger $logger){
$this->logger = $logger;
}
}
$app = new Application;
$app->setLogger(new class implements Logger{
public function log(string $msg){
echo $msg;
}
});
var_dump($app->getLogger());
Все объекты, созданные одним и тем же объявлением анонимного класса, являются экземплярами этого самого класса. Анонимные классы могут передавать аргументы в конструкторы, расширять другие классы, реализовывать интерфейсы и использовать трейты как обычный класс:
02
03
04
05
06
07
08
09
10
11
12
13
14
15
class SomeClass {}
interface SomeInterface {}
trait SomeTrait {}
var_dump(
new class(10) extends SomeClass implements SomeInterface{
private $num;
public function __construct($num){
$this->num = $num;
}
use SomeTrait;
}
);
Капустин Яков (2018.12.23 20:32)