Статьи

9 магических методов в PHP

Заголовок немного похож на сельдь, так как в PHP более 9 магических методов, но они помогут вам начать использовать магические методы PHP . Это может быть волшебство, но никаких палочек не требуется!

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

__construct

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

class Animal{
 
  public function __construct() {
    $this->created = time();
    $this->logfile_handle = fopen('/tmp/log.txt', 'w');
  }
 
}

Теперь мы можем создать класс, который наследуется от класса Animal — Penguin! Не добавляя ничего в класс Penguin, мы можем объявить это и наследовать от Animal, например так:

class Penguin extends Animal {
 
}
 
$tux = new Penguin;
echo $tux->created;

Если мы определим __constructметод в классе Penguin, тогда объекты Penguin будут запускать его, когда они создаются. Поскольку его нет, PHP ищет информацию в родительском классе и использует ее. Таким образом, мы можем переопределить или нет, в нашем новом классе — очень удобно.

__destruct

Вы заметили дескриптор файла, который также был частью конструктора? Мы действительно не хотим оставлять такие вещи такими, когда мы заканчиваем использовать объект, и поэтому __destructметод делает противоположность конструктору. Он запускается, когда объект уничтожается, либо явно нами, либо когда мы его больше не используем, и PHP очищает его для нас. Для Animal наш __destructметод может выглядеть примерно так:

class Animal{
 
  public function __construct() {
    $this->created = time();
    $this->logfile_handle = fopen('/tmp/log.txt', 'w');
  }
 
  public function __destruct() {
    fclose($this->logfile_handle);
  }
}

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

__получить

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

class Penguin extends Animal {
 
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
 
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
}

Теперь, если после загрузки наш пингвин будет иметь свойства «имя» и «возраст», мы сможем сделать следующее:

$tux = new Penguin(3);
echo $tux->name . " is " . $tux->age . " years old\n";

Однако представьте, что что-то изменилось в серверной базе данных или поставщике информации, поэтому вместо «name» свойство было названо «username». И представьте, что это сложное приложение, которое ссылается на свойство name в слишком многих местах, чтобы мы могли его изменить. Мы можем использовать __getметод, чтобы сделать вид, что свойство «name» все еще существует:

class Penguin extends Animal {
 
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
 
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
 
  public function __get($field) {
    if($field == 'name') {
      return $this->username;
    }
}

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

__установлен

Таким образом, мы обновили все вызовы для $this->nameвозврата, $this->usernameно как насчет того, когда мы хотим установить это значение, возможно, у нас есть экран учетной записи, где пользователи могут изменить свое имя? Справка доступна в форме __set метода, и ее проще всего проиллюстрировать на примере.

class Penguin extends Animal {
 
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
 
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
 
  public function __get($field) {
    if($field == 'name') {
      return $this->username;
    }
  }
 
  public function __set($field, $value) {
    if($field == 'name') {
      $this->username = $value;
    }
  }
}

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

__вызов

На самом деле есть два метода, которые достаточно похожи, чтобы они не получили свой собственный заголовок в этом посте! Первый — это __callметод, который вызывается , если он определен, когда для этого объекта вызывается неопределенный метод. Второе — это то, __callStaticчто ведет себя точно так же, но вместо этого отвечает на неопределенные статические вызовы методов (это было добавлено в PHP 5.3). Вероятно, наиболее распространенной вещью, которую я использую, __callявляется вежливая обработка ошибок, и это особенно полезно в библиотечном коде, где другие люди могут нуждаться в интеграции с вашими методами. Так, например, если в скрипте был объект Penguin с именем $ penguin, и он содержал $penguin->speak()speak()метод не определен, поэтому в обычных условиях мы увидим:


PHP Fatal error: Call to undefined method Penguin::speak() in ...

Что мы можем сделать, так это добавить что-то, чтобы лучше справиться с таким типом ошибки, чем фатальная ошибка PHP, которую вы видите здесь, путем объявления метода __call. Например:

class Animal {
}
class Penguin extends Animal {
 
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
 
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
 
  public function __get($field) {
    if($field == 'name') {
      return $this->username;
    }
  }
 
  public function __set($field, $value) {
    if($field == 'name') {
      $this->username = $value;
    }
  }
 
  public function __call($method, $args) {
      echo "unknown method " . $method;
      return false;
  }
}

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

__спать

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

__просыпайся

Это противоположность __sleep()метода и позволяет изменить поведение десериализации объекта. Используемый совместно с __sleep()этим, он может использоваться для восстановления дескрипторов и ссылок на объекты, которые были удалены при сериализации объекта. Хорошим примером приложения может быть дескриптор базы данных, который сбрасывается при сериализации элемента, а затем восстанавливается путем обращения к текущим параметрам конфигурации, когда элемент не сериализован.

__clone

Мы рассмотрели пример использования cloneключевого слова во второй части моего введения в ООП в PHP, чтобы сделать копию объекта, а не иметь две переменные, указывающие на одни и те же фактические данные. Переопределив этот метод в классе, мы можем повлиять на то, что происходит, когда для этого объекта используется ключевое слово clone. Хотя это не то, с чем мы сталкиваемся каждый день, хороший вариант использования — создать настоящий синглтон, добавив в метод модификатор личного доступа.

__нанизывать

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

class Penguin {
 
  public function __construct($name) {
      $this->species = 'Penguin';
      $this->name = $name;
  }
 
  public function __toString() {
      return $this->name . " (" . $this->species . ")\n";
  }
}

Имея это в виду, мы можем буквально вывести объект, вызвав для него echo, например так:

$tux = new Penguin('tux');
echo $tux;

Я не часто использую этот ярлык, но полезно знать, что он там есть.

Больше магических методов

На самом сайте php.net есть отличная ссылка, в которой перечислены все доступные магические методы (да, их больше, я выбрал только те, которые, по моему мнению, лучше всего начинать), так что если вы хотите узнать, что еще есть доступно, найдите время, чтобы проверить это.