[Перевод] Магические Методы В Php

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

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

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

Я думаю, это можно считать своего рода отправной точкой в мир магических методов.



Приступаем к учебе

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

Я считаю, что для того, чтобы что-то понять, нужно попробовать это в контексте реальной проблемы.

Вот с этого мы и начнем.

Давайте представим, что мы хотим получать все твиты с помощью API Tweeter. Мы получаем JSON всех твитов текущего пользователя и хотим превратить каждый твит в объект с методами, которые позволят нам выполнять определенные операции.

Ниже я представил базовый класс Tweet:

  
  
  
  
  
  
  
  
  
  
  
  
  
  
   

class Tweet { }

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

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

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

Пожалуй, одним из наиболее распространенных магических методов является конструктор ( __construct() ).

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

Метод __construct() вызывается автоматически при создании экземпляра объекта.

В нем вы можете задать исходные свойства объекта или установить зависимости.

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

public function __construct($id, $text) { $this->id = $id; $this->text = $text; } $tweet = new Tweet(123, 'Hello world');

Когда мы создаем экземпляр класса Tweet, мы можем передать параметры, которые будут переданы в метод __construct().

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

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

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



class Entity { protected $meta; public function __construct(array $meta) { $this->meta = $meta; } } class Tweet extends Entity { protected $id; protected $text; public function __construct($id, $text, array $meta) { $this->id = $id; $this->text = $text; parent::__construct($meta); } }

При попытке удалить объект будет вызван метод __destruct().

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

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



public function __destruct() { $this->connection->destroy(); }

Честно говоря, я скрыл от вас большую часть описанного выше метода __destruct().

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

Жизненный цикл запроса в самом PHP настолько короток, что этот метод принесет больше хлопот, чем пользы.



Геттеры и сеттеры

Когда вы работаете с объектами в PHP, вам бы хотелось получить доступ к свойствам объекта примерно так:

$tweet = new Tweet(123, 'hello world'); echo $tweet->text; // 'hello world'

Однако если для свойства text установлен модификатор доступа protected, то такой вызов вызовет ошибку.

Волшебный метод __get() перехватывает вызовы любых закрытых свойств.



public function __get($property) { if (property_exists($this, $property)) { return $this->$property; } }

Метод __get() принимает в качестве аргумента имя свойства, к которому вы обращаетесь.

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

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

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

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

Этот метод принимает в качестве аргументов 2 параметра — свойство, в которое хотели записать значение, и само значение.

Если вы хотите использовать этот метод, ваш класс получит такое свойство:

public function __set($property, $value) { if (property_exists($this, $property)) { $this->$property = $value; } } $tweet->text = 'Setting up my twttr'; echo $tweet->text; // 'Setting up my twttr'

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

Однако работать с этими волшебными методами не всегда будет лучшей идеей.

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

Однако иногда вы все равно будете сталкиваться с методами __get() и __set(), которые обычно называются геттерами и сеттерами соответственно.

Это довольно хорошее решение, если вы решите изменить какое-то значение или добавить бизнес-логику.



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

Если вы знакомы с PHP, вы, вероятно, знаете о функции isset(), которая обычно используется при работе с массивами.

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



public function __isset($property) { return isset($this->$property); } isset($tweet->text); // true

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

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



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

Опять же, вы можете использовать функцию unset(), чтобы очистить значение закрытого свойства.

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



public function __unset($property) { unset($this->$property); }



Приведение к строке

Метод __toString() позволит вам определить логику вашего приложения при попытке привести объект к строковому типу.

Например:

public function __toString() { return $this->text; } $tweet = new Tweet(1, 'hello world'); echo $tweet; // 'hello world'

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

Хорошей иллюстрацией в данном случае являются Eloquent Models из фреймворка Laravel. Когда вы попытаетесь привести объект к строке, вы получите json. Если вы хотите увидеть, как Laravel это делает, я рекомендую посмотреть исходный код .



Сон и пробуждение

Функция сериализации (serialize()) — довольно распространенный способ хранения объекта.

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

Метод __sleep() позволяет вам определить, какие свойства следует сохранить.

Если бы мы, например, не хотели поддерживать какие-либо соединения или внешние ресурсы.

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



$tweet = new Tweet(123, 'Hello world', new PDO ('mysql:host=localhost;dbname=twttr', 'root'));

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

Поэтому в методе __sleep() мы определим массив свойств, которые необходимо сохранить.



public function __sleep() { return array('id', 'text'); }

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

В этом конкретном примере нам нужно установить соединение с базой данных.

Это можно сделать с помощью магического метода __wakeup().



public function __wakeup() { $this->storage->connect(); }



Методы вызова

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

Например, у вас может быть массив данных, который вы хотите изменить:

class Tweet { protected $id; protected $text; protected $meta; public function __construct($id, $text, array $meta) { $this->id = $id; $this->text = $text; $this->meta = $meta; } protected function retweet() { $this->meta['retweets']++; } protected function favourite() { $this->meta['favourites']++; } public function __get($property) { var_dump($this->$property); } public function __call($method, $parameters) { if (in_array($method, array('retweet', 'favourite'))) { return call_user_func_array(array($this, $method), $parameters); } } } $tweet = new Tweet(123, 'hello world', array('retweets' => 23, 'favourites' => 17)); $tweet->retweet(); $tweet->meta; // array(2) { ["retweets"]=> int(24) ["favourites"]=> int(17) }

Другой типичный пример — использование другого общедоступного API в вашем объекте.



class Location {

Теги: #обучение #php #магические методы #php

Вместе с данным постом часто просматривают: