Деструктор и копирование объектов с помощью метода __clone()
Определение методов деструктора
Как мы уже видели, при создании экземпляра объекта автоматически вызывается метод __construct(). Однако в PHP существует и противоположный метод __destruct().
Он вызывается непосредственно перед тем, как объект отправляется на "свалку", а точнее - удаляется из памяти. Поэтому данный метод удобно использовать для выполнения завершающей очистки объекта, если это входит в ваши коварные планы)
Допустим, у нас есть класс, который при получении специальной команды сохраняет данные объекта в базе данных. Тогда метод __destruct() можно использовать для того, чтобы гарантированно сохранить данные объекта перед его удалением из памяти. Давайте добавим деструктор в класс Person:
class Person { private $name; private $age; private $id; function __construct($name, $age) { $this->name = $name; $this->age = $age; } function setId( $id ) { $this->id = $id; } function __destruct() { if (!empty( $this->id )) { // Сохраним данные объекта Person] print "сохранение объекта Person<br>"; } } }
Метод __destruct() вызывается каждый раз, когда объект Person удаляется из памяти. Это происходит при вызове функции __unset(), которой передается ссылка на удаляемый объект, а также в случае, когда в текущем процессе не остается никаких ссылок на наш объект.
Поэтому, если мы создадим объект Person, а затем решим его уничтожить, то увидим, как на самом деле работает метод __destruct()
$person = new Person("Иван",33); $person->setId(343); unset($person); // Выводится: "сохранение объекта Person"
Хотя такие приемы выглядят очень занимательно, нужно добавить нотку предостережения. Методы __call(), __destruct() и им подобные нередко называют магическими.
Если вы когда-нибудь читали разного рода "фэнтезийные" книжки, то точно знаете, что магия - это не всегда есть хорошо. Порой она случайна и непредсказуема, порой она нарушает правила, а иной раз несет сплошные разрушения.
— Регулярная проверка качества ссылок по более чем 100 показателям и ежедневный пересчет показателей качества проекта.
— Все известные форматы ссылок: арендные ссылки, вечные ссылки, публикации (упоминания, мнения, отзывы, статьи, пресс-релизы).
— SeoHammer покажет, где рост или падение, а также запросы, на которые нужно обратить внимание.
SeoHammer еще предоставляет технологию Буст, она ускоряет продвижение в десятки раз, а первые результаты появляются уже в течение первых 7 дней. Зарегистрироваться и Начать продвижение
Чем это светит в нашем случае?
Например, в случае метода __destruct() может случиться так, что программист столкнется с неприятными сюрпризами... Задумайтесь, как работает класс Person - он делает запись в базу данных при помощи своего метода __destruct().
А теперь представьте, что некий разработчик использует класс Person недостаточно "раскурив", что происходит внутри на самом деле. Он не заметил метод __destruct() и собирается создать ряд экземпляров объекта Person.
Передавая значения конструктору, он назначает тайную и обидную кличку генерального директора своей конторы свойству $name, а для свойства $age указывает немаленькую цифру - 150)
Разработчик спокойненько прогоняет тестовый сценарий несколько раз, используя безумные сочетания имени и возраста, а на следующее утро начальник вызывает его к себе в кабинет и просит пояснить, что это за странные оскорбительные данный появились в БД.
Смысл сей басни таков: не доверяйте магии!
Копирование объектов с помощью метода __clone()
Начиная с PHP 5 для целей копирования объектов предусмотрено специальное ключевое слово clone. Как нетрудно догадаться, оно применяется к экземпляру объекта и создает дополнительную копию.
class CopyMe{} $first = new CopyMe(); $second = clone $first; // Результат: переменные $first и $second ссылаются на два разных объекта
Однако не все так просто и гладко - вопросы, касающиеся копирования объектов, здесь только начинаются...
Рассмотрим класс Person, который мы создали чуть раньше.
Стандартная копия объекта Person содержит идентификатор (свойство $id), который при реализации полноценного приложения будет использоваться для нахождения нужной строки в базе данных.
Если мы разрешим копировать это свойство, то получим два различных объекта, ссылающихся на один и тот же источник данных, причем, вероятно, не тот, который мы хотели, когда создавали копию. Изменение в одном объекте повлияет на другой и наоборот.
К счастью, мы можем контролировать то, что копируется с помощью использования перед объектом ключевого слова clone. Для этого следует реализовать специальный метод __clone(). Данный метод вызывается автоматически, когда для копирования объекта используется ключевое слово clone.
При реализации метода __clone() важно понимать контекст, в котором он работает, а работает он в контексте скопированного объекта, а не исходного. Давайте теперь добавим метод __clone() к одной из версий нашего класса Person.
class Person { private $name; private $age; private $id; function __construct( $name, $age ) { $this->name = $name; $this->age = $age; } function setId( $id ) { $this->id = $id; } function __clone() { $this->id = 0; } }
Когда оператор clone вызывается для объекта типа Person, создается его новая поверхностная (shallow) копия, и для нее вызывается метод __clone(). Сей факт означает, что все изменения значений свойств, выполняемых в методе __clone(), отразятся только на новой копии объекта. Старые значения, полученные из исходного объекта, будут затерты.
— Разгрузит мастера, специалиста или компанию;
— Позволит гибко управлять расписанием и загрузкой;
— Разошлет оповещения о новых услугах или акциях;
— Позволит принять оплату на карту/кошелек/счет;
— Позволит записываться на групповые и персональные посещения;
— Поможет получить от клиента отзывы о визите к вам;
— Включает в себя сервис чаевых.
Для новых пользователей первый месяц бесплатно. Зарегистрироваться в сервисе
В данном случае мы гарантируем, что свойство $id скопированного объекта устанавливается равным нулю.
$person = new Person( "Петр", 44 ); $person->setId( 343 ); $person2 = clone $person; // $person2: // name: "Петр" // age: 44 // id: 0
Поверхностное копирование гарантирует, что значения элементарных свойств будут скопированы из старого объекта в новый. Однако свойства-объекты также будут скопированы, но по ссылке. Возможно, это не совсем то, что вы хотите, когда клонируете объект.
Предположим, мы добавим к объекту Person свойство-объект Account. В этом объекте хранятся данные о состоянии счета, которые мы тоже хотим скопировать в клонированный объект. При этом, однако, нам не нужно, чтобы в обоих объектах Person сохранялись ссылки на один и тот же счет.
class Account { public $balance; function __construct( $balance ) { $this->balance = $balance; } } class Person { private $name; private $age; private $id; public $account; function __construct( $name, $age, Account $account ) { $this->name = $name; $this->age = $age; $this->account = $account; } function setId( $id ) { $this->id = $id; } function __clone() { $this->id = 0; } } $person = new Person( "Иван", 44, new Account( 200 ) ); $person->setId( 343 ); $person2 = clone $person; // добавим $person немного денег $person->account->balance += 10; // это изменение увидит и $person2 print $person2->account->balance;
При исполнении этого кода мы получим значение 210.
Объектная переменная $person содержит ссылку на объект типа Account, который мы сделали общедоступным ради компактности (обычно же стоит ограничивать доступ к свойству, создавая в случае необходимости метод доступа).
Когда создается клон, он содержит ссылку на тот же самый объект Account, на который ссылается и $person. Мы демонстрируем это, добавляя немного денег к балансу объекта Account переменной $person, а затем распечатывая ее значение с помощью переменной $person2.
Если же мы не хотим, чтобы после клонирования в новом объекте осталась ссылка на строе свойство-объект, последнее нужно клонировать явно в методе __clone()
function __clone() { $this->id = 0; $this->account = clone $this->account; }
На этом пока заканчиваем и продолжим тему ООП в следующем материале - Функции обратного вызова, анонимные функции и механизм замыканий.
Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!
Смотрите также: