Магические методы классов в PHP. Как использовать скрытые возможности?

магические методы классов в php. как использовать скрытые возможности?

Что такое магические методы?

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

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

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

Методы __sleep() и __wakeup()

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

//Определяем простой класс "статья"
class Article
{
    public $title = 'Заголовок статьи';
    public $description = 'Описание';
}

//Осуществляем сериализацию экземпляра
$article = new Article();
$savedData = serialize($article);
echo $savedData;
/*
* Результат:
* O:7:"Article":2:{s:5:"title";s:31:"Заголовок статьи";s:11:"description";s:16:"Описание";}
*/

//Удаляем существующий объект и создаем
//из сохраненных данных
unset($article);
$savedArticle = unserialize($savedData);
echo $savedArticle->title;
/*
* Результат:
* Заголовок статьи
*/

Рассмотренные функции предоставляют широкие возможности, которых хватит для решения большинства задач. Однако вы можете управлять процессом сохранения состояния с помощью магических методов __sleep() и __wakeup().

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

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

//Определяем простой класс "Пользователь"
class User
{
    //Задаем список свойств и их значений
    public $name = 'Имя пользователя';
    public $surname = 'Фамилия пользователя';
    public $password = 'af@5fas$85!sg';

    //Метод возвращает имена и значения
    //свойств, кроме пароля
    public function __sleep()
    {
        return [
            'surname' => $this->surname,
            'name' => $this->name
        ];
    }

    //При воссоздании объекта устанавливаем
    //пароль как неопознаный
    public function __wakeup()
    {
        $this->password = 'Отсутствует...';
    }
}

//Осуществляем сериализацию экземпляра класса
$user = new User();
$savedData = serialize($user);
echo $savedData;
/*
* Результат:
* O:4:"User":2:{s:39:"Фамилия пользователя";N;s:31:"Имя пользователя";N;}
*/

//Восстанавливаем объект и получаем пароль
$savedUser = unserialize($savedData);
echo "Пароль пользователя: {$savedUser->password}";
/*
* Результат:
* Пароль пользователя: Отсутствует...
*/

Методы __sleep() и __wakeup() не принимают параметров. Они могут работать только с внутренним состоянием объекта. Поначалу сфера их применения может быть не понятна. Чтобы прояснить картину, представьте объект, содержащий в своих свойствах логин и пароль пользователя. Сохранение этих значений в результирующей строке крайне нежелательно, поэтому вы можете использовать метод __sleep() для их исключения.

Методы __get(), __set() и __call()

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

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

//Определяем простой класс "Словарь"
class Glossary
{
    //Установка нового свойства происходит,
    //если его имя начинается с буквы "g"
    public function __set($name, $value)
    {
        if ($name[0] === 'g') {
            $this->$name = $value;

        } else {
            $this->$name = 'ничего...';
        }
    }

    //Если свойство не существует, то
    //возвращаем сообщение
    public function __get($name)
    {
        return 'Запрошенное свойство не существует';
    }
}

//Проверяем работу волшебных методов
$golossary = new Glossary();
$golossary->funny = 'определение слова "забавный"';
echo "Свойство test содержит: {$golossary->funny}";
/*
* результат:
* Свойство funny содержит: ничего...
*/

$golossary->great = 'определение слова "великий"';
echo "Свойство great содержит: {$golossary->great}";
/*
* результат:
* Свойство great содержит: определение слова "великий"
*/

echo $golossary->undefinedProperty;
/*
* результат:
* Запрошенное свойство не существует
*/

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

//Определяем простой класс "Доверитель"
class Delegator
{
    //Если имя несуществующего метода равно "addition",
    //то мы складываем два переданных числа и возвращаем
    //результат
    public function __call($name, $arguments)
    {
        if ($name === 'addition') {
            return $arguments[0] + $arguments[1];

        } else {
            return $this->returnText($name);
        }
    }

    //Защищенный метод, который исполняется в случае вызова
    //несуществующего метода с именем не равным "addition"
    protected function returnText($name)
    {
        return "метод $name не определен";
    }
}

//Приводим практические примеры использования
$delegator = new Delegator();
echo "Вычисление равно: {$delegator->addition(10, 15)}";
/*
* результат:
* Вычисление равно: 25
*/

echo "Вычисление равно: {$delegator->someMethod()}";
/*
* результат:
* Вычисление равно: метод someMethod не определен
*/

Последние публикации