ООП в PHP (общие понятия)

 

oop

 

Данный пост, является продолжением поста — «Структура и синтаксис PHP»

 

 

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

(данная возможность добавлена с PHP 5)

 

 

Общие понятия

 

Класс определяется следующим образом:

Пример №1 , описание примитивного класса

 

Пример №2, описание класса с «различными возможностями»:

 

 

P.S. Если перед методом не написать ключевое слово (public,private,protected), то по умолчанию будет считаться public

 

Для создание экземпляра класса – объекта, используется ключевое слово new, пример:

 

 

Мы можем присвоить данный объект другой переменной:

 

 

Теперь переменная $obj и $obj2 указывает на один и тот же объект, все изменения в $obj видны в $obj2.

 
P.S. Помимо этого, объекты можно копировать с помощью ключевого слова clone, например :

Теперь, переменные $obj2 и $obj указывают на разные объекты с идентичными свойствами (и их значениями) и методами. Изменения в $obj не видны в $obj2.
 
 
Доступ к свойствам объекта осуществляется следующим образом:

 

 

Вызов методов делается так:

 

 

Попробуем изменить final метод,

СЛЕДУЮЩИЙ КОД ВЫЗОВЕТ ОШИБКУ

 

  • P.S. Кстати, если метод обращается к не существующему свойству класса, это не будет считаться ошибкой, оно (свойство) просто создаться.
  • P.S. Класс помимо переменных и методов, может содержать также константы, пример использования:
    class myClass {
                    const pi=3.14;
    }
                   
    echo myClass::pi;
     
    Увидеть константы через объекты, почему-то нельзя, работать с ними можно только через оператор разрешения области видимости — «::»

 

Приведу рисунок, чтобы, было понятнее как себя ведут public, protected, private в сочетание с final, при наследование:

(картинка кликабельная, откроется в новом окне)

php1

 

 

Базовым классом я назвал класс-родитель, а дочерний класснаследуемый класс от родителя. Шапка таблицы показывает, как обозначено свойство или метод (public, private, protected, final public, final private, final protected). Ячейки поделены по диагонали пополам, в правом нижнем углу написано, видно или не видно свойство или метод в объекте, в левой верхнем углу ячейке написано, видно или не видно свойство или метод в классе. Например, свойство или метод обозначенное как private в базовом классе видно, но его не видно в объекте базового класса, а также в классах наследнике и его объекте.

P.S. С помощью ключевого слова final помимо запрета на изменения свойств и методов в классах-потомках, можно запретить наследование самого класса, для этого final необходимо написать перед class при описание класса, пример:

 

 

Данный код вызовет ошибку, т.к. класс myClass нельзя наследовать:

 

P.S. Полезно повторить (о чем я уже говорил в разделе «3. Типы данных»),

 

Что с PHP 5 стало возможно явно указывать тип аргументов методов, делается это как и в С/C++, перед аргументам указываем его тип

 

Пример«аргумент должен быть массивом»:

class myClass {

                public function f(array $arg) {

                               …

               }

}

 

$obj=new myClass;

                              

$var=array(1,2,3);

$obj->f($var);

 

Если мы вызовем метод f с аргументом отличного типа от array (массив), то интерпретатор выведет ошибку:

«Catchable fatal error: Argument 1 passed to myClass::f() must be an array, тип_аргумента_который_вы_использовали given, called in …»

P.S. Если вы хотите передать в качестве аргумента объект, то его типом будет имя класса, например:

public function f(ClassName $ObjectName)
 

Также, неплохо повторить, что в PHP как и в С++ есть возможность задавать значение аргументов по умолчанию:

 

class myClass {

                public function f($var=1) {

                               echo $var;

                }

}

 

$obj=new myClass;

$obj->f(); //результат – «1»

$obj->f(2); //результат – «2»

 

 

Статические методы и свойства (static)

 

Помимо «final», которое может применяться в сочетание с public, private, protected в PHP есть еще одно ключевое слово – static. С помощью него, мы можем обозначить методы и свойства как статические, что это значит? А это значит, что к данным методам и свойствам мы сможем обращаться, не создавая экземпляр класса –объект. Работать с ними мы будем напрямую через класс. Статические методы могут вызываться также и в объекте, но статические свойства только через класс. Пример:

 

 

Результат работы:

 

P.S. Заметьте, вместо «$this->» при работе «в классе» используется «self::»

 

Статичный метод мы можем вызывать в экземпляре:

 

 

а статичное свойство нет

 

 

Возникает хороший вопрос, а зачем нам все это? Честно говоря, я сам затрудняюсь дать ответ, где применяются статичные методы и свойства, могу дать один из примеров в Интернете – «счетчик объектов», пример:

 

 

Результат:

 

