Reg.ru: домены и хостинг

Крупнейший регистратор и хостинг-провайдер в России.

Более 2 миллионов доменных имен на обслуживании.

Продвижение, почта для домена, решения для бизнеса.

Более 700 тыс. клиентов по всему миру уже сделали свой выбор.

Перейти на сайт->

Бесплатный Курс "Практика HTML5 и CSS3"

Освойте бесплатно пошаговый видеокурс

по основам адаптивной верстки

на HTML5 и CSS3 с полного нуля.

Начать->

Фреймворк Bootstrap: быстрая адаптивная вёрстка

Пошаговый видеокурс по основам адаптивной верстки в фреймворке Bootstrap.

Научитесь верстать просто, быстро и качественно, используя мощный и практичный инструмент.

Верстайте на заказ и получайте деньги.

Получить в подарок->

Бесплатный курс "Сайт на WordPress"

Хотите освоить CMS WordPress?

Получите уроки по дизайну и верстке сайта на WordPress.

Научитесь работать с темами и нарезать макет.

Бесплатный видеокурс по рисованию дизайна сайта, его верстке и установке на CMS WordPress!

Получить в подарок->

*Наведите курсор мыши для приостановки прокрутки.


Деструктор и копирование объектов с помощью метода __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() и им подобные нередко называют магическими.

Если вы когда-нибудь читали разного рода "фэнтезийные" книжки, то точно знаете, что магия - это не всегда есть хорошо. Порой она случайна и непредсказуема, порой она нарушает правила, а иной раз несет сплошные разрушения.

Чем это светит в нашем случае?

Например, в случае метода __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;
}

На этом пока заканчиваем и продолжим тему ООП в следующем материале - Функции обратного вызова, анонимные функции и механизм замыканий.

Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!


Смотрите также:

PHP: Получение информации об объекте или классе, методах, свойствах и наследовании

PHP: Получение информации об объекте или классе, методах, свойствах и наследовании

CodeIgniter: жив или мертв?

CodeIgniter: жив или мертв?

Функции обратного вызова, анонимные функции и механизм замыканий

Функции обратного вызова, анонимные функции и механизм замыканий

Применение функции к каждому элементу массива

Применение функции к каждому элементу массива

Слияние массивов. Преобразование массива в строку

Слияние массивов. Преобразование массива в строку

Эволюция веб-разработчика или Почему фреймворк - это хорошо?

Эволюция веб-разработчика или Почему фреймворк - это хорошо?

Магические методы в PHP или методы-перехватчики (сеттеры, геттеры и др.)

Магические методы в PHP или методы-перехватчики (сеттеры, геттеры и др.)

PHP: Удаление элементов массива

PHP: Удаление элементов массива

Ключевое слово final (завершенные классы и методы в PHP)

Ключевое слово final (завершенные классы и методы в PHP)

50 классных сервисов, программ и сайтов для веб-разработчиков

50 классных сервисов, программ и сайтов для веб-разработчиков

Наверх