yakoffka.ru
    грабли, костыли и велосипеды php, css, html, js и прочего

    PHP 2016. Уровень 3.1: ООП

    ошибки phpОбъектно-ориентированное программирование на PHP. конспект-памятка видеокурса 'Программирование на PHP 2016'. Лектор Борисов Игорь Олегович. Учебный Центр «Специалист» при МГТУ им. Н.Э.Баумана.

    конспектировал Капустин Яков

    оглавление

    К недостаткам процедурной парадигмы относят отсутствие целостности и неявность взаимосвязей, необходимость тщательного комментирования кода, трудности в масштабируемости.

    PHP поддерживает и процедурное и объектно-ориентированное программирование. Кроме того, в php существует много встроенных решений, выполненных в объектно-ориентированном стиле, и, что еще важнее, в последнее время наблюдается тенденция к отказу от процедурной парадигмы в пользу объектно-ориентированной.

    ООП - парадигма программирования, в основу которой заложено понятие объекта, отождествляемого с объектами окружающего нас мира.

    Основные концепции ООП

    1. Система состоит из объектов
    2. Объекты некоторым образом взаимодействуют между собой
    3. Каждый объект характеризуется своим состоянием и поведением

    Основные принципы ООП

    1. Инкапсуляция - принцип, согласно которому любой класс, а в более широком смысле любая часть системы, должны рассматриваться как чёрный ящик - пользователь должен видеть только интерфейс (список декларируемых свойств и методов) и не вникать во внутреннюю реализацию. Позволяет (теоретически) минимизировать число связей между подсистемами и упростить независимую реализацию и модификацию классов и подсистем.
    2. Наследование - возможность создавать класс на основе существующего с сохранением всех свойств и методов класса-предка (базового класса, суперкласса) с добавлением при необходимости собственных свойсв и методов. Наследование отображает иерархичность реального мира.
    3. Полиморфизм Классы-потомки могут изменять реализацию методов класса-предка, сохраняя его сигнатуру, оставляя тем самым неизменным интерфейс предка. Полиморфизм позволяет обрабатывать объекты классов-потомков как однотипные объекты, не смотря на то, что реализация методов у них может различаться.

    Базовым понятием ООП является класс.

    В ООП рекомендуется использовать camelCase нотацию. Имена классов в PHP принято дополнительно начинать с заглавной буквы: NameClass.

    PHP Code:
    01
    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);  // принудительный вызов деструктора с помощью удаления переменной
      
    Result:
    Cat sayd "I’m a cat"
    использование псевдоконстанты __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__.

    • Создайте класс User_dd;
    • В текстовом редакторе откройте файл oop\users.php;
    • В классе создайте свойства name, login и password;
    • В классе создайте и опишите метод showInfo(), который выводит информацию о пользователе в произвольной форме;
    • Создайте три объекта, экземпляра класса 'User_dd': $user1, $user2 и $user3;
    • Задайте произвольные значения свойств name, login и password для каждого из объектов;
    • Вызовите метод showInfo() для каждого объекта;
    • Сохраните файл oop\users.php.
    PHP Code:
    01
    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 -> "Ivann";
        
    $user01 -> "ivann";
        
    $user01 -> =  "7c2ed4b2b9" ;

        
    $user02 = new User_dd;
        
    $user02 -> "Petro";
        
    $user02 -> "petro";
        
    $user02 -> "81b4fa24b7";

        
    $user03 = new User_dd;
        
    $user03 -> "Fedor";
        
    $user03 -> "fedor";
        
    $user03 -> "8ac0182ec9";

        
    $content.=$user01 -> showInfo();
        
    $content.=$user02 -> showInfo();
        
    $content.=$user03 -> showInfo();
        
    Result:
    name: 'Ivann'; login: 'ivann'; password: '7c2ed4b2b9';
    name: 'Petro'; login: 'petro'; password: '81b4fa24b7';
    name: 'Fedor'; login: 'fedor'; password: '8ac0182ec9';

    Конструктор - магический метод, автоматически вызываемый при создании объекта. Для описания контруктора применяется служебное слово '__construct'. Удобен для инициализации, когда для каждого объекта необходимо выполнить ряд действий. В параметрах конструктора можно указать свойства объекта.

    Деструктор - магический метод, автоматически вызываемый перед удалением объекта. Антипод конструктора. Для вызова метода при удалении объекта применяется '__destruct'. Круглые скобки в деструкторе указываются только из-за особенностей стнтаксиса php. В деструктор нельзя передать параметр. Кроме того, последовательность удаления объектов четко не определена, вследствии чего из деструктора НЕЛЬЗЯ обращаться к другим объектам. Вызывается в двух случаях: при завершении выполнения кода (не совсем) и при удалении объекта.

    Клон. В PHP версии 5 и выше знак '=' при создании объекта на самом деле является присваиванием по ссылке. Копирование же объекта осуществляется при помощи служебного слова 'clone'. В клон нельзя передать параметр. При клонировании конструктор не вызывается!

    PHP Code:
    01
    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);  // принудительный вызов деструктора с помощью удаления переменной
      
    Result:
    Создан объект класса 'Pet_db'. type: cat; name:
    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:
    • В текстовом редакторе откройте файл oop\users.php;
    • В классе User_d2 создайте и опишите конструктор, который принимает в качестве аргументов имя, логин и пароль пользователя;
    • Конструктор должен инициализировать свойства name, login и password;
    • Измените код, который инициализирует объекты, передавая нужные параметры в конструктор;
    • Удалите те строки кода, в которых задаются значения свойств объектов;
    • В классе User_d2 создайте и опишите деструктор;
    • Деструктор должен выводить строку Пользователь [логин_пользователя] удален;
    • Подставьте вместо подстроки [логин_пользователя] значение свойства login;
    • Сохраните файл oop\users.php.
    PHP Code:
    01
    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);  // принудительный вызов деструктора с помощью удаления объектов
      
    Result:
    Создан объект класса 'User_d2'. name: 'Ivann'; login: 'ivann'; password: '8ec4c4354f';
    Создан объект класса '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;
    • Вызвать конструктор по имени класса;
    • Создать, клонировать и присвоить по ссылке объекты;
    • Изменить свойства клонированного и ссыльного объектов;
    • Обратить внимание на количество удаленных объектов.
    PHP Code:
    01
    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->"Sidor";
      
    $user02->"sidor";
      
    $user02->bin2hex(random_bytes(5));

      
    $user03->"Yakov";
      
    $user03->"yakov";
      
    $user03->bin2hex(random_bytes(5));

      
    $user01->showInfo();
      
    $user02->showInfo();
      
    $user03->showInfo();
      echo 
    "<br>\n";

      unset(
    $user01,$user02,$user03);
      
    Result:
    Создан объект класса 'User_69'. name: 'Ivann'; login: 'ivann'; password: '1fcffce26c';
    Клонирован объект класса '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;

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

    Опишем класс SimpleHouse с свойствами: модель, этажность, площадь этажа, цвет. Создадим объект этого класса.

    PHP Code:
    01
    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"
    ;
        }

      }
      
    Result:

    Для создания подобного, но видоизменённого объекта (имеющего, к примеру, дополнительные свойства в виде камина и патио) создадим класс-наследник SuperHouse.

    Наследование происходит с помощью конструкции extends с указанием класса, свойства и методы которого необходимо унаследовать.

    Экземпляр класса-наследника унаследует свойства и методы суперкласса.

    PHP Code:
    01
    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);
      
    Result:
    Создан объект класса 'SimpleHouse_05'. model: 'A-200-120';
    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';

    Наследование можно осуществлять только от одного класса. Множественное наследование не поддерживается.

    Для изменения метода, описанного в суперклассе, существует операция перегрузки метода. Опишем класс Fabric, наследующий класс SimpleHouse. Перегрузим в нём метод build.

    Для выполнения перегруженного родительского метода в методе наследника применяют ключевое слово 'parent' с указанием через двойное двоеточие имени родительского метода. Соответственно обращение из метода в классе-наследнике к неперегруженному родительскому методу осуществляется как к своему - через $this ($this->nameMethod).

    PHP Code:
    01
    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);
      
    Result:
    Создан объект класса 'SimpleHouse_05'. model: 'F-100-560';
    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';
    • В текстовом редакторе откройте файл oop\users.php;
    • Создайте и опишите класс SuperUser, наследованный от класса User;
    • В классе SuperUser создайте свойство role;
    • Перегрузите конструктор супер-класса так, чтобы он принимал четвёртым параметром значение для свойства role;
    • Вызовите из конструктора родительский конструктор и передайте в него первые три параметра;
    • Перегрузите метод супер-класса showInfo() так, чтобы выводилось и значение свойства role;
    • Создайте объект $user, экземпляр класса SuperUser;
    • Вызовите метод showInfo() объекта $user;
    • Сохраните файл oop\users.php.
    PHP Code:
    01
    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;
          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);
      
    Result:
    Создан объект класса 'User_fe'. name: 'Ivann'; login: 'ivann'; password: '9fc201c516';
    Создан объект класса '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;

    Примером встроенного класса в 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', независимо от того, было ли выброшено исключение, перед тем как продолжится нормальное выполнение кода.

    PHP Code:
    01
    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();
      
    Result:
    Start
    Continue
    finally
    The end

    Start
    exception: $var is false!
    in line: 7
    finally
    The end

    Сокрытие внутренних процессов в ООП носит название инкапсуляции.

    Инкапсуляция – это свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе и скрыть детали реализации от пользователя. Инкапсуляция неразрывно связана с понятием интерфейса класса. По сути, всё то, что не входит в интерфейс, инкапсулируется в классе.

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

    Полиморфизм – это возможность создавать разные реализации, используя одинаковый интерфейс (в данном случае интерфейс является способом вызова объекта в коде).

    Абстрактный класс - класс, имеющий хотя бы один абстрактный метод.

    Для объявления абстрактного класса необходимо указать 'abstract' перед ключевым словом 'class'.

    'Что нам это даёт? Ничего! Базовый класс это класс, от которого нельзя создавать объекты. Его можно только наследовать.' - Борисов И.О.

    Абстрактные классы могут содержать абстрактные методы, а классы, имеющие абстрактные методы должены быть абстрактными.

    Абстрактные методы имеют декларацию (описательный смысл) и не имеют реализации.

    Абстрактные методы также предваряет ключевое слово 'abstract'. При наследовании абстрактного класса необходимо описать содержащиеся в нём абстрактные методы.Кроме того, область видимости этих методов должна совпадать или быть менее строгой (если абстрактный метод объявлен как protected, то реализация этого метода должна быть protected или public, но не private). Более того, объявления методов должны совпадать, то есть контроль типов (type hint) и количество обязательных аргументов должно быть одинаковым. К примеру, если в дочернем классе указан необязательный параметр, которого нет в объявлении абстрактного класса, то в данном случае конфликта объявлений методов не будет.

    PHP Code:
    01
    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);
      
    Result:
    Создан объект класса 'AbstractHouse_13'. model: 'SH-01';
    Build Simple House. Объект класса 'SimpleHouse_13'. model: 'SH-01'; square: '70'; floor: '1';

    Интерфейс - Абстрактный класс, содержащий только абстрактные методы (справедливо для php!).

    Так как неабстрактных методов в интерфейсе быть не может, ключевое слово 'abstract' перед объявлением метода не указывается. Интерфейсы не наследуются, а реализуются с помощью ключевого слова 'implements'.

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

    Один интерфейс - множество реализаций. Программируйте на уровне интерфейсов, реализуйте на уровне классов.

    PHP Code:
    01
    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_a8Paintable_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);
      
    Result:
    Создан объект класса 'AbstractHouse_a8'. model: 'SH-01';
    Build Simple House. Объект класса 'SimpleHouse_a8'. model: 'SH-01'; square: '70'; floor: '1';
    Paint Simple House. Объект покрашен в зелёный цвет.
    Paint Simple House. Объект убран.

    Интерфейсы реализуются при необходимости в методах, описанных в них.

    instanceOf - оператор, позволяющий проверить наличие класса в цепочке предков.

    • В текстовом редакторе откройте файл oop\users.php;
    • Создайте и опишите абстрактный класс UserAbstract;
    • В классе UserAbstract объявите абстрактный метод showInfo();
    • Обновите класс User, унаследовав его от абстрактного класса UserAbstract;
    • Если требуется, внесите в класс User необходимые изменения;
    • Сохраните файл oop\users.php.
    PHP Code:
    01
    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;
          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);
      
    Result:
    Создан объект класса 'User_e8'. name: 'Ivann'; login: 'ivann'; password: '8ee45f9018';
    Создан объект класса '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;

    Константа класса - константа, принадлежащая не экземпляру класса, а классу. К константе класса можно обратиться без создания экземпляра класса.

    Статической переменной (в процедурном программировании) называют локальную переменную, сохраняющую своё значение после выхода из функции. Статические свойства присущи и классам. Статическое свойство сохраняет своё значение после выхода из функции. Применяется в основном для реализации счётчиков. Статические свойства не могут быть доступны через объект с помощью оператора '->'.

    Для обращения к константе или к статическому свойству класса используются либо ключевое слово 'self' (при обращении из метода класса), либо имя класса (при обращении без создания экземпляра класса) и через двойное двоеточие указывают имя константы 'self::NAME_CONST' или статического свойства 'self::$nameProperties'.

    Помимо этого существуют статические методы класса. Вызов статического метода осуществляется через имя класса, двойное двоеточие, имя метода: 'NameClass::nameMethod();'. Так как статические методы вызываются без создания экземпляра класса, то псевдопеременная $this недоступна внутри метода, объявленного как статический.

    Объявление свойств и методов класса статическими позволяет обращаться к ним без создания экземпляра класса. Свойство класса, объявленное как статическое, не может быть доступно посредством экземпляра класса (но статический метод может быть вызван).

    PHP Code:
    01
    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);
      
    Result:
    обращение к константе без создания экземпляра класса: ООО 'ИХиО';
    обращение к статическому свойству без создания экземпляра класса: просмотров: 0;
    Спасибо за обращение в нашу компанию.
    обращение к константе из метода класса: ООО 'ИХиО';
    обращение к статическому свойству из метода класса: просмотров: 1;
    обращение к статическому свойству из метода класса: просмотров: 2;
    обращение к статическому свойству из метода класса: просмотров: 3;

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

    Само название 'позднее статическое связывание' отражает в себе внутреннюю реализацию этой особенности. 'Позднее связывание' отражает тот факт, что обращения через static:: не будут вычисляться по отношению к классу, в котором вызываемый метод определен, а будут вычисляться на основе информации в ходе исполнения. Также эта особенность была названа 'статическое связывание' потому, что она может быть использована (но не обязательно) в статических методах.

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

    PHP Code:
    01
    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
      
    Result:
    A_19
    B_19

    Большинство разработчиков объектно-ориентированных приложений используют такое соглашение именования файлов, в котором каждый класс хранится в отдельно созданном для него файле. Одна из самых больших неприятностей - необходимость писать в начале каждого скрипта длинный список подгружаемых файлов (по одному для каждого класса).

    В PHP 5 это делать необязательно. Функция 'spl_autoload_register()' позволяет зарегистрировать необходимое количество автозагрузчиков для автоматической загрузки классов и интерфейсов, если они в настоящее время не определены. Регистрируя автозагрузчики, PHP получает последний шанс для интерпретатора загрузить класс прежде, чем он закончит выполнение скрипта с ошибкой.

    В то время как функция '__autoload()' также может быть использована для автоматической загрузки классов и интерфейсов, следует отдать предпочтение 'spl_autoload_register()', потому, что она предоставляет гораздо более гибкую альтернативу, позволяя регистрировать необходимое количество автозагрузчиков, например, для сторонних библиотек. По этой причине использование '__autoload()' объявлено устаревшим, начиная с PHP 7.2.0 и его использование крайне не рекомендовано.

    Создаём директорию '_php31' и поместим в неё файлы с описанием классов (каждый класс хранится в отдельно созданном для него файле, названном по имени класса с промежуточным расширением '.class': '_php31/NameClass.class.php') и описываем функцию автоматической загрузки классов 'spl_autoload_register'.

    Содержимое файла '_php31/NewClass_53.class.php':
    1
    2
    3
    4
    5
    6
    7
    <?php
      
    class NewClass_53{
        function 
    __construct($n){
          echo 
    "hello, i am $n";
        }
      }
      
    PHP Code:
    1
    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);
      
    Result:
    hello, i am Feodor
    • В текстовом редакторе откройте файл 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.
    Содержимое файла '_php31/UserAbstract_be.class.php':
    1
    2
    3
    4
    <?php
      
    abstract class UserAbstract_be{
        abstract function 
    showInfo();
      }
    Содержимое файла '_php31/User_be.class.php':
    01
    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
    <?php
      
    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"
    ;
        }
      }
    Содержимое файла '_php31/SuperUser_be.class.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <?php
      
    class SuperUser_be extends User_be{
        public 
    $r;
        static public 
    $countSuperUser;

        function 
    __construct($n,$l,$p,$r){
          
    parent::__construct($n,$l,$p);
          
    $this->$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"
    ;
        }
      }
    PHP Code:
    01
    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);
      

    К свойствам и методам классов для повышения градуса абстрактности применимы три модификатора:

    Модификатор public (общедоступный) позволяет получить доступ к свойствам и методам класса из любого места.

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

    Модификатор private (закрытый) ограничивает область видимости так, что доступ к нему имеет только тот класс, в котором объявлен сам элемент.

    Модификаторы protected и private являются чем-то вроде таблички 'для персонала'. Ничего секретного там нет и быть не должно, это просто предупреждение.

    Для доступа на чтение описывается функция, возвращающая значение закрытого свойства.

    В php допускается создание неописанных в классе свойств объектов из глобальной области видимости:

    PHP Code:
    1
    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);
      
    Result:
    legalProp

    Для закрытия этого бага можно воспользоваться магическими методами __set() и __get(). Метод __set() будет выполнен при записи данных в недоступные свойства. Метод __get() будет выполнен при чтении данных из недоступных свойств. Данный финт ушами делается для контроля над необъявленными переменными. Теперь к ним можно обратиться через массив $arr.

    Если при присвоении свойства экземпляру класса объявления этого свойства в классе не найдено (а - свойство не объявлено; б - ограничена область видимости свойства), то сначала проверяется, описан метод '__set()' (1) или не описан (2).

    Если метод '__set()' не описан, то, если свойство не объявлено, (случай 2а) - свойство создается; если ограничена область видимости свойства (2б) - выдаётся сообщение об ошибке; иначе вызывается описанный метод '__set()' (1а, 1б), и там как масть ляжет.

    PHP Code:
    01
    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);
      
    Result:
    ERROR! чтение данных из недоступного свойства! $n='illegalProp'

    print_r($obj->__getArr()):
    Array ( [illegalProp] => 42 )

    С помощью магического метода '__set' можно позволить классу принимать только те свойства, которые мы явно разрешим (запрет на динамическое создание свойств).

    PHP Code:
    01
    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->"42";
      
    $obj->"42";
      
    $obj->illegalProp "42";

      echo 
    "{$obj->illegalProp}<br>\n";

      unset(
    $obj);
      
    Result:
    variable '$x' accepted.
    variable '$y' accepted.
    ERROR 9: illegal variable '$illegalProp'.
    ERROR 14: чтение данных из недоступного свойства! $n='illegalProp'.

    Еще один вариант. При возникновении необходимости дополнительной обработки принятого свойства после написания кучи дочерних классов можно поступить как во многих других языках, где свойству по-умолчанию присваиваится 'privat'. Для обработки входящих данных каждому свойству прописывается геттер и сеттер.

    PHP Code:
    01
    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);
      
    Result:
    EGOR
    42

    Но в этом случае необходимо переписывать и код родительского класса, и точек присвоения/вызова измененяемого свойства. В php это сделать проще - для обработки принятого свойства изменяется только родительский класс, все остальное остается неизменным. По этому заранее описывать геттер и сеттер здесь нет необходимости - это можно сделать в любой момент.

    PHP Code:
    01
    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);
      
    Result:
    EGOR
    42

    Метод __call($name,$args) запускается при вызове недоступных методов в контексте Метод __callStatic($name,$args) запускается при вызове недоступных методов в статическом контексте.

    Аргумент $name принимает имя вызываемого метода. Аргумент $arguments принимает нумерованный массив, содержащий параметры, переданные в вызываемый метод $name.

    PHP Code:
    01
    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);
      
    Result:
    Call undefined method 'name' with arguments: .
    Call undefined static method 'age' with arguments: 42, Timofey.

    Метод __toString() позволяет кодеру решать, как должен реагировать объект при преобразовании его в строку. Например, что вывести при выполнении 'echo $obj;'. Этот метод должен возвращать строку, иначе произойдёт фатальная ошибка уровня E_RECOVERABLE_ERROR.

    Начиная с PHP 5.2.0, преобразование объекта в строку при отсутствии метода __toString() вызывает ошибку E_RECOVERABLE_ERROR.

    Внимание! Нельзя выбросить исключение из метода __toString(). Это приведет к фатальной ошибке.

    PHP Code:
    01
    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);
      
    Result:
    Это объект, экземпляр класса '__CLASS__'

    Метод __invoke() позволяет кодеру решать, как должен реагировать объект при вызове его как функции.

    PHP Code:
    01
    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);
      
    Result:
    10
    25

    В результате сериализации объекта вы получите его строковое представление, которое содержит значение всех переменных, содержащихся в нем. Для того, чтобы восcтановить объект из строки в другом PHP-файле класс A должен быть определен заранее.

    Применяется, например, для сохранения объектов между сессиями.

    Если в приложении производится сериализация объектов для последующего использования, настоятельно рекомендуется подключать определение класса для этого объекта во всем приложении. Невыполнение этого требования может привести к тому, что объект будет десериализован без определения класса, что приведет к тому, что PHP будет использовать для этого объекта класс __PHP_Incomplete_Class_Name, который не имеет методов и сделает объект бесполезным.

    Также можно подключиться к событиям сериализации и десериализации объекта с помощью методов __sleep() и __wakeup(). Метод __sleep() также позволяет сериализовать лишь некоторое подмножество свойств объекта.

    PHP Code:
    01
    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";
      
    Result:

    'Ivann', 'ivann', '21d37ebcc3'
    'otrPe', 'oerpt', 'ed31fad98c'

    Класс, объявленный финальным нельзя наследовать. Применяется, например, при неполном описании класса и намерении кардинального изменения его в будущем.

    Разместив перед объявлениями методов класса ключевое слово final можно предотвратить их переопределение в дочерних классах.

    Свойства не могут быть объявлены окончательными.

    PHP Code:
    01
    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(); // Ошибка!
      

    Трейт - это механизм обеспечения повторного использования кода в языках с поддержкой только одиночного наследования, таких как PHP. Трейт предназначен для уменьшения некоторых ограничений одиночного наследования, позволяя разработчику повторно использовать наборы методов свободно, в нескольких независимых классах и реализованных с использованием разных архитектур построения классов. Семантика комбинации трейтов и классов определена таким образом, чтобы снизить уровень сложности, а также избежать типичных проблем, связанных с множественным наследованием и смешиванием (mixins).

    Трейт очень похож на класс, но предназначен для группирования функционала хорошо структурированым и последовательным образом. Невозможно создать самостоятельный экземпляр трейта. Это дополнение к обычному наследованию и позволяет сделать горизонтальную композицию поведения, то есть применение членов класса без необходимости наследования.

    Несколько трейтов могут быть вставлены в класс путем их перечисления в директиве use, разделяя запятыми.

    PHP Code:
    01
    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();
      
    Result:
    Hello World!
    PHP Code:
    01
    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
      

    В php 7 была добавлена поддержка анонимных классов с помощью new class. Имена анонимным классам присваиваются движком PHP, как показано в примере ниже. Это имя следует рассматривать как особенность реализации, на которую не следует полагаться. Их можно использовать тогда, когда нужен одноразовый класс и создавать полноценный класс, а потом его объект не имеет смысла:

    PHP Code:
    01
    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());
      
    Result:
    object(class@anonymous)#1 (0) { }

    Все объекты, созданные одним и тем же объявлением анонимного класса, являются экземплярами этого самого класса. Анонимные классы могут передавать аргументы в конструкторы, расширять другие классы, реализовывать интерфейсы и использовать трейты как обычный класс:

    PHP Code:
    01
    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;
        }
      );
      
    Result:
    object(class@anonymous)#4 (1) { ["num":"class@anonymous":private]=> int(10) }