ООП в PHP

Содержание:

  • Введение в ООП

    ООП — это идея, способ, парадигма, которая выражает собой объединение данных и кода, который их обрабатывает,  в одном объекте.

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

    • рост,
    • вес,
    • цвет кожи,
    • возраст и т.д.

    У него есть методы:

    • ходить,
    • говорить,
    • кидать камень,
    • кодить на php и т.д.

    Три ключевых понятия объектно-ориентированного программирования:

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

    В ООП все построено на классах. Класс (Class) можно воспринимать как чертеж, на основе которого будут созданы будущие объекты. Следовательно, объект, созданный на основе класса, называется экземпляром класса. Другими словами, у вас есть чертеж вентилятора. Там будут свойства, такие как: цвет, вес, тип металла, диаметр лопастей и т.д., и методы такие как: вращать лопасти, остановить лопасти, повернуть голову вентилятора вправо,  повернуть голову вентилятора влево.

    Пример создания класса:

    class Fans{ //Fans - имя класса
    	//Описание свойств 
    	//Описание методов
    }
    //Создание экземпляра класса
    $fan = new Fans(); //Экземпляр создается при помощи ключевого слова new
    

    Экземпляр класса представляет собой ни что иное, как тип данных. А именно тип данных object.

    class Fans{
    	//Описание свойств 
    	//Описание методов
    }
    $fan = new Fans();
    echo gettype($fan);//object
    

    Свойства и методы объекта

    Свойства

    По своей сути, свойства — это переменные внутри класса. Но со своими особенностями. Во-первых, у свойств есть модификаторы доступа, которые указываются перед именем свойства и определяют область видимости свойства. Это:

    • public,
    • protected,
    • private.

    Подробнее о модификаторах доступа тут.

    Во-вторых, свойство принадлежит именно объекту. Это означает, что если создать экземпляры класса Pet, например cat и dog., то и у объекта cat и у dog будет набор свойств, объявленных в рамках класса.

    Пример создания свойств:

    class Pet{
        public $age = 3; //можно определять значение
        public $name; //можно не определять значение
    }
    

    Так как тут указан модификатор public, к свойству можно обратиться отовсюду (находясь в любой части скрипта). Как на чтение значения, так и на запись.

    $cat = new Pet();
    $dog = new Pet();
    

    Присваивание значения в свойство объекта

    $cat->name = 'Murzik'; //теперь в свойстве name объекта cat, значение Murzik
    $dog->name = 'Tuzik'; //а в свойстве name объекта dog, значение Tuzik
    

    Чтение значения из свойства объекта

    echo $cat->name;// Murzik
    echo $dog->name;// Tuzik
    

    Методы 

    По своей сути  это обычная функция, которая описывает поведение объекта. Декларируется (объявляется) такая функция внутри тела класса. У метода по аналогии со свойствами есть модификаторы доступа. По умолчанию метод всегда public. Подробнее тут.

    Пример:

    class Pet{
        public $age;
        public $name;
        
        function say($x){
            echo "Object said $x";
        }
    }
    $cat = new Pet();
    $dog = new Pet();
    

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

    $dog->say('Gav'); // Object said Gav
    $cat->say('Meow'); // Object said Meow
    

    Теперь объекты умеют  «говорить». Т.е. мы определили их поведение.

    Ключевое слово this (обращение к свойству/методу объекта из метода)

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

    Пример:

    class Pet{
        public $name;
        
        function say($x){
            echo "Object $this->name said $x"; //осуществляем доступ к свойству
        }
    }
    $cat = new Pet();
    $cat->name = "Murzik";
    $cat->say('Meow'); //Object Murzik said Meow
    
    $dog = new Pet();
    $dog->name = 'Tuzik';
    $dog->say('Gav'); //Object Tuzik said Gav
    

    Слово this означает — «этого объекта» или «того объекта, который вызывает метод», «принадлежащее объекту, который вызывает данный метод». Т.е. мы записали в свойство name, объекта cat, значение Murzik. В свойстве name у объекта dog будет значение Tuzik. Следовательно, если бы в методе say было написано так:

    function say($x){
            echo "Object $name said $x";
        }
    

    тогда при вызове этого метода у любого из существующих экземпляров класса, интерпретатор не смог бы понять, чей именно name нужно использовать. Ключевое слово this как раз и говорит, что нужно обратиться к свойству того объекта, который вызвал этот метод. Также из метода можно обратиться к другому методу этого же объекта. Для этого, по аналогии, используется ключевое слово this.

    Например:

    class Pet{
        public $name;
        
        function say($x){
            echo "Object $this->name said $x";
            $this->drawLine(); //осуществляем доступ к методу
        }
        
        function drawLine(){
            echo "<hr>"; 
        } 
    }
    

    Псевдо-константы

    В ООП есть несколько псевдо-констант. Эти константы отличаются от обычных тем, что значение в них меняется.

    Пример:

    class SuperClass{
        function functionName(){  
            echo "Вызвана функция " . __FUNCTION__;
        }
        
        function className(){  
            echo "Используем класс " . __CLASS__;
        }
        
        function methodName(){  
            echo "Вызван метод " . __METHOD__;
        }
    }
    // Создание объекта
    $obj = new SuperClass();
    // Используем псевдо константы
    $obj->functionName(); // вызвана функция functionName
    $obj->className(); // используем класс SuperClass
    $obj->methodName(); // вызван метод SuperClass::methodName
    

    Магические методы

    (Мануал https://www.php.net/manual/ru/language.oop5.magic.php)

    В ООП есть ряд магических методов. Суть всех этих методов в том, что они вызываются НЕ классическим способом $obj_name->method(), а вызывают их те или иные события, происходящие с экземпляром класса/объектом.

    Существуют следующие магические методы:
    __construct — вызывается при создании объекта.

    class Pet{
    public $name;
        function __construct($x){
            echo "class object " . __CLASS__ ." said  - $x";
        }
    }
    $cat = new Pet('I created'); //создаем объект и провоцируем, тем самым, вызов метода  __construct.
    

    Важно!
    При создании объекта, после имени класса, указываются круглые скобки. Пример $cat = new Pet(). Эти скобки не являются обязательными и экземпляр можно создать без них. Но их использование предназначено именно для того, чтобы передать аргумент в магический метод __construct.

    Может подходить для ситуаций, в которых, например, нужно послать cookie или соединиться с базой данных, при создании нового пользователя. И для многих других.

    __destruct — данный метод является логической противоположностью методу __construct. Т.е. он вызывается при удалении объекта. Т.е. либо при unset($obj) или в конце выполнения скрипта.

    Пример:

    class SuperClass{
        public $number;
        
        function __construct($number){
            $this->number = $number;
        }
        
        function __destruct(){
            echo "object in $this->number number deleted";
        }
    }
    
    $obj = new SuperClass(1);
    unset($obj);
    

    Важно!

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

    Копирование объектов

    (Ур. 3 Д. 1 тайм код 0.58.13)

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

    Первый:

    $x = 10;
    $y = $x;
    echo $y; //10
    

    В данном примере в памяти создается новая ячейка и туда копируется значение переменной x. В результате $y будет полностью самостоятельной копией.

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

    $x = 10;
    $y = &$x;
    

    В данном примере в переменную y будет скопировано не значение, а ссылка на значение, т.е. в памяти так и останется только одно значение 10. Иными словами и X, и Y суть одно и тоже, ссылки на ячейку памяти со значением 10.
    Когда речь идет об объектах, то копирование всегда происходит по ссылке. При этом знак & ставить не нужно.
    Пример:

    // Создание объекта
    $objX = new MyClass(10); // $objX - ссылка на объект в памяти
    $objY = $objX; // $objY - ссылка на тот же объект, на который ссылается $objX
    

    Для более глубокого представления процесса копирования объекта разберем такой пример.

    Шаг 1 мы создаем экземпляр класса.

    $objX = new MyClass(10);
    

    Тут у нас есть два действия. Первое — это new и второе — присваивание =. Так вот, new создаст в памяти объект и установит значение, например 10. После этого произойдет присваивание ссылки на этот объект переменной $objX. Далее, при попытке копирования $objY = $objX, будет создана еще одна ссылка на этот объект.

    Как результат:

    • изменить значение объекта можно как по ссылке objX, так и по ссылке objY,
    • при удалении любой из этих ссылок объект продолжит существовать,
    • объект будет помечен к удалению только после того, как величина имеющихся на него ссылок будет равна нулю.

    Как видим, объекты скопировать невозможно. Мы лишь получаем один и тот же объект под разными именами.

    Важно!
    Основываясь на вышесказанном, можно описать то, как объекты будут вести себя при сравнении. При сравнении двух переменных с ссылками на один и тот же объект, они всегда будут равны.

    class Myclass{
    	//Описание свойств 
    	//Описание методов
    }
    $a = new MyClass();
    $b = $a;
    if($a === $b){
        echo "Ссылки на один и тот же экземпляр всегда равны";
    }
    

    А вот если сравнить две переменные с ссылками на разные на объекты, то они всегда неравны. Даже если содержание объектов идентично!

    class Myclass{
        public $color = 'red';
    }
    $a = new MyClass();
    $b = new MyClass();
    
    if($a !== $b){
        echo "Два разных объекта всегда неравны!";
    }
    

    Клонирование объектов

    В php все-таки есть способ клонировать объекты. Для этого применяется ключевое слово clone.

    $objY = clone $objX; //$objY копия $objX
    

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

    Пример:

    class MyClass{
    	public $value;
    	public $cloneValue; 
       
     function __construct($value){
        $this->value = $value;
     }
     
    function __clone(){
        $this->cloneValue = $this->value + 1;
     }
    }
    
    $a = new MyClass(1);
    $b = clone $a;
    echo $b->cloneValue; //2
    

    Важно!

    Все, что будет сделано при помощи метода __clone, не будет существовать в оригинальном экземпляре.

    Пример:

    class MyClass{
        public $value;
        public $cloneValue = 0;
    
    function __construct($value){
        $this->value = $value;
    }
    
    function __clone(){
        $this->cloneValue = $this->value + 1;
      }
    }
    
    $a = new MyClass(1);
    $b = clone $a;
    echo $b->cloneValue; //2
    echo $a->cloneValue; //0 несмотря на то, что мы обращаемся к свойству $cloneValue 
    //уже после того, как отработал clone, значение в свойстве останется 0.

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

    (Ур. 3 Д.1  тайм код 1.09)

    Это одно из ключевых понятий ООП. Позволяет повторно использовать весь существующий функционал класса, а также все свойства и расширять его новым функционалом, при необходимости. Т.е., у нас может быть чертеж дома. Некая базовая концепция дома. Фундамент, стены, пол, крыша, окна и т.д. А далее, у нас появляется заказчик, который говорит: «ок, дом хорош, но я хочу еще и камин.» Так вот, в данном случае мы и будем использовать наследование. Мы наследуем от базового чертежа дома все свойства и методы и просто дополним его камином.
    Пример:

    class BaseHouse{
        public $model;
        public $square = 0;
        public $floors = 0;
        public $color = 'none';
        
        function __construct($model, $square, $floors, $color){
            $this->model = $model;
            $this->square = $square;
            $this->floors = $floors;
            $this->color = $color;        
        }
        
        function startProject(){
            echo "Start. Model: {$this->model}\n";
        }
        
        function stopProject(){
            echo "Stop. Model: {$this->model}\n\n";
        }
        
        function build(){
            echo "Build. House: {$this->square}x{$this->floors}\n";
        }
        
        function paint(){
            echo "Paint. Color: {$this->color}\n";
        }
    }
    
    //Создание простого дома
    $house = new BaseHouse('t1000', 120, 2, 'white');
    $house->startProject();
    $house->build();
    $house->paint();
    $house->stopProject();
    
    class DerivedHouse extends BaseHouse{ //Все свойства и методы перешли по наследству 
        //в этот класс. При помощи слова extends
        public $fireplace = true; //Мы просто добавили камин
        
        function fire(){ //И расширили класс методом розжига камина
            if($this->fireplace){
                echo "Fueled fireplace\n";
            }
        }
    }
    
    //Создание супер дома
    $superHouse = new DerivedHouse('t1000', 120, 2, 'white');
    $superHouse->startProject(); //Пример использования метода наследника, который не 
    //описан в классе DerivedHouse
    $superHouse->build();
    $superHouse->paint();
    $superHouse->fire();
    $superHouse->stopProject();
    

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

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

    Пример

    class A{
        function test(){
            echo 1 + 1;
        }
    }    
    
    class B extends A{
        function test(){ // Мы перегружаем метод в классе-наследнике, просто описав 
            //его с аналогичным именем, что позволяет изменить и тело метода.
            echo 2 + 2;
        }
    }
    $b = new B();
    $b->test(); //4 Без переопределения было бы 2
    

    Если понадобится, чтобы при переопределении отработал и код, метода test, из родительского класса, необходимо использовать ключевое слово parent.

    Пример

    class A{
        function test(){
            echo 1 + 1;
        }
    }    
    
    class B extends A{
        function test(){
            echo 2 + 2;
           $a =  parent::test(); //тут произойдет вызов этого метода из класса родителя.
        }
    }
    $b = new B();
    $b->test(); //4 и 2
    

    Примечание

    Если parent::test() присвоить переменной при условии, что в родительском методе не будет return, ей присваивается NULL. В тех же случаях, когда в методе родителя есть return, в переменную присваивается возвращаемое значение.

    Пример

    class A{
        function test(){//Пример без return
            echo 1 + 1;
        }
    }    
    
    class B extends A{
        function test(){
            echo 2 + 2;
            $curiosity = parent::test(); //Присваиваем выражение переменной. Тут 
            //выполнится метод test из родительского класса, а в переменную 
            //присваивается NULL
            echo gettype($curiosity);
        }
    }
    $b = new B();
    $b->test(); //вызываем без return и получим 4 2 NULL.
    

    Пример 2

    class A{
        function test(){ //Пример с return
            echo 1 + 1;
            return 1;
        }
    }    
    
    class B extends A{
        function test(){
            $curiosity = parent::test();//Присваиваем выражение переменной
            echo "";
            echo 2 + 2 + $curiosity;
            
        }
    }
    $b = new B();
    $b->test(); //Вызываем с return. Получим 2 5
    

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

    Исключения в php\Обработка исключений

    (Ур. 3 Д. 1 тайм код 1.32.50)

    Идея данного механизма представляет из себя следующее. У нас есть кусок кода, в котором возможна ошибка или исключительная ситуация. Эту часть кода заключаем в некий логический блок. Именуется данный блок try. Далее внутри этого блока, в случае появления исключительной ситуации, выполняется создание экземпляра класса и перебрасывания ссылки на него в другой логический блок. Важно, что выполнение скрипта останавливается на той строчке, где был создан объект и переброшена ссылка в другой блок. Название второго блока — это catch.

    Пример

    function funcName($value){
      try{    
           echo 'Тут начинается некий код' . '\n';
           if(!$value){ //если false, значит это исключительная ситуация
             echo 'раз уж мы сюда зашли, значит “это” случилось.'. '\n';            
             throw new Exception('Тут можно описать суть исключения.'. '\n'); //Создание  
             //объекта и переброс ссылки на него в блок catch, при помощи ключевого слова throw
               }
                
               echo 'А если исключительной ситуации не случилось,'. '\n';
               echo 'Тут может отрабатывать другой код'. '\n';
               echo 'Важно! Я отработаю ТОЛЬКО если не случилось исключение.'. '\n';
           }catch(Exception $error){//Этот блок ловит посланную ему ссылку
               //и присваивает ее в переменную внутри круглых скобок после
               //указания имени класса.
               echo ''. $error->getMessage() . '';//Выведет то описание,
               //которое мы послали при создании объекта Exception.
               echo ''. $error->getFile() . '\n';//Выведе имя файла
               echo ''. $error->getLine() . '\n';//Выведет строку на, которой 
               //случилось исключение
           }
    }    
    
    funcName(false); //Посылаем false, чтобы сработало исключение
    

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

    function a(){
        return 1 + b();
        
    }
    
    function b(){
        return 2 + c();
    }
    
    function c(){
        return 1;
    }
    //и т.д.
    

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

    function a(){
        try{
            return 1 + b();
        }catch(Exception $error){
                    echo $error->getMessage();
                    echo $error->getLine();
                }
    }
    
    function b(){
        return 2 + c();
    }
    //...
    function c($param = 0){
        if($param) return 1;    
        throw new Exception('в двадцать пятую функцию пришел false');
    }
    
    echo a();
    

    Наследование класса Exception

    Так как Exception — это класс, значит его можно наследовать. А, следовательно, мы можем расширить класс или переопределить методы.

    Пример:

    //Объявляем свой класс
    class myException extends Exception{
        public $message;
        function __construct($message){
            $this->message = $message;
        }
        
        function myGetMessage(){
            echo $this->message;
      }
        function myNewCode(){
            return 1; //Тут может быть любой код
        }
    }
    //Реализуем
    function a(){
        try{
            return 1 + b();
        }catch(myException $error){
            echo $error->myGetMessage();
            echo $error->myNewCode();
            }
    }
    
    function b(){
        return 2 + c();
    }
    //...
    function c($param = 0){
        if($param) return 1;    
        throw new myException('в двадцать пятую функцию пришел false');
    }
    echo a();
    

    Важно!
    Можно использовать более одного блока catch. Т.е. оригинальный и любую комбинацию наследников. Но важно, чтобы оригинальный catch был последним в списке.

    Пример:
    //Объявляем свой класс
    class myException extends Exception{
        public $message;
        function __construct($message){
            $this->message = $message;
        }
        
        function myGetMessage(){
            echo $this->message;
      }
        function myNewCode(){
            return 1;
        }
    }
    //Объявляем блок try и два catch
    $a = 'Exception';
    try{
        echo 'начало кода' . '\n';
        if($a == 'myException') throw new myException('ситуация 1');
        if($a == 'Exception') throw new myException('ситуация 2');
        echo 'Исключений не случилось';
    }catch(myException $error){
        echo $error->myGetMessage();
    }catch(Exception $error){ //Exception с оригинальным классом объявляем последним
        echo $error->getMessage();
    }
    

    Блок Finally

    К вышеописанной паре блоков try и catch можно добавлять блок finally. Этот блок отработает в любом случае.Так же важно, что все эти блоки могут что-то возвращать в скрип.

    Пример:

       function test($value){
            try{
                echo 'start' . '\n';
                if(!$value){
                    throw new Exception('ля-ля-ля тополя' . '\n');
                }
                return 1;
            }catch(Exception $e){
                echo $e->getMessage();
                return 2;
            }finally{
                echo 'какой-то код в finally' . '\n';
                return 3;
            }
            echo 'конец кода';
    }
    
    echo test(1);
    

    Разница между try\catch и error handler

    (Ур. 1 Д. 1 тайм код 1:54:02 )

    В завершении разберем разницу в работе try/catch и error handler.

    Отработка error handler:

    • выполняется код до строки, где происходит ошибка (например, строка 5),
    • эта ошибка принимается функцией и обрабатывается,
    • выполнение кода продолжается с шестой строки (рис. 1).

    Разница между работой try_catch и error hendler 1

    Рисунок №1 — Разница между работой try_catch и error handler 1

     

    Отработка try/catch:

    • выполняется код до строки, где происходит исключение (например, строка 5),
    • ключевое слово throw перебрасывает ссылку в блок catch (например 20 строка)
    • Выполняется блок catch
    • Код идет далее
    • Как результат строки с шестой по 20 не выполняются (рис. 2)

    Разница между работой try_catch и error handler 2

    Рисунок №2 — Разница между работой try_catch и error handler 2

    Абстрактные классы

    (Ур. 1 Д. 1 тайм код 1:56:55 )

    Это в некотором роде набросок класса. Он описывает основную идею и не позволяет создавать экземпляр (объект). Абстрактный класс может иметь абстрактные методы, но НЕ ОБЯЗАТЕЛЬНО. У этих методов нет реализации, нет тела. Они выражают идею того, что класс ОБЯЗАТЕЛЬНО должен иметь такой-то и такой функционал, но конечную реализацию оставляют для класса наследника.

    Пример:

    abstract class MyAbstractClass{ //имеет дополнительное слово abstract
        public $color = 'red';
        public $propArr;
        
        function getColor(){
            return $this->color;
        }
        
        abstract function sortProp(); // Объявляем абстрактный метод. Но без реализации.
        
        abstract function mySumm($v, $v2); //Если мы укажем определенный набор аргументов
        //тогда они обязательно должны быть в том же количестве в классе наследнике
        //при реализации
    }
    
    class MyClass extends MyAbstractClass{
        function sortProp(){ //Реализуем обязательный метод
            echo 'что-то сортируем';
        }
        
        function mySumm($v, $v2){
            return $v + $v2;
        }
    }
    
    $obj = new MyClass();
    echo $obj->getColor(); //red
    echo $obj->mySumm(2, 2); // 4
    $obj->sortProp(); // что-то сортируем
    

    При попытке создать экземпляр класса от абстрактного класса получим ошибку.

    $a = new myAbstractClass();// Fatal error: Cannot instantiate abstract class myAbstractClass
    

    Интерфейсы

    (Ур. 1 Д. 1 тайм код  2:11:00)

    Это абстрактные классы, которые содержат только абстрактные методы, а также могут содержать константы. В данном типе классов, при создании, не нужно писать слово abstract, а используется слово interface. Интерфейсы применяются в основном для стандартизации содержания и поведения класса. Иными словами, позволяют архитектору определить набор функций без реализации, набор аргументов и тем самым ответить на вопрос «что мы делаем?» без ответа на вопрос «как?». Создать объект от интерфейса невозможно. Так как в интерфейсе все методы абстрактные, а при попытке создать свойство возникнет ошибка (Interfaces may not include member variables) , наследовать собственно нечего. В следствие этого interface не наследуются, а реализуются. Основное отличие от абстрактных классов это то, что можно реализовывать больше одного интерфейса. Как результат, класс потомок может наследовать один класс и реализовывать сколько угодно интерфейсов.

    Пример:

    interface MyInterface{
         function test($v); //писать abstract не нужно.
    }
    
    class myClass implements MyInterface{ //реализация осуществляется при помощи слова implements
        function test($v){ //В реализующем интерфейс классе,
            //обязательно должен быть описан абстрактный метод интерфейса.
        }
    }
    

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

    При этом у нас может быть программист, который не имеет отношения к созданиям классов и интерфейсов. Он будет просто получать экземпляр класса «жилой дом» или «склад». Также он знает, что есть или будет интерфейс «окрашивание». Так вот, он может просто проверять есть ли в цепочке предков тот или иной интерфейс или класс. И если есть, вызывать метод покраски, а если нет — идти дальше.

    Пример:

    //Абстрактный класс здание
        abstract class Building{
            public $color = 'none';
            public $model;
            public $wallColor;
            
            abstract function start_building();
            //...
            abstract function stop_building();
    }
    //Интерфейс окрашивание
        interface Paintable{
            function paint();
        }
    //Класс жилой дом
        class House extends Building implements Paintable{
            function start_building(){
                echo "Начинаем строительство";
            }
            
            function stop_building(){
                echo "Остановка строительства\n";
            }
            
            function __construct($color){
                $this->color = $color;
            }
            
            function paint(){
                //устанавливаем определенный цвет здания
                $this->wallColor = $this->color;
            }
        }
    //Класс склад
        class Stock extends Building{
                function start_building(){
                echo "Начинаем строительство\n";
            }
            
            function stop_building(){
                echo "Остановка строительства\n";
            }
        }
    //Создание экземпляров классов
    $house = new House('red');
    $stock = new Stock();
    //Код конечного программиста
    //...
    if($house instanceof Paintable){ //Оператор instanceof проверяет есть ли в цепочке предков 
        //класса от которого создан объекта $house, 
        //класс по имени Paintable. Если есть тогда возвращается true.
        $house->paint();
    }
    
    if($stock instanceof Paintable){
        $stock->paint();
    }
    echo $house->color;//red
    echo $stock->color;//none
    

    Константы и статические члены класса

    (Ур. 1 Д. 1  тайм код 2:27:25)

    Константы

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

    const NAME = "значение константы";
    

    В отличии от свойств, константа принадлежит именно классу, а не экземпляру класса. Т.е. константа одна, для всех объектов этого класса. Как результат, при обращении к константе из метода используется уже не this , а ключевое слово self и двойное двоеточие «Paamayim Nekudotayim» ::, а далее имя константы NAME. Для обращения к константе из-за пределов класса также используется двойное двоеточие, className::CONST_NAME. Следовательно, к константе можно обратиться не создавая экземпляра класса. 

    Пример:

    class myClass{
        const NAME = 'my name is Vitaliy';
        
        function getValueConst(){
            echo self::NAME; //Обращаемся к константе из метода
        }
    }
    echo myClass::NAME; //Обращаемся к константе за пределом класса без создания экземпляра
    

    Статические свойства

    доп. мат. — https://habr.com/ru/post/259627/

    Также как и в обычных функциях можно объявлять статические переменные, внутри класса можно создавать статические свойства. Они являются свойствами самого класса. Объявляются при помощи ключевого слова static. При обращении из метода к статическому свойству используется ключевое слово self, далее двойное двоеточие — :: и имя свойства — $name.

    Пример:

    class MyClass{
            public static $count = 0;
        
        function __construct(){
            $this->counter();
        }
        
        function counter(){
            self::$count++; //Знак доллара указывается именно с именем свойства.
            //В противном случае это было бы обращение к константе.
        }
    }
    
    $a = new MyClass();
    $b = new MyClass();
    $c = new MyClass();
    echo MyClass::$count; //В основном это может применяться для создания счетчиков.
    // например, для подсчета количества созданных экземпляров класса.
    

    Статические методы

    В объектно-ориентированной модели существуют еще и статические методы. Данные методы принадлежат классу. Поэтому обращение к статическому методу осуществляется через слово self и :: methodName() или $ClassName::methodName() при обращении за рамками класса. Для объявления статического метода используется слово static.

    Пример:

    class MyClass{    
        static function test(){ //объявляем статический метод
        //Внутри статического метода не должно быть $this
            echo 1;
        }
        
        function test2(){
            self::test(); //Обращаемся к статическому методу из другого метода.
        }
    }
    MyClass::test(); //1. Вызываем статический метод извне класса
    $a = new MyClass();
    $a->test2(); //1
    

    Важно!
    Если статический метод не содержит в теле $this, его можно вызвать и в стандартном динамическом контексте.

    class MyClass{
        static function test(){
            echo 'какой-то результат';
        }
    }
    $a = new MyClass();
    $a->test();//какой-то результат
    
    

    Обратиться же к статическому свойству в динамическом контексте невозможно.

    Позднее статическое связывание

    https://habr.com/ru/post/23066/

    LSB (Late Static Binding, LSB)  дает возможность унаследованным методам обращаться к статическим свойствам, методам и константам класса потомка. Без этого функционала, если наследуемый метод обратится к статическому свойству, он получит его из класса родителя, даже если в классе наследнике свойство переопределено.

    Пример без LSB

    class Beer {
        public static $name = 'Beer!';
        public function getDrinkName() {
               return self::$name;
        }
    }
    class Ale extends Beer {
        public static $name = 'Ale!'; //Переопределяем статическое свойство
    }
    
    $beerDrink = new Beer;
    $aleDrink = new Ale;
    
    echo "Beer is: " . $beerDrink->getDrinkName() ."\n"; //Beer is: Beer!
    echo "Ale is:  " . $aleDrink->getDrinkName()  ."\n"; //Ale is:  Beer!
    

    Не смотря на то, что метод getName унаследован классом Ali, он все же продолжит обращаться к статическому методу родителя. Т.е. его self продолжает указывать на класс, в котором объявлен изначально. Для тех случаев, когда нам нужно, чтобы наследуемый метод все-таки обращался к своему статическому свойству, мы используем LSB через ключевое слово static.

    Пример с LSB

    class Beer {
        public static $name = 'Beer!';
        public function getDrinkName() {
               return static::$name; //Заменяем self на static
        }
    }
    class Ale extends Beer {
        public static $name = 'Ale!'; //Переопределяем статическое свойство
    }
    
    $beerDrink = new Beer;
    $aleDrink = new Ale;
    
    echo "Beer is: " . $beerDrink->getDrinkName() ."\n";//Beer is: Beer!
    echo "Ale is: " . $aleDrink->getDrinkName()  ."\n";//Ale is: Ale!
    

    Функция  __autoload

    (Ур. 1 Д. 1  тайм код 3:03:00)

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

    $a = new NoneExistentClass();
    

    Больше в файле ничего нет. Т.е., мы пытаемся создать экземпляр от несуществующего класса и как результат получим ни что иное, как Fatal error: Class ‘NoneExistentClass’ not found.
    Но перед тем, как вывести ошибку, интерпретатор проверит, а не объявили ли мы функцию __autoload. И если объявили, тогда передает туда имя класса, от которого пытаемся создать объект.
    Пример:

     __autoload($name){ //Событие 2
         echo $name;
     }
    $a = new NoneExistentClass(); //Событие 1
    

    На вопрос  «а зачем это нужно?», можно ответить так — позволяет задать путь к каталогу с классами и автоматически подключать классы при обращении к ним в теле программы.

    Это значит, что каждый отдельный класс мы описываем в отдельном файле. Имена файлов должны соответствовать имени самого класса. Как результат, мы получаем удобную в использовании декомпозицию кода. Декомпозиция — разделение некоего общего целого на части. Например, у нас есть 100 классов и их используют в разной пропорции 3 скрипта. Нам не нужно писать в каждом скрипте десятки include\require. Мы используем __autoload.

    Пример:

     __autoload($name){ //
         require_once "class/$name.php"; //Представим, что у нас есть папка class, а в ней все
        //файлы с отдельными классами.
     }
    $a = new ExistingClass();
    

    Теперь уже мы не получим ошибку. А получим экземпляр класса, описанного в файле ExistingClass.php. Начиная с версии 7.2 объявлена устаревшей.

    Модификаторы доступа

    (Ур. 1 Д. 1 тайм код  3:11:15)

    доп. мат — https://myrusakov.ru/modifikatory-dostupa-php.html

    Модификаторы доступа — это ни что иное, как представление инкапсуляции, в языке php. Инкапсуляция — это способ скрыть реализацию и упаковка данных в единый компонент.

    Всего у нас есть три модификатора. Это:

    • public — к свойству/методу с этим модификатором можно обращаться из любой части скрипта.
    • protected — к свойству/методу с этим модификатором можно обращаться только из класса, в котором объявлено свойство и/или из класса наследника.
    • private — свойство/метод доступно только в рамках класса, в котором объявлено.

    Иными словами,  это три параметра, определяющих область видимости свойств и методов, которые позволяют обеспечить или ограничить доступ к тем или иным свойствам/методам. У метода по умолчанию стоит public. А вот для определения метода как protected или private, нужно по аналогии со свойством использовать ключевые слова.  Допустим, у нас есть свойства, которые нужно защитить от перезаписи.

    Пример:

    class User{
    //создаем приватные свойства
        private $name;
        private $login;
        private $password;
        private $sex;
        
        public function __construct($name, $login, $password){
            $this->name = $name;
            $this->name = $login;
            $this->name = $password;
        }
        //Теперь, для того, чтобы 
        //конечный программист, из внешнего кода, мог получить доступ на чтение и запись
        //к свойствам этого класса, мы создадим такие
        //методы как "гетеры" и "сеттеры".
        
        public function getName(){
            return $this->name;
        }
        public function getlogin(){
            return $this->login;
        }
        public function getPassword(){
            return $this->password;
        }
       public function getSex(){
            return $this->sex;
        }
        public function setSex($value){
            $this->sex = $value;
        }
    }
    $user = new User('Ivan', 'T1000', 'qwerty');
    

    Попытка обратиться на чтение или запись к любому из свойств приведет к ошибке.

    $user->name = 'Anton'; //Fatal error: Cannot access private property User::$name
    echo $this->password; //Using $this when not in object context
    

    Тем самым, мы стандартизировали заполнение основных свойств.  И создали в классе метод setSex, позволяющий нам записывать значение в свойство sex.

    $user->setSex('male'); //Применяем сеттер
    echo $user->getSex(); //male. Применяем гетер
    

    Магические методы __set, __get, __call, callstatic, __toString, __invoke

    Для полной инкапсуляции, приватности объекта мы можем отслеживать и реагировать на такие события как:

    • попытка чтения/записи свойства, которое является приватным или не существует,
    • попытка вызова метода, который не существует или является приватным,
    • попытка вызова статического метода.

    Применяются для этого магические методы __set, __get, __call и callstatic

    Магический метод __set будет вызван после того, как произойдет попытка

    установить значение в свойство, которое не было объявлено или является приватным.

    Пример:

     class MyClass{
           private $name;
           private $age;
                
           function __set($name, $value){
                try{
                   //если свойств мало, можно поступить следующим образом
                   switch($name){
                   case 'name': $this->name = $value;break;
                   case 'age': $this->age = $value;break;
                   default: throw new Exception('Вы пытаетесь установить
                   значение  в свойство, которое не объявлено в классе. 
                   Имя свойства :' . $name . '\n' .
                   'Значение :' . $value);
                   }
                  }catch(Exception $e){
                        echo $e->getMessage();
                }
              }
            }
    $user = new MyClass ();
    $user->password = 12345; //записываем в необъявленное свойство.
    echo gettype($user->password); //NULL
    

    Ошибка не произойдет. Свойство НЕ будет создано. После того, как мы попытаемся записать значение в несуществующее свойство, интерпретатор проверит, а не объявлен ли магический метод __set и если объявлен, передаст ему имя и значение свойства и вызовет его. Результатом кода, описанного выше, мы позволим обращаться только к свойствам $name и $age, а записывать что-либо в необъявленные свойства запретим. А вот если мы не опишем метод __set, мы получим совсем другую картину.

    Пример:

    class MyClass{
          private $name;
          private $age;
                
               
        }
    $user = new MyClass ();
    $user->password = 12345; //записываем в необъявленное свойство.
    echo $user->password; // 12345
    

    Свойство будет создано и ошибки также не будет.

    Магический метод __get будет вызван в том случае, если произойдет запрос на чтение свойства, которое не объявлено или является приватным.

    Пример:

    class MyClass{
           private $name;
           private $age;
                
           function __get($name){
                try{
                //если свойств мало, можно поступить следующим образом
               switch($name){
                   case 'name': echo $this->name;break;
                   case 'age': echo $this->age;break;
                   default: throw new Exception('Вы пытаетесь считать значение свойства,  
                   которого не объявлено в классе. Имя свойства : ' . $name);
                    }
                  }catch(Exception $e){
                   echo $e->getMessage();
                }
              }
            }
    $user = new MyClass ();
    echo $user->password; //Обращение на чтение к несуществующему свойству.
    

    Результатом кода, описанного выше, мы позволим считывать только свойства $name и $age, а считывать с несуществующих запретим.

    Магический метод __call — будет вызван при попытке вызвать несуществующий/приватный метод.

    Пример

    class MyClass{
          private $name;
          private $age;
        
          public function getName(){
               return $this->name;
             }
           public function getAge(){
                   return $this->age;
                }
                
            function __call($methodName, $arrayArg){
                try{
                switch($name){
                      case 'getName': $this->getName();break;
                      case 'getAge': $this->getAge();break;
                      default: throw new Exception('Вы пытаетесь вызвать
                      метод, который не объявлен в классе. 
                      Имя метода : ' . $methodName);                        
                    }
                  }catch(Exception $e){
                        echo $e->getMessage();
                }
             }
        }        
    $user = new MyClass();
    echo $user->foo(); //Вызываем несуществующий метод.
    

    Метод __callStatic — срабатывает при вызове защищенных статических методов или не объявленных в классе.

    Пример

    class MyClass{
                private $name;
                private $age;
        
                public function getName(){
                    return $this->name;
                }
                public function getAge(){
                    return $this->age;
                }
                
                function __callStatic($methodName, $arrayArg){
                  echo 'Вы обращаетесь к несуществующему статическому методу ' 
                  . $methodName . ' '. 'Со списком аргументов : ';
                  print_r($arrayArg);
             }
        }        
    $user = new MyClass();
    echo MyClass::foo(1,2,3,4,5); //Вызываем несуществующий  статический метод. Вернет
    //вы обращаетесь к несуществующему статическому методу foo
    //со списком аргументов : Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 )
    

    Как результат, мы запретили вызывать несуществующие статические методы.

    Магический метод __toString — вызывается при попытке приведения объекта к строке. Например, echo $obj или strval($obj). После выполнения __toString() дальнейший код не выполняется.

    Пример:

    class MyClass{
            private $x;
            private $y;
            public  $z = 'Secret';
            public static $count;
            
            function __construct($x, $y){
                $this->x = $x;
                $this->y = $y;
            }
            
            function __toString(){
                print_r($this); //$this - тоже самое что и print_r($obj)
                $arr = [];
                foreach($this as $k=>$v){ //может перебирать свойства объекта.
                    $arr[$k] = $v;
                }
                echo '<pre>';
                print_r($arr); //Array([x] => 10[y] => 20[z] => Secret)
                echo '<pre>';
    }

    }
    $obj = new MyClass(10, 20);
    strval($obj);

    Магический метод __invoke — вызывается при обращении к объекту, как к функции — «$obj(2, 2, 3);».

    Пример:

     class MyClass{
            public $x;
            function __invoke($a, $b, $c){
                echo $a + $b - $c;
            }
    }
    
    $obj = new MyClass();
    $obj(2, 2, 3);//1
    

    Сериализация объектов в php и магические методы __sleep() и __wakeup

    Сериализация необходима для того, чтобы преобразовать объект к строке и сохранить эту строку в БД или файле. В последствии, восстановить объект из этой строки. Это может понадобиться при работе с сессиями или при переходах между скриптами. При сериализации сохраняются только свойства объекта. После запуска функции serialize($obj), сериализуются только те свойства, которые будут указаны в магическом методе __sleep() внутри класса, если он конечно описан. В противном случае, сериализуются все свойства объекта. Рассмотрим пример, когда может понадобиться __sleep. Например, у нас есть экземпляр класса User.У него среди прочих есть свойство password. Так вот мы хотим, чтобы на жестком диске/хостинге/БД не хранилась строка с паролем и его значением. Для этого мы и применим __sleep. Он выступит в качестве фильтра ,который пропустит только те свойства, которые будут указаны.

    Пример без __sleep

    class User{
        public $name;
        public $login;
        public $password;
        public $time;
        
        function __construct($name, $login, $password){
            $this->name = $name;
            $this->login = $login;
            $this->password = $password;
            $this->time = time();
        }
    }
    
    $obj = new User('Ivan', 'Batman', 'qwerty');
    $string = serialize($obj);
    echo $string; //O:4:"User":4:{s:4:"name";s:4:"Ivan";s:5:"login";s:6:"Batman";s:8:"password";s:6:"qwerty";s:4:"time";i:1564747562;}
    $objNew = unserialize($string);
    print_r($objNew); //User Object ( [name] => Ivan [login] => Batman [password] => qwerty [time] => 1564747562 )
    

    Как видно и сама строка, и новый объект содержат значение свойства password, что не всегда хорошо.

    Пример с применением __sleep

    class User{
        public $name;
        public $login;
        public $password;
        public $time;
        
        function __construct($name, $login, $password){
            $this->name = $name;
            $this->login = $login;
            $this->password = $password;
            $this->time = time();
        }
        
        function __sleep(){
            return array('name', 'login', 'time'); //Указываем имена свойств, которые необходимо
            //пропустить на сериализацию.
        }
    }
    
    $obj = new User('Ivan', 'Batman', 'qwerty');
    $string = serialize($obj);//Перед тем, как отработает serialize, будет вызван магический метод __sleep.
    echo $string; //O:4:"User":3:{s:4:"name";s:4:"Ivan";s:5:"login";s:6:"Batman";s:4:"time";i:1564747606;}
    $objNew = unserialize($string);
    print_r($objNew); //User Object ( [name] => Ivan [login] => Batman [password] => [time] => 1564747606 ) 
    

    Теперь ни в строке, ни в новом объекте не хранится значение свойства password. Также могут возникнуть ситуации, когда после восстановления объекта необходимо инициализировать, например, соединение с БД или обновить данные в свойстве. Для этого применяется метод __wakeup.

    Пример с применением __wakeup

    class User{
        public $name;
        public $login;
        public $password;
        public $time;
        
        function __construct($name, $login, $password){
            $this->name = $name;
            $this->login = $login;
            $this->password = $password;
            $this->time = time();
        }
        
        function __sleep(){
            return array('name', 'login', 'time');
        }
        
        function __wakeup(){ //В объекте инициализированом после сериализации
            //мы обновим значение свойства time. Создадим для наглядности
            //искусственную задержку на 5 сек.
            $timeStart = time();
            $timeEnd = $timeStart + 5;
            while($timeEnd >= time()){
                $this->time = time();
            }   
        }
    }
    
    $obj = new User('Ivan', 'Batman', 'qwerty');
    print_r($obj); //[time] => текущая метка времени
    $string = serialize($obj);
    $objNew = unserialize($string); //unserialize спровоцирует вызов __wakeup
    print_r($objNew); //В объекте инициализированом после сериализации
    значение обновлено [time] => текущая метка времени + 5 сек
    

    Финальные классы и методы

    (Ур. 1 Д. 1 тайм код 4:02:24)

    Финальный класс — это противоположность абстрактным классам. Их нельзя наследовать, а можно только создавать экземпляры. Для обозначения класса, как финальный, используется слово final.

    Пример:

    final class MyClass{
        //...
    }
    

    Финальный метод нельзя переопределять. Т.е., если в классе родителе есть метод с словом final, в классе наследнике каким бы то ни было образом переопределить данный метод нельзя.

    Пример:

    class MyClass{
        final function test(){
            //тело метода
        }
    }
    
    class A extends MyClass{
            final function test(){
            echo 'Переопределим метод'; //Cannot override final method MyClass::test()
        }
    }
    

    Множественное наследование/ или Типажи(traits)

    (Ур. 1 Д. 1 тайм код 4:02:24)

    доп. мат — https://php5.kiev.ua/php7/language.oop5.traits.html

    Так как в php не используется множественное наследование в качестве фичи или костыля, существуют разные мнения, с версии 5.4.0 были добавлены трейты.

    Трейт — это механизм дополняющий основное наследование. Создать экземпляр класса от трейта нельзя.

    Пример:

    trait A{ //Объявляется трейт при помощи ключевого слова trait
        function getText(){
            echo "Я есть текст";
        }
    }
    
    trait B{
        function getNumber(){
            echo 2 + 2;
        }
    }
    
    class C{
        use A, B; //"подмешивание" трейта в класc осуществляется при помощи слова use
    }
    
    $obj = new C();
    $obj->getText(); //Я есть текст
    $obj->getNumber(); //4
    

    При попытке создать экземпляр класса от трейта получим такой результат:

    $objTraite = new A(); //Fatal error: Uncaught Error: Cannot instantiate trait A
    

    Помимо подмешивания в класс, трейты могут наследовать другие трейты.

    Пример

    trait A{
        function getText(){
            return "text";
        }
    }
    
    trait B{
        use A;
    }
    
    class C{
     use B;    
    }
    $obj = new C;
    echo $obj->getText(); //text
    

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

    Пример

      trait A{
        function test(){
            echo 'тело';
        }
    }
    
    trait B{
        function test(){
            echo 'тело 2';
        }
    }
    
    class C{
        use A, B; //Trait method test has not been applied, because there are collisions with other trait methods on C
    }
    

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

    trait A{
        function test(){
            echo 'Я из трейта А' . '\n';
        }
    }
    
    trait B{
        function test(){
            echo 'Я из трейта B' . '\n';
        }
    }
    
    class C{
        use A, B{
         A::test as aliasTest; //устанавливаем псевдоним при помощи оператора as.
         B::test insteadof A; //Оператор insteadof говорит, что если мы дернем метод test
            //то обратиться нужно к трейту B вместо A.
        } 
    }
    $obj = new C();
    $obj->test();
    $obj->aliasTest();
    

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

    Изменение модификаторов доступа при помощи трейтов

    При использовании трейтов возможны возникновения следующих ситуаций.

    Пример:

    trait A{
            private function test{
                return 'какое-то значение';
            }
    }
    
    class B{
        use A{
            test as public; //Данная команда означает, что метод test теперь
                //используется как public. Что нарушает логику инкапсуляции.
        }
    }
    

    Уточнение типа данных в PHP

    (Ур. 1 Д. 1 тайм код 4:20:31)

    доп. мат. —https://habr.com/ru/post/259991/

    Псевдо тип callable — псевдотип данных, смысл которого “что-то, что может быть вызвано как функция”.  Наиболее часто используемый в PHP вариант callable — это ни что иное, как анонимная функция.

    Пример:

    $a = function($x){ //объявляем анонимную функцию
        return $x + 2;
    };
    
    if(is_callable($a)){
        echo 'значение переменной может быть вызвано в качестве функции';
    };
    

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

    Пример:

    function methodName($x){
        return $x + 2;
    }
    
    $a = 'methodName';
    
    if(is_callable($a)){
        echo 'значение переменной может быть вызвано в качестве функции';
    }
    

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

    class A{
        public $name;
    }
    
    class B{
        public $name;
    }
    $objA = new A();
    $objB = new B();
    
    
    function foo(B $obj){ //теперь функция примет в качестве аргумента только объект и при этом экземпляр класса B.
        echo 'какой-то код';
    }
    foo($objB); //Отработает
    foo($objA); // Uncaught TypeError: Argument 1 passed to foo() must be an instance of B, instance of A given, called…
    

    Во-вторых, в качестве callable можем использовать строковое представление статического метода вида ‘ClassName::method’.

    Пример:

    class className{
        static function methodName($x){
            return $x + 2;
        }
    }
    
    $a = 'className::methodName';
    
    if(is_callable($a)){
        echo 'значение переменной может быть вызвано в качестве функции';
    };
    echo call_user_func($a, 2); // Вторым аргументом передаем аргумент для анонимной функции/статического метода
    

    Важно!
    Вызвать статический метод, имя которого является типом callable можно только через функцию call_user_func().

    Автор Виталий Сухомлинов
    практикующий Seo-специалист
    и программист любитель

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *