Деструктор и копирование объектов с помощью метода __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;
}
На этом пока заканчиваем и продолжим тему ООП в следующем материале - Функции обратного вызова, анонимные функции и механизм замыканий.
Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!
Смотрите также:
