Также с помощью статичного метода можно организовать «фабрику» объектов (вместо new), а в статичном свойстве вести подсчет созданных объектов. Таким образом, мы сможем, вести учет и ограничивать кол-во создаваемых экземпляров данного класса (мы можем даже запретить создавать экземпляры класса через new, для этого необходимо конструктор — __construct обозначить как private).

 

P.S. Статичное свойство, похоже на константы классов, оно также определяются в классе и обращаются к нему через оператор разрешения «::».

 

 

«Перегрузка» методов и свойств

 

Понятие «перегрузка» в других языках отличается от того, чем это называют в PHP, поэтому я обозначил данный термин в кавычках.

«Перегрузка» методов и свойств это возможность исполнять некий код при обращение к несуществующему методу или свойству.

 

На примере должно быть более понятнее.

 

Для несуществующих свойств:

 

В аргументе $name автоматически передается имя несуществующего свойства, к которому происходит обращение, а в $value (в методе __set()) данные, которые хотят записать в несуществующее свойство. Имя аргументов можем быть любым, главное их последовательность.

 

P.S. Заметьте, что все выше указанные методы могут быть только public.

 

На примере должно быть более понятно:

 

 

Результат работы:

 

P.S. Для чего это применяется, я затрудняюсь ответить. Возникает мысль, что это можно использовать, для объявления и инициализации несуществующих свойств, например, так:

 

public function __set($n,$v) {

                $this->$n=$v;

}

 

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

 

Для несуществующих методов:

 

В аргументе $name автоматически передается имя несуществующего метода, к которому происходит обращение, а в $arguments массив с аргументами. Имя аргументов можем быть любым, главное их последовательность.

 

Пример использования __call:

 

Результат:

 

Пример использования __callStatic:

 

Результат:

 

Опять же, продемонстрировать где применяется данная возможность PHP, я сказать не могу, я просто осветил её

 

 

Конструкторы и деструкторы

 

Конструктор

Конструктор – это метод «__construct()», который вызывается при создание объекта, пример использования: в конструкторе можно задавать состояние объекта (т.е. инициализировать его свойства), пример:

 

 

Метод-конструктор также как и другие методы может принимать аргументы, пример:

 

 

Конструктор наследуется от родителя к наследнику, если в наследнике нет своего конструктора, пример:

 

 

P.S. Если указать конструктор как private, то мы не сможем создать экземпляр класса,  пример:

class myClass {

                private function __construct() {}

}

                              

$obj= new myClass; // ошибка, т.к. конструктор объявлен как private

Данный прием пригодиться,  когда нам надо запретить явно, т.е. через new создавать экземпляры класса и принудить создавать экземпляр через наш метод. Например это может пригодиться для контроля числа создаваемых объектов, подсчет которых ведется в статичных свойствах класса.

 
P.S. В методах (в конструкторе и деструкторе в том числе), можно обращаться к методам (конструктору и деструктуру) класса родителя через parent::__construct(параметры) .
Например, конструктор потомка использует конструктор родителя .
class myCl extends myClass {

                function __construct() {
parent::__construct(параметры);
}
}
Таким образом, мы можем расширить методы (конструкторы и деструктуры) в классах потомках, используя функционал родителей дополнив его в наследниках.

 

Деструктор

Деструктор – это метод «__destruct()», который вызывается при освобождение всех ссылок на объект или при завершение скрипта.

 

Пример – «деструктор вызывается при завершение скрипта»:

 

 

Результат работы скрипта:

 

 

Как видно, по завершению скрипта, срабатывает деструктор объекта.

 

P.S. Деструктор, также как и конструктор наследуется от класса родителя, если в потомке не реализован свой деструктор.

 

Пример – деструктор вызывается при освобождении всех ссылок на объект:

 

 

Результат работы скрипта:

 

 

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

 

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

 

 

Результат работы скрипта:

 

 

Растолкую результат, первая строка «работает конструктор …» выводиться, когда мы первый раз создаем объект и присваиваем его $obj. Вторая строка «работает конструктор …» выводиться  когда, мы второй раз создали объект и присвоили его $obj. Третья строка «работает деструктор …» выводиться из-за того, что компилятор замечает что на «первый объект» не ведет не одна ссылка, поэтому вызывается деструктор. Четвертая строка «последняя команда в скрипте» говорит нам, что сработала последняя команда echo. И при завершении работы скрипта срабатывает деструктор «второго объекта».

 

Для чего нужен деструктор?

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

 

Магические методы
 
В PHP, есть особые методы, называемые «магические», к ним относятся конструкторы, деструкторы и методы для «перезагрузки» что мы рассмотрели выше.
В запасе есть еще несколько методов, о которых самое время рассказать:
 
 
Метод __clone()
 
Данный метод, выполняется когда мы копируем (или клонируем, что синоним) объект.

 
Результат работы:

Зачем нужен __clone() и как его можно использовать?
Допустим у нас в объекте есть свойство с указателем на БД и есть методы по работе с БД, при клонирование желательно обнулить ссылку на БД, иначе две копии будут работать с одной и той-же БД.
 
 
Метод __toString()
 
Данный метод срабатывает, когда мы применяем функцию print или echo к объекту, другими словами хотим его распечатать. С помощью __toString() мы можем управлять что будет выводится при «печати» объекта. Простой пример: 

 
Результат работы:

 
 
Абстрактный класс и абстрактные методы

 

Есть также еще один «вид методов» — абстрактные, с этим понятие связанно «абстрактные классы». Абстрактный класс это класс, в котором находится хотя бы один абстрактный метод. Абстрактный метод это метод, у которого отсутствует реализация в данном классе, есть только прототип (имя и аргументы в скобках). Реализация абстрактного метода должна быть в классах наследниках. У вас может возникнуть вопрос, зачем вводить «новый тип классов», мол, если я захочу, то я в «простом классе» могу объявить методы без реализации, … оказывается не можете. Если вы напишете класс с  методом без реализации:

 

 

То интерпретатор вернет ошибку, что класс myClass не абстрактный и что, мол, так объявлять метод без реализации в «простом классе» нельзя.

 

Итак, подведем итог, абстрактный класс это класс, который может содержать как «простые методы» с реализации так и абстрактные, т.е. без реализации.

 

Зачем нам это нужно?

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

 

Классический пример:

Есть класс по работе с БД, в классе есть метод, формирующий SQL запрос к БД и метод выполняющий SQL запрос к БД. Так вот, метод формирующие запрос к БД будет всегда одинаковый для любой БД работающий с SQL, язык то один и правила для работы с ним одни. А метод, выполняющий SQL запрос к БД, для каждой БД будет разный. Выходит, что метод, формирующий SQL запрос для всех один, а метод, выполняющий SQL запрос, от каждого случая разный. Для этого и создается один общий абстрактный класс, с реализованным методом для формирования запроса и абстрактным методом, выполняющим запрос. Далее создаются классы для каждой БД, например для MySQL, MS SQL, Oracle, причем эти классы наследуются от общего абстрактного класса. В каждом классе реализуется свой метод по запросу к БД, причем по одному шаблону, который указан в  общем абстрактном классе.

 

Какие преимущества?

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

 

Перейдем от теории к практики, как реализовать абстрактный класс:

 

 

P.S. Создать экземпляр абстрактного класса нельзя, т.к. в нем не реализованы абстрактные методы 

 

Результат работы:

 

 

Стоит обратить на еще одну особенность:

В классе наследнике от абстрактного класса, должны реализовываться абстрактные методы с тем же кол-вом аргументов, как указанно в «шаблоне» абстрактного метода, все логично, т.е. если есть абстрактный метод:

 

то и реализация должна быть с тем же набором параметров

 

Но тут есть особенность, в реализуемом методе, мы можем добавить свои «необязательные параметры» (т.е. параметры, имеющие значение по умолчанию), это не будет считаться ошибкой, т.е. если есть абстрактный метод:

 

то в реализуемом методе мы можем сделать такое

 

 

Интерфейс

 

Язык PHP поддерживает «интерфейсы», что это? Интерфейс это нечто похожее на класс, точнее на абстрактный класс. В интерфейсе описываются методы без реализации (как бы абстрактные методы). Пример интерфейса:

 

 

Для чего он нужен?

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

 

Как прикрутить интерфейс к классу?

Если говорить более грамотно, то, как реализовать или осуществить интерфейс для класса, говорить «наследовать интерфейс» не правильно. Итак, для реализации интерфейса для класса, нужно использовать ключевое слово «implements», пример:

 

 

Если мы в классе myClass указали бы метод с другим именем или другое количество аргументов (имя аргумента не важно), то интерпретатор вывел бы ошибку. Причем не важно, создали ли вы экземпляр класса или нет, ошибка будет уже на момент описания класса.

 

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

 

 

Класс может реализовать несколько интерфейсов:

 

 

Есть такая интересная особенность, но интерфейсы также как и классы могут наследовать друг друга, пример:

 

 

И как это не странно, интерфейсы поддерживают множественное наследование (классы в PHP множественное наследование не поддерживают), это означает, что один интерфейс может сразу наследовать больше одного интерфейса, т.е. наследовать некое множество интерфейсов. Пример:

 

 

В этом случае, описание класса должно быть таким:

 

 

 

Foreach для объектов и классов

 

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

 

 

Результат работы:

 

 

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

 

Оператор foreach можно использовать и внутри класса, пример:

 

 

Результат работы:

 

 

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

 

THE END.




Буду признателен если вы поделитесь данным постом


Ваш комментарий


Ответ в цифрах

 
© s-engineer.ru, 2012-2017 | Все права защищены