Магические методы PHP


Магические методы PHP

Имена функций __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() и __debugInfo() являются магическими в PHP. Не стоит называть свои методы этими именами, если вы не хотите использовать их магическую функциональность.

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

__sleep() и __wakeup()

Функция serialize() проверяет, присутствует ли в вашем классе метод с магическим именем __sleep(). Если это так, то этот метод выполняется до любой операции сериализации. Он может очистить объект и должен возвращать массив с именами всех переменных этого объекта, которые должны быть сериализованы. Если метод ничего не возвращает, то сериализуется NULL и выдается предупреждение E_NOTICE .

Недопустимо возвращать в __sleep() имена закрытых свойств в родительском классе. Это приведет к ошибке уровня E_NOTICE . Вместо этого вы можете использовать интерфейс Serializable.

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

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

Предполагаемое использование __wakeup() заключается в восстановлении любых соединений с базой данных, которые могли быть потеряны во время операции сериализации и выполнения других операций повторной инициализации.

Пример #1 Сериализация и десериализация

class Connection
<
protected $link ;
private $dsn , $username , $password ;

public function __construct ( $dsn , $username , $password )
<
$this -> dsn = $dsn ;
$this -> username = $username ;
$this -> password = $password ;
$this -> connect ();
>

private function connect ()
<
$this -> link = new PDO ( $this -> dsn , $this -> username , $this -> password );
>

public function __sleep ()
<
return array( ‘dsn’ , ‘username’ , ‘password’ );
>

public function __wakeup ()
<
$this -> connect ();
>
> ?>

__toString()

Метод __toString() позволяет классу решать, как он должен реагировать при преобразовании в строку. Например, что вывести при выполнении echo $obj;. Этот метод должен возвращать строку, иначе произойдёт фатальная ошибка уровня E_RECOVERABLE_ERROR .

Нельзя выбросить исключение из метода __toString(). Это приведет к фатальной ошибке.

Пример #2 Простой пример

// Объявление простого класса
class TestClass
<
public $foo ;

public function __construct ( $foo )
<
$this -> foo = $foo ;
>

public function __toString ()
<
return $this -> foo ;
>
>

$class = new TestClass ( ‘Привет’ );
echo $class ;
?>

Результат выполнения данного примера:

Стоит отметить, что до PHP 5.2.0 метод __toString() вызывался только непосредственно в сочетании с echo или print . Начиная с PHP 5.2.0, он вызывается в любом строчном контексте (например, в printf() с модификатором %s), но не в контексте других типов (например, с %d модификатором). Начиная с PHP 5.2.0, преобразование объекта в строку при отсутствии метода __toString() вызывает ошибку E_RECOVERABLE_ERROR .

__invoke()

Метод __invoke() вызывается, когда скрипт пытается выполнить объект как функцию.

Данный метод доступен начиная с PHP 5.3.0.

Пример #3 Использование __invoke()

Наилучшая практика: магические методы PHP __set и __get

Возможный дубликат:
Являются ли методы магии лучшей практикой в ​​PHP?

Это простые примеры, но представьте, что у вас больше свойств, чем у двух в вашем классе.

Что было бы лучшей практикой?

a) Использование __get и __set

б) Использование традиционных сеттеров и геттеров

В этой статье: https://blog.webspecies.co.uk/2011-05-23/the-new-era-of-php-frameworks.html

Автор утверждает, что использование магических методов не является хорошей идеей:

Прежде всего, тогда было очень популярно использовать магические функции PHP (__get, __call и т. Д.). С ними нет ничего плохого в первом взгляде, но они действительно опасны. Они делают API неясным, автоматическое завершение невозможным и, самое главное, они медленны. Для них был взлом PHP, чтобы делать то, чего он не хотел. И это сработало. Но произошли плохие вещи.

Но я хотел бы услышать больше об этом.

Я был в вашем случае в прошлом. И я пошел на магические методы.

Это была ошибка, в последней части вашего вопроса говорится все:

  • это медленнее (чем геттеры / сеттеры)
  • автоматическое завершение (и это на самом деле серьезная проблема) и управление типом IDE для рефакторинга и просмотра кода (в Zend Studio / PhpStorm это можно обрабатывать с помощью аннотации @property phpdoc, но это требует их поддержки : довольно боль)
  • документация (phpdoc) не соответствует тому, как предполагается использовать ваш код, и просмотр вашего класса также не приносит много ответов. Это смущает.
  • добавлено после редактирования: наличие getters для свойств более согласовано с «реальными» методами, где getXXX() не только возвращает частную собственность, но и выполняет реальную логику. У вас одинаковое название. Например, у вас есть $user->getName() (возвращает частное свойство) и $user->getToken($key) (вычисляется). В тот день, когда ваш получатель получает больше, чем получатель, и ему нужно сделать какую-то логику, все по-прежнему непротиворечиво.

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

Геттеры и сеттеры – это боль, чтобы писать (я их ненавижу), но они того стоят.

Вам нужно всего лишь использовать магию, если объект действительно «волшебный». Если у вас есть классический объект с фиксированными свойствами, то используйте сеттеры и геттеры, они работают нормально.

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

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

этот код недвусмысленно говорит, что я делаю:

этот код заставляет меня чувствовать себя глупо, что мне не нравится:

Разница между этими двумя особенно очевидна при одновременном доступе к нескольким свойствам.

Должен ли «$ a-> b» действительно что- то делать или просто вернуть значение, является обязанностью вызываемого. Для вызывающего абонента «$ user-> name» и «$ user-> accountBalance» должны выглядеть одинаково, хотя последние могут включать сложные вычисления. В моих классах данных я использую следующий небольшой метод:

когда кто-то называет «$ obj-> xxx», и класс имеет «get_xxx», этот метод будет неявно вызываться. Таким образом, вы можете определить геттер, если он вам нужен, при этом ваш интерфейс будет однородным и прозрачным. В качестве дополнительного бонуса это обеспечивает элегантный способ запоминания расчетов:

Итог: php – это динамический язык сценариев, используйте его таким образом, не делайте вид, что вы делаете Java или C #.

Я делаю сочетание ответа edem и вашего второго кода. Таким образом, у меня есть преимущества общих getter / seters (завершение кода в вашей среде IDE), простота кодирования, если я хочу, исключения из-за несуществующих свойств (отлично подходит для обнаружения опечаток: $foo->naem вместо $foo->name ), только для чтения свойств и составных свойств.

Я голосую за третье решение. Я использую это в своих проектах, и Symfony тоже использует что-то вроде этого:

Таким образом, у вас есть автоматические getters (вы также можете писать сеттеры), и вам нужно писать только новые методы, если есть специальный случай для переменной-члена.

Вы должны использовать stdClass, если вам нужны магические члены, если вы пишете класс – определите, что он содержит.

Наилучшей практикой было бы использование традиционных геттеров и сеттеров из-за интроспекции или размышления. В PHP есть способ (точно так же, как в Java), чтобы получить имя метода или всех методов. Такая вещь вернет «__get» в первом случае и «getFirstField», «getSecondField» во втором (плюс сеттеры).

Я возвращаюсь к сеттерам и геттерам, но я также помещаю геттеры и сеттеры в магические методы и __set. Таким образом, у меня есть поведение по умолчанию, когда я делаю это

Это просто вызовет getter, который я установил в __get. Обычно я просто использую getter напрямую, но есть еще некоторые примеры, когда это просто проще.

Второй пример кода – гораздо более правильный способ сделать это, потому что вы полностью контролируете данные, которые предоставляются class . Есть случаи, когда __set и __get полезны, но не в этом случае.

Магические методы PHP


Имена методов __construct, __destruct (см. Конструкторы и деструкторы), __call, __callStatic, __get, __set, __isset, __unset (см. Перегрузка), __sleep, __wakeup, __toString, __set_state и __clone зарезервированы для «магических» методов в PHP. Не стоит называть свои методы этими именами, если вы не хотите использовать их «магическую» функциональность.

PHP оставляет за собой право все методы, начинающиеся с __, считать «магическими». Не рекомендуется использовать имена методов с __ в PHP, если вы не желаете использовать соответствующий «магический» функционал.

__sleep и __wakeup

Функция serialize() проверяет, присутствует ли в вашем классе метод с «магическим» именем __sleep. Если это так, то этот метод выполняется прежде любой операции сериализации. Он может очистить объект и предполагается, что будет возвращен массив с именами всех переменных объекта, который должен быть сериализован. Если метод ничего не возвращает кроме NULL, то это значит, что объект сериализован и выдается предупреждение E_NOTICE.

Обычно __sleep используется для передачи ожидаемых данных или для выполнения обычных задач их очистки. Также, этот метод можно выполнять в тех случаях, когда вы не хотите сохранять очень большие объекты полностью.

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

Обычно __wakeup используется для восстановления любых соединений с базой данных, которые могли быть потеряны во время операции сериализации и выполнения других операций повторной инициализации.

Пример #1 Sleep и wakeup

class Connection <
protected $link ;
private $server , $username , $password , $db ;

public function __construct ( $server , $username , $password , $db )
<
$this -> server = $server ;
$this -> username = $username ;
$this -> password = $password ;
$this -> db = $db ;
$this -> connect ();
>

private function connect ()
<
$this -> link = mysql_connect ( $this -> server , $this -> username , $this -> password );
mysql_select_db ( $this -> db , $this -> link );
>

public function __sleep ()
<
return array( ‘server’ , ‘username’ , ‘password’ , ‘db’ );
>

public function __wakeup ()
<
$this -> connect ();
>
>
?>

__toString

Метод __toString позволяет классу решать самостоятельно, как он должен реагировать при преобразовании в строку.

Пример #2 Простой пример

// Декларирование простого класса
class TestClass
<
public $foo ;

public function __construct ( $foo ) <
$this -> foo = $foo ;
>

public function __toString () <
return $this -> foo ;
>
>

$class = new TestClass ( ‘Привет’ );
echo $class ;
?>

Результат выполнения данного примера:

Ранее, до PHP 5.2.0, метод __toString вызывался только непосредственно в сочетании с функциями echo() или print() . Начиная с PHP 5.2.0, он вызывается в любом строчном контексте (например, в printf() с модификатором %s), но не в контекстах других типов (например, с %d модификатором). Начиная с PHP 5.2.0, преобразование объекта в строку при отсутствии метода __toString вызывает ошибку E_RECOVERABLE_ERROR.

__invoke

The __invoke method is called when a script tries to call an object as a function.

Урок 16. Курс по ООП PHP. Магические методы

Дата публикации: 25-05-2020

От автора: продолжаем изучение объектно-ориентированного программирования (ООП) в PHP. Как вы уже знаете, PHP предлагает ряд магических методов, с двумя из которых вы уже знакомы (__construct и __destruct). Данные методы вызываются при наступлении некоторых событий и без нашего участия. В этом уроке мы познакомимся с еще несколькими магическими методами, в частности с методами __set, __get и __toString.

Все уроки курса:

Комментарии (2)

Благодарю за качественные уроки! Информативно, лаконично, понятно. Очень жду последний урок.

Добавить комментарий Отменить ответ

Для отправки комментария вы должны авторизоваться.

Количество уроков: 17

Продолжительность курса: 05:49:22

Автор: Андрей Кудлай

Меня зовут Андрей Кудлай — один из авторов проекта webformyself и практикующий веб-разработчик. Имею хорошие навыки работы с HTML, CSS, jQuery, PHP, MySQL, Bootstrap, CakePHP, Yii2, WordPress, OpenCart. Являюсь автором нескольких курсов-бестселлеров издательства WebForMySelf.com

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

Все права защищены © 2020
ИП Рог Виктор Михайлович
ОГРН: 313774621200541
Служба поддержки

Магические методы PHP

$obj = new GetSet;
// Заносим во внутренние переменные класса значения хитов
$obj->path = 100;
echo $obj->path;

$test = new Test;
$test->property = 1; // значение будет успешно присвоено
$test->property = new Test; // будет ошибка

Это для простой реализации инкапсуляции, когда аксессоры ни к чему

Не совсем. Это лишь пример. По некоторым концепциям, валидацией занимаются объекты валидации, а сущности данных лишь хранят в себе данные и состояние объекта.

Нужно в ООП вникнуть, чтобы понять, зачем это нужно

16 Magic Methods That PHP Developers Must Know

The methods which begin with two underscores (__) are called Magic Methods in PHP, and they play an important role. Magic methods include:

Method Name Description
__construct() the constructor of a class
__destruct() the destructor of a class
__call($funName, $arguments) The __call() method will be called when an undefined or inaccessible method is called.
__callStatic($funName, $arguments) The __callStatic() method will be called when an undefined or inaccessible static method is called.
__get($propertyName) The __get() method will be called when getting a member variable of a class.
__set($property, $value) The __set() method will be called when setting a member variable of a class.
__isset($content) The __isset() method will be called when calling isset() or empty() for an undefined or inaccessible member.
__unset($content) The __unset() method will be called when calling reset() for an undefined or inaccessible member.
__sleep() The __sleep() method will be called first when executing serialize().
__wakeup() The __wakeup() method will be called first when deserialization() is executed.
__toString() The __toString() method will be called when using echo method to print an object directly.
__invoke() The __invoke() method will be called when trying to call an object in a way of calling function.
__set_state($an_array) The __set_state() method will be called when calling var_export().
__clone() The __clone() method will be called when the object is copied.
__autoload($className) Try to load an undefined class.
__debugInfo() Print debug information.

In this article, we will show you how to use these PHP magic methods with some examples.

1.__construct()

The PHP constructor is the first method that is automatically called after the object is created. Each class has a constructor. If you do not explicitly declare it, then there will be a default constructor with no parameters and empty content in the class.

1) the use of the method

Constructors are usually used to perform some initialization tasks, such as setting initial values ​​for member variables when creating objects.

2) the declaration format of the constructor in a class

Note: Only one constructor can be declared in the same class, because PHP does not support constructor overloading.

Here is a complete example:

Create object $Person1 without any parameters.

Create object $Person2 with parameter «Jams».

Create object $Person3 with three parameters.

2. __destruct()

Now that we have already known what the constructor is, the destructor is the opposite.

Destructor allows you to perform some operations before destroying an object, such as closing a file, emptying a result set, and so on.

Destructor is a new feature introduced by PHP5.


The declaration format of the destructor is similar to that of the constructor __construct(), which means that __destruct() is also started with two underscores, and the name of the destructor is also fixed.

1) the declaration format of destructor

Note: Destructor cannot have any parameters.

2) the use of the destructor

In general, the destructor is not very common in PHP. It’s an optional part of a class, usually used to complete some cleanup tasks before the object is destroyed.

Here is an example of using the destructor:

The output of the program above is:

3. __call()

This method takes two parameters. The first parameter $function_name will automatically receive the undefined method name, while the second $arguments will receive multiple arguments of the method as an array.

1)The use of __call() method

When an undefined method is called in a program, the __call() method will be called automatically.

Here is an example:

The results are as follows:

4. __callStatic()

When an undefined static method is called in a program, the __callStatic() method will be called automatically.

The use of __callStatic() is similar to the __call(). Here is an example:

The results are as follows:

5. __get()

When you try to access a private property of an external object in a program, the program will throw an exception and end execution. We can use the magic method __get() to solve this problem. It can get the value of the private property of the object outside the object. Here is an example:

The results are as follows:

6. __set()

The __set( $property, $value) method is used to set the private property of the object. When an undefined property is assigned, the __set() method will be triggered and the passed parameters are the property name and value that are set.

Here is the demo code:

Here is the result:

7. __isset()

Before using the __isset () method, let me explain the use of the isset () method first. The isset () method is mainly used to determine whether the variable is set.

If you use the isset() method outside the object, there are two cases:

  1. If the parameter is a public property, you can use the isset() method to determine whether the property is set or not.
  2. If the parameter is a private property , the isset() method will not work.

So for the private property, is there any way to know if it is set? Of course, as long as we define a __isset() method in a class, we can use the isset() method outside the class to determine whether the private property is set or not.

When the isset() or empty() is called on an undefined or inaccessible property, the __isset() method will be called. Here is an example:

Мастер Йода рекомендует:  Несколько слов о том, как работают роботы поисковых машин

The results are as follows:

8. __unset()

Similar to the __isset() method, the __unset() method is called when the unset() method is called on an undefined or inaccessible property. Here is an example:

The results are as follows:

9. __sleep()

The serialize() method will check if there is a magic method __sleep() in the class. If it exists, the method will be called first and then perform the serialize operation.

The __sleep() method is often used to specify the properties that need to be serialized before saving data. If there are some very large objects that don’t need to be saved all, then you will find this feature is very useful.

For details, please refer to the following code:

The results are as follows:

10. __wakeup()

In contrast to the __sleep() method, the __wakeup() method is often used in deserialize operations, such as re-building a database connection, or performing other initialization operations.

Here is an example:

The results are as follows:

11. __toString()

The __toString() method will be called when using echo method to print an object directly.

Note: This method must return a string, otherwise it will throw a fatal error at `E_RECOVERABLE_ERROR` level. And you also can’t throw an exception in the __toString() method.

Here is an example:

The results are as follows:

So what if the __toString() method is not defined in the class? Let’s have a try.

The results are as follows:

Obviously, It reports a fatal error on the page, which is not allowed by PHP syntax.

12. __invoke()

When you try to call an object in the way of calling a function, the __ invoke() method will be called automatically.

Note: This feature is only valid in PHP 5.3.0 and above.

Here is an example:

Here is the result:

If you insist on using objects as methods(but not defining the __invoke() method),then you will get the following result:

13.__set_state()

Starting from PHP 5.1.0, the __set_state() method is called automatically when calling var_export() to export the class code.

The parameters of the __set_state() method is an array containing the values of all the properties, with the format of array(‘property’ => value. )

We don’t define __set_state() method in the following example:


Here is the result:

Obviously, the properties of the object are printed.

Now let’s have a look at the other case of being defined the __set_state() method:

Here is the result:

14. __clone()

In PHP we can use the clone keyword to copy objects with the following syntax:

However, using clone keyword is just a shallow copy, because all referenced properties will still point to the original variable.

If a __clone() method is defined in a object, then the __clone() method will be called in the copy-generated object and can be used to modify the value of the property (if necessary).

Here is an example:

The results are as follows:

15.__autoload()

The __autoload() method can try to load an undefined class.

In the past, if you were to create 100 objects in a program file, then you had to use include() or require() to contain 100 class files, or you had to define the 100 classes in the same class file, for example:

So what if we use the __autoload() method?

When the PHP engine uses class A at the first time, if class A is not found, then the __autoload method will be called automatically and the class name «A» will be passed as a parameter. So what we need to do in the __autoload() method is to find the corresponding class file based on the class name and then include the file. If the file is not found, then the php engine will throw an exception.

16. __debugInfo()

The __debugInfo() method will be called when the var_dump() method is executed. If the __debugInfo() method is not defined, then the var_dump() method will print out all the properties in the object.

Here is an example:

Here is the result:

Note: The __debugInfo() method should be applied to the PHP 5.6.0 and above.

Summarize

The above are the PHP magic methods that I understand, of which commonly used include __set(), __get() and __autoload(). If you still have any questions, you can get more help from the official PHP website.

Магические методы PHP

Имена методов __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state() and __clone() зарезервированы для «магических» методов в PHP. Не стоит называть свои методы этими именами, если вы не хотите использовать их «магическую» функциональность.

PHP оставляет за собой право все методы, начинающиеся с __, считать «магическими». Не рекомендуется использовать имена методов с __ в PHP, если вы не желаете использовать соответствующий «магический» функционал.

__sleep() и __wakeup()

Функция serialize() проверяет, присутствует ли в вашем классе метод с «магическим» именем __sleep(). Если это так, то этот метод выполняется прежде любой операции сериализации. Он может очистить объект и предполагается, что будет возвращен массив с именами всех переменных объекта, который должен быть сериализован. Если метод ничего не возвращает кроме NULL , то это значит, что объект сериализован и выдается предупреждение E_NOTICE .

Недопустимо возвращать в __sleep() имена приватных свойств объекта в родительский класс. Это приведет к предупреждению E_NOTICE . Вместо этого вы можете использовать интерфейс Serializable.

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

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

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

Пример #1 Sleep и wakeup

class Connection
<
protected $link ;
private $server , $username , $password , $db ;

public function __construct ( $server , $username , $password , $db )
<
$this -> server = $server ;
$this -> username = $username ;
$this -> password = $password ;
$this -> db = $db ;
$this -> connect ();
>

private function connect ()
<
$this -> link = mysql_connect ( $this -> server , $this -> username , $this -> password );
mysql_select_db ( $this -> db , $this -> link );
>

public function __sleep ()
<
return array( ‘server’ , ‘username’ , ‘password’ , ‘db’ );
>

public function __wakeup ()
<
$this -> connect ();
>
>
?>

__toString()

Метод __toString() позволяет классу решать самостоятельно, как он должен реагировать при преобразовании в строку. Например, что напечатает echo $obj;. Этот метод должен возвращать строку, иначе выдастся неисправимая ошибка E_RECOVERABLE_ERROR .

Нельзя бросить исключение из метода __toString(). Попытка это сделать закончится фатальной ошибкой.

Пример #2 Простой пример

// Объявление простого класса
class TestClass
<
public $foo ;

public function __construct ( $foo )
<
$this -> foo = $foo ;
>

public function __toString ()
<
return $this -> foo ;
>
>

$class = new TestClass ( ‘Привет’ );
echo $class ;
?>

Результат выполнения данного примера:

Ранее, до PHP 5.2.0, метод __toString() вызывался только непосредственно в сочетании с функциями echo или print . Начиная с PHP 5.2.0, он вызывается в любом строчном контексте (например, в printf() с модификатором %s), но не в контекстах других типов (например, с %d модификатором). Начиная с PHP 5.2.0, преобразование объекта в строку при отсутствии метода __toString() вызывает ошибку E_RECOVERABLE_ERROR .

__invoke()

Метод __invoke() вызывается, когда скрипт пытается выполнить объект как функцию.

Данный метод доступен начиная с PHP 5.3.0.

Пример #3 Использование __invoke()

Результат выполнения данного примера:

__set_state()

Этот статический метод вызывается для тех классов, которые экспортируются функцией var_export() начиная с PHP 5.1.0.

Параметр этого метода должен содержать массив, состоящий из экспортируемых свойств в виде array(‘property’ => value, . ).

Пример #4 Использование __set_state() (начиная с PHP 5.1.0)

class A
<
public $var1 ;
public $var2 ;

public static function __set_state ( $an_array ) // С PHP 5.1.0
<
$obj = new A ;
$obj -> var1 = $an_array [ ‘var1’ ];
$obj -> var2 = $an_array [ ‘var2’ ];
return $obj ;
>
>

$a = new A ;
$a -> var1 = 5 ;
$a -> var2 = ‘foo’ ;

eval( ‘$b = ‘ . var_export ( $a , true ) . ‘;’ ); // $b = A::__set_state(array(
// ‘var1’ => 5,
// ‘var2’ => ‘foo’,
// ));
var_dump ( $b );

Результат выполнения данного примера:

Коментарии


If you are attempting to write an abstract/base class which automates the __sleep process in PHP5 you will run into some trouble if the subclasses which are being serialized have private/protected variables you need to be serialized.

The reason is, even though get_class($this) within the base class will return the subclass — get_class_vars(get_class($this)) will *not* return the subclass’ protected/private variables. Which makes sense — using OO principles.

However, when automating __sleep it becomes necissary to have access to the private/protected subclass variables because their names have to be returned by __sleep.

So here is the work around:
public function __sleep ()
<
. code .
$sleepVars = array_keys ((array) $this );
return $sleepVars ;
>
?>

Even though array_keys includes more information about the variable names than just the variable names — it still seems to work appropriately.

One of the principles of OOP is encapsulation—the idea that an object should handle its own data and no others’. Asking base classes to take care of subclasses’ data, esp considering that a class can’t possibly know how many dozens of ways it will be extended, is irresponsible and dangerous.

Consider the following.

class SomeStupidStorageClass
<
public function getContents ( $pos , $len ) < . stuff . >
>

class CryptedStorageClass extends SomeStupidStorageClass
<
private $decrypted_block ;
public function getContents ( $pos , $len ) < . decrypt . >
>
?>

If SomeStupidStorageClass decided to serialize its subclasses’ data as well as its own, a portion of what was once an encrypted thingie could be stored, in the clear, wherever the thingie was stored. Obviously, CryptedStorageClass would never have chosen this. but it had to either know how to serialize its parent class’s data without calling parent::_sleep(), or let the base class do what it wanted to.

Considering encapsulation again, no class should have to know how the parent handles its own private data. And it certainly shouldn’t have to worry that users will find a way to break access controls in the name of convenience.

If a class wants both to have private/protected data and to survive serialization, it should have its own __sleep() method which asks the parent to report its own fields and then adds to the list if applicable. Like so.

class BetterClass
<
private $content ;

public function __sleep ()
<
return array( ‘basedata1’ , ‘basedata2’ );
>

public function getContents () < . stuff . >
>

class BetterDerivedClass extends BetterClass
<
private $decrypted_block ;

public function __sleep ()
<
return parent :: __sleep ();
>

public function getContents () < . decrypt . >
>

?>

The derived class has better control over its data, and we don’t have to worry about something being stored that shouldn’t be.

In reply to krisj1010 at gmail.com below:

__sleep() handles protected/private properties very well. You should never rely on get_class_vars() to retrieve property names since this function only returns the public properties. Use the Reflection API instead for that purpose. Better yet, if you know which ones you want to save it is always faster to specify the return array manually.

When you use sessions, its very important to keep the sessiondata small, due to low performance with unserialize. Every class shoud extend from this class. The result will be, that no null Values are written to the sessiondata. It will increase performance.

class BaseObject
<
function __sleep ()
<
$vars = (array) $this ;
foreach ( $vars as $key => $val )
<
if ( is_null ( $val ))
<
unset( $vars [ $key ]);
>
>
return array_keys ( $vars );
>
>;
?>

about __sleep and _wakeup, consider using a method like this:

var $sub_core; //ref of subcore
var $_sleep_subcore; // place where serialize version of sub_core will be stored

function core() <
$this->sub_core = new sub_core();
return true;
>

function __wakeup()
<
// on wakeup of core, core unserializes sub_core
// wich it had stored when it was serialized itself
$this->sub_core = unserialize($this->_sleep_subcore);
return true;
>

function __sleep()
<
// sub_core will be serialized when core is serialized.
// the serialized subcore will be stored as a string inside core.
$this->_sleep_subcore = serialize($this->sub_core);
$return_arr[] = «_sleep_subcore»;
return $return_arr;
>

class sub_core
<
var $info;

function sub_core()
<
$this->info[«somedata»] = «somedata overhere»
>

function __wakeup()
<
return true;
>

function __sleep()
<
$return_arr[] = «info»
return $return_arr;
>

this way subcore is being serialized by core when core is being serialized. subcore handles its own data and core stores it as a serialize string inside itself. on wakeup core unserializes subcore.

this may have a performance cost, but if you have many objects connected this way this is the best way of serializing them. you only need to serialize the the main object wich will serialize all those below which will serialize all those below them again. in effect causing a sort of chainreaction in wich each object takes care of its own info.

offcoarse you always need to store the eventualy serialized string in a safe place. somebody got experience with this way of __wakeup and __sleep.

The sequence of events regarding __sleep and __destruct is unusual __ as __destruct is called before __sleep. The following code snippet:

= 0 ;
class foo <
public $stuff ;
public function __construct ( $param ) <
global $sequence ;
echo «Seq: » , $sequence ++, » — constructor\n» ;
$this -> stuff = $param ;
>
public function __destruct () <
global $sequence ;
echo «Seq: » , $sequence ++, » — destructor\n» ;
>
public function __sleep () <
global $sequence ;
echo «Seq: » , $sequence ++, » — __sleep\n» ;
return array( «stuff» );
>
public function __wakeup () <
global $sequence ;
echo «Seq: » , $sequence ++, » — __wakeup\n» ;
>
>
session_start ();
$_SESSION [ «obj» ] = new foo ( «A foo» );
?>

yields the output:

Seq: 0 — constructor
Seq: 1 — destructor
Seq: 2 — __sleep

Only when you end your script with a call to session_write_close() as in:

= 0 ;
class foo <
public $stuff ;
public function __construct ( $param ) <
global $sequence ;
echo «Seq: » , $sequence ++, » — constructor\n» ;
$this -> stuff = $param ;
>
public function __destruct () <
global $sequence ;
echo «Seq: » , $sequence ++, » — destructor\n» ;
>
public function __sleep () <
global $sequence ;
echo «Seq: » , $sequence ++, » — __sleep\n» ;
return array( «stuff» );
>
public function __wakeup () <
global $sequence ;
echo «Seq: » , $sequence ++, » — __wakeup\n» ;
>
>
session_start ();
$_SESSION [ «obj» ] = new foo ( «A foo» );
session_write_close ();
?>

the sequence is as common sense would expect it to be as the following output shows:

Seq: 0 — constructor
Seq: 1 — __sleep
Seq: 2 — destructor

To copy base part of derived class appropriate method in base should be defined. E.g.:

class A <
public function setAVar( $oAVar) < $this->oAVar = $oAVar; >
public function getAVar() < return $this->oAVar; >

class B extends A <
public function setBVar( $oBVar) < $this->oBVar = $oBVar; >
public function getBVar() < return $this->oBVar; >

$oA = new A();
$oB = new B();

$oB->setAVar( 5);
$oB->setBVar( 6);
echo «oA::oAVar » . $oA->getAVar() . «
«;
echo «oB::oAVar » . $oB->getAVar() . «
«;
echo «oB::oBVar » . $oB->getBVar() . «
«;
echo «
«;

echo «oA::oAVar » . $oA->getAVar() . «
«;
echo «oB::oAVar » . $oB->getAVar() . «
«;
echo «oB::oBVar » . $oB->getBVar() . «
«;

oA::oAVar 4
oB::oAVar 5
oB::oBVar 6

oA::oAVar 4
oB::oAVar 4
oB::oBVar 6

If you use the Magical Method ‘__set()’, be shure that the call of
-> test [ ‘myarray’ ] = ‘data’ ;
?>
will not appear!

For that u have to do it the fine way if you want to use __set Method 😉
-> test = array( ‘myarray’ => ‘data’ );
?>

If a Variable is already set, the __set Magic Method already wont appear!

My first solution was to use a Caller Class.
With that, i ever knew which Module i currently use!
But who needs it. :]
There are quiet better solutions for this.
Here’s the Code:

class Caller <
public $caller ;
public $module ;

function __call ( $funcname , $args = array()) <
$this -> setModuleInformation ();

$this -> unsetModuleInformation ();
return $return ;
>

function __construct ( $callerClassName = false , $callerModuleName = ‘Webboard’ ) <
if ( $callerClassName == false )
trigger_error ( ‘No Classname’ , E_USER_ERROR );

$this -> module = $callerModuleName ;

if ( class_exists ( $callerClassName ))
$this -> caller = new $callerClassName ();
else
trigger_error ( ‘Class not exists: \» . $callerClassName . ‘\» , E_USER_ERROR );

if ( is_object ( $this -> caller ))
<
$this -> setModuleInformation ();
if ( method_exists ( $this -> caller , ‘__init’ ))
$this -> caller -> __init ();
$this -> unsetModuleInformation ();
>
else
trigger_error ( ‘Caller is no object!’ , E_USER_ERROR );
>

function __destruct () <
$this -> setModuleInformation ();
if ( method_exists ( $this -> caller , ‘__deinit’ ))
$this -> caller -> __deinit ();
$this -> unsetModuleInformation ();
>

function __isset ( $isset ) <
$this -> setModuleInformation ();
if ( is_object ( $this -> caller ))
$return = isset( $this -> caller ->< $isset >);
else
trigger_error ( ‘Caller is no object!’ , E_USER_ERROR );
$this -> unsetModuleInformation ();
return $return ;
>

function __unset ( $unset ) <
$this -> setModuleInformation ();
if ( is_object ( $this -> caller )) <
if (isset( $this -> caller ->< $unset >))
unset( $this -> caller ->< $unset >);
>
else
trigger_error ( ‘Caller is no object!’ , E_USER_ERROR );
$this -> unsetModuleInformation ();
>

function __set ( $set , $val ) <
$this -> setModuleInformation ();
if ( is_object ( $this -> caller ))
$this -> caller -> < $set >= $val ;
else
trigger_error ( ‘Caller is no object!’ , E_USER_ERROR );
$this -> unsetModuleInformation ();
>

function __get ( $get ) <
$this -> setModuleInformation ();
if ( is_object ( $this -> caller )) <
if (isset( $this -> caller ->< $get >))
$return = $this -> caller ->< $get >;
else
$return = false ;
>
else
trigger_error ( ‘Caller is no object!’ , E_USER_ERROR );
$this -> unsetModuleInformation ();
return $return ;
>

function setModuleInformation () <
$this -> caller -> module = $this -> module ;
>


function unsetModuleInformation () <
$this -> caller -> module = NULL ;
>
>

// Well this can be a Config Class?
class Config <
public $module ;

function __construct ()
<
print( ‘Constructor will have no Module Information. Use __init() instead!
‘ );
print( ‘ ‘ . print_r ( $this -> module , 1 ). ‘ );
print( ‘
‘ );
print( ‘
‘ );
$this -> test = ‘123’ ;
>

function __init ()
<
print( ‘Using of __init()!
‘ );
print( ‘ ‘ . print_r ( $this -> module , 1 ). ‘ );
print( ‘
‘ );
print( ‘
‘ );
>

function testFunction ( $test = false )
<
if ( $test != false )
$this -> test = $test ;
>
>

echo( » );
?>

Outputs something Like:

Constructor will have no Module Information. Use __init() instead!
Guestbook

I’ve just come accross something interesting relating to storing PHP5 objects in a session. If you don’t provide an __autoload(), then you MUST load the class definition before calling session_start(). I guess that when you call session_start(), any objects in the session are unserialized then and there and placed into $_SESSION. If you don’t provide the class definition before calling session_start(), your object will get the class __PHP_Incomplete_Class, and you won’t be able to use it for anything.

Мастер Йода рекомендует:  Монтаж снимка с помощью маски слоя

();
require_once ‘MyClass.php’ ;
$obj = new MyClass ;
$_SESSION [ ‘obj’ ] = $obj ;
?>

Works fine. Then on a subsequent page load:

();
require_once ‘MyClass.php’ ;
$_SESSION [ ‘obj’ ]-> callSomeMethod ();
?>

Fatal error: The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition «MyClass» of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide a __autoload() function to load the class definition.

But if you do this instead, it works fine:

require_once ‘MyClass.php’ ;
session_start ();
$_SESSION [ ‘obj’ ]-> callSomeMethod ();
?>

Hopefully in some future release of PHP, __PHP_Incomplete_Class will be smart enough to check for a class definition at time of use (method call or property operation), and, if the class exists, magically «complete» itself and turn into the desired object.

When overriding __get and __set, the above code can work (as expected) but it depends on your __get implementation rather than your __set. In fact, __set is never called with the above code. It appears that PHP (at least as of 5.1) uses a reference to whatever was returned by __get. To be more verbose, the above code is essentially identical to:

$tmp_array = &$myclass->foo;
$tmp_array[‘bar’] = ‘baz’;
unset($tmp_array);

Therefore, the above won’t do anything if your __get implementation resembles this:

function __get($name) <
return array_key_exists($name, $this->values)
? $this->values[$name] : null;
>

You will actually need to set the value in __get and return that, as in the following code:

function __get($name) <
if (!array_key_exists($name, $this->values))
$this->values[$name] = null;
return $this->values[$name];
>

Since PHP 5.2.0, you’ll always get an error like this:
«Object of class foo could not be converted to string»

When one tries to use an object as string, for instance:

class Test<>
echo new Test();

Thus, one way to avoid this problem is to programme the magic method __toString.

However, in the older versions, it would output a string saying that it was an object together a unique obj id. Therefore, the __toString() method must comply with this behaviour.

class Test <
function __toString() <
if(!isset($this->__uniqid))
$this->__uniq > return(get_class($this).»@».$this->__uniqid);
>

would output something like this:

Maybe not really new and all in all definitely not the best solution,but if you cant extend a class (if your class alreay extends an abstract or other things like that) you can ‘fake’ a extend.

class MyClass
extends SomeAbstractUnknownClass <

public function __construct ( classObject $classToExtend ) <
$this -> classObject = $classToExtend ;
>

public function __call ( $func , $var ) <
if ( ! count ( $var ) ) <
return $this -> classObject -> $func ( $var );
> else <
$str = » ;
$values = array_values ( $var );
for ( $i = 0 ; $i count ( $values ); $i ++ ) <
$str .= «‘» . $values [ $i ]. «‘ ,» ;
>
$str = substr ( $str , 0 , — 2 );
return eval( ‘return $this->classObject->’ . $func . ‘(‘ . $str . ‘);’ );
>
>
>
?>

So if you’ll do a $myClass->unknownMethod() and it is found neither in MyClass nor in SomeAbstractUnknownClass, MyClass will try to call this method in $classObject.

I use this for ‘extending’ a UserObject-Class which already extends an other one.

Better solutions are always welcome 😉

There is no need to use eval() to mimic mixins (i.e., multiple inheritance) within PHP 5. You only need to:

class MyClass
<
private $_obj = null ;
public function __construct ( $obj )
<
$this -> _obj = $obj ;
>

public function __call ( $method , $args )
<
if (! method_exists ( $this -> _obj , $method )) <
throw new Exception ( «unknown method [$method]» );
>

return call_user_func_array (
array( $this -> _obj , $method ),
$args
);
>
>

?>

You could just as easily add an addMixin() method that would allow you to add multiple objects to an array, and then iterate over that array until you found the right method. As noted, these are referred to as a Mixins in other languages.

Referering my previus note: there was an error in the code. But i find a better way:

class Classes <
private $name ;
private $statics ;

function __construct ( $name ) <
$this -> name = $name ;
$this -> statics =array();
>

function setStatic ( $k , $v ) <
if(! is_resource ( $v ))
$this -> statics [ $k ]= $v ;
>

function __wakeup () <
foreach( $this -> statics as $k => $v )
eval( $this -> name . «::\$» . $k . «=\$this->statics[‘» . $k . «‘];» );
>
>

function storeStaticAttributes () <
$classes = get_declared_classes ();
foreach( $classes as $name ) <
$reflect =new ReflectionClass ( $name );

if( $reflect -> isUserDefined ()) <
$statics = $reflect -> getStaticProperties ();

if(empty( $_SESSION [ «_classes» ]))
$_SESSION [ «_classes» ]=array();

if(empty( $_SESSION [ «_classes» ][ $name ]))
$_SESSION [ «_classes» ][ $name ]=new Classes ( $name );

foreach( $statics as $k => $v )
$_SESSION [ «_classes» ][ $name ]-> setStatic ( $k , $v );
>
>
>
register_shutdown_function ( ‘storeStaticAttributes’ );
?>

Another small thing that is important to note about __sleep() and privte member variables:

class A
<
private $a ;

public function __construct ()
<
$this -> a = 1 ;
>
>

class B extends A
<
protected $b ;

public function __construct ()
<
parent :: __construct ();
$this -> b = 2 ;
>

function __sleep ()
<
return array( ‘a’ , ‘b’ );
>
>

serialize (new B );
?>

result:
Notice: serialize(): «a» returned as member variable from __sleep() but does not exist in .

To summerize: in a given class hierarchy in which parent classes contain private member variables, those variables are serialized when __sleep() is not defined. However, once __sleep() is defined, there is no way to make those private member variables serialized as well. From that point on, serialization is performed from the visibility scope of the subclass.

It is particularly important to note this little quirk when designing base classes that their derivables may be serialized, or when subclassing an external library class.

Until __sleep is «fixed» (here’s hoping), a function that will return ALL members of a given object — public, protected, AND private:

public function getPropertyNames (array $filter = NULL )
<
$rc = new ReflectionObject ( $this );
$names = array();

while ( $rc instanceof ReflectionClass )
<
foreach ( $rc -> getProperties () as $prop )
<
if (! $filter || ! in_array ( $prop -> getName (), $filter ))
$names [] = $prop -> getName ();
>

$rc = $rc -> getParentClass ();
>

The above hint for using array_keys((array)$obj) got me investigating how to get __sleep to really work with object hierarchies.

With PHP 5.2.3, If you want to serialize an object that is part of an object hierarchy and you want to selectively serialize members (public, private, and protected) by manually specifying the array of members, there are a few simple rules for naming members that you must follow:

1. public members should be named using just their member name, like so:


class Foo <
public $bar ;

public function __sleep () <
return array( «bar» );
>
>
?>

2. protected members should be named using «\0» . «*» . «\0» . member name, like so:

class Foo <
protected $bar ;

public function __sleep () <
return array( «\0*\0bar» );
>
>
?>

3. private members should be named using «\0» . class name . «\0» . member name, like so:

class Foo <
private $bar ;

public function __sleep () <
return array( «\0Foo\0bar» );
>
>
?>

So with this information let us serialize a class hierarchy correctly:

class Base <
private $foo = «foo_value» ;
protected $bar = «bar_value» ;

public function __sleep () <
return array( «\0Base\0foo» , «\0*\0bar» );
>
>

class Derived extends Base <
public $baz = «baz_value» ;
private $boo = «boo_value» ;

public function __sleep () <
// we have to merge our members with our parent’s
return array_merge (array( «baz» , «\0Derived\0boo» ), parent :: __sleep ());
>
>

class Leaf extends Derived <
private $qux = «qux_value» ;
protected $zaz = «zaz_value» ;
public $blah = «blah_value» ;

public function __sleep () <
// again, merge our members with our parent’s
return array_merge (array( «\0Leaf\0qux» , «\0*\0zaz» , «blah» ), parent :: __sleep ());
>
>

// test it
$test = new Leaf ();
$s = serialize ( $test );
$test2 = unserialize ( $s );
echo $s ;
print_r ( $test );
print_r ( $test2 );

?>

Now if you comment out all of the __sleep() functions and output the serialized string, you will see that the output doesn’t change. The most important part of course is that with the proper __sleep() functions, we can unserialize the string and get a properly set up object.

I hope this solves the mystery for everybody. __sleep() does work, if you use it correctly 🙂

Remember that setters and getters (__set, __get) will work in your class as long as you NOT SET the property with given name.

If you still want to have the public property definition in the class source code (phpDocumentor, editor code completition, or any other reason) when using these magic methods, simply unset() your public properties inside the constructor.
__set/__get function will be called and code reader will see at first sight, which public properties are available.

Example:
class user <
/**
* @var int Gets and sets the user ID
*/
public $UserID ;
private $_userID ;

public function __construct () <

// All the magic is in single line:
// We unset public property, so our setters and getters
// are used and phpDoc and editors with code completition are happy
unset( $this -> UserID );

public function __set ( $key , $value ) <
// assign value for key UserID to _userID property
>

public function __get ( $key ) <
// return value of _userID for UserID property
>
>
?>

Maybe we can using unserialize() & __wakeup() instead «new» when creating a new instance of class.

Consider following codes:

class foo
<
static public $WAKEUP_STR = ‘O:3:»foo»:0:<>‘;
public function foo()<>
public function bar()<>
>

Serializing objects is problematic with references. This is solved redefining the __sleep() magic method. This is also problematic when parent class has private variables since the parent object is not accessible nor its private variables from within the child object.

I found a solution that seems working for classes that implements this __sleep() method, and for its subclasses. Without more work in subclasses. The inheritance system does the trick.

Recursively __sleep() call parent’ __sleep() and return the whole array of variables of the object instance to be serialized.

class a <
private $var1 ;

/** Return its variables array, if its parent exists and the __sleep method is accessible, call it and push the result into the array and return the whole thing. */
public function __sleep () <
$a = array_keys ( get_object_vars (& $this ));
if ( method_exists ( parent , ‘__sleep’ )) <
$p = parent :: __sleep ();
array_push ( $a , $p );
>;
return $a ;
>
>

session_start ();
$myfoo = &new foo ();
$myb = &new b ( $myfoo );
$myb = unserialize ( serialize (& $myb ));
?>

This should work, I haven’t tested deeper.

A note: __wakeup occurs before saving the unserialization of an session object.

Therefore, $_SESSION[‘var’]::__wakeup() setting $_SESSION[‘var’] = new Class() will fail and $_SESSION[‘var’] will remain unchanged.

This means that if you have a pseudo-temporary object that contains a class to auto revert to, you have to revert that session object in the initialization of the website rather than via a __wakeup() script.

To be helpful, the __toString() method should return the class name and the state of all its properties inside square brackets.

class Point <
protected $x , $y ;

public function __construct ( $xVal = 0 , $yVal = 0 ) <
$this -> x = $xVal ;
$this -> y = $yVal ;
>

$point1 = new Point ( 10 , 10 );
$point2 = new Point ( 50 , 50 );
echo $point1 . ‘
‘ ;
echo $point2 . ‘

Classes that include objects, should call that objects __toString() method.

class Line <
protected $start , $end ;

public function __construct ( Point $p1 , Point $p2 ) <
$this -> start = $p1 ;
$this -> end = $p2 ;
>

public function __toString () < // the function we're interested in.
return ‘Line[start=’ . $this -> start -> __toString () . // call __toString()
‘, end=’ . $this -> end -> __toString () . ‘]’ ; // call __toString()
>
>

echo (new Line ( $point1 , $point2 ));
?>

Line[start=Point[x=10, y=10], end=Point[x=50, y=50]]

The __toString() method is extremely useful for converting class attribute names and values into common string representations of data (of which there are many choices). I mention this as previous references to __toString() refer only to debugging uses.

I have previously used the __toString() method in the following ways:

— representing a data-holding object as:
— XML
— raw POST data
— a GET query string
— header name:value pairs

— representing a custom mail object as an actual email (headers then body, all correctly represented)

When creating a class, consider what possible standard string representations are available and, of those, which would be the most relevant with respect to the purpose of the class.

Being able to represent data-holding objects in standardised string forms makes it much easier for your internal representations of data to be shared in an interoperable way with other applications.

Imports Pattern — Extend Classes in Real Time:

class BaseClass
<
var $__imported ;
var $__imported_functions ;

function __construct ()
<
$__imported = Array();
$__imported_functions = Array();
>

function Imports ( $object )
<
$new_imports = new $object ();
$imports_name = get_class ( $new_imports );
array_push ( $__imported , Array( $imports_name , $new_imports ) );
$imports_function = get_class_methods ( $new_imports );
foreach ( $imports_function as $i => $function_name )
<
$this -> __imported_functions [ $function_name ] = & $new_imports ;
>
>

function __call ( $m , $a )
<
if ( array_key_exists ( $m , $this -> __imported_functions ))
<
return call_user_func_array (Array( $this -> __imported_functions [ $m ], $m ), $a );
>
throw new ErrorException ( ‘Call to Undefined Method/Class Function’ , 0 , E_ERROR );
>
>

class ExternalFunc
<
function TestB ()
<
echo «External Imported!» ;
>
>

class B extends BaseClass
<
function __construct ()
<
$this -> Imports ( «ExternalFunc» );
>

$b = new B ();
$b -> Msg ();
// or call $b->Imports(«ExternalFunc»);
$b -> TestB ();
//$b->TestB(1,3,4);
?>

Be very careful to define __set_state() in classes which inherit from a parent using it, as the static __set_state() call will be called for any children. If you are not careful, you will end up with an object of the wrong type. Here is an example:

class A
<
public $var1 ;

public static function __set_state ( $an_array )
<
$obj = new A ;
$obj -> var1 = $an_array [ ‘var1’ ];
return $obj ;
>
>

class B extends A <
>

$b = new B ;
$b -> var1 = 5 ;


eval( ‘$new_b = ‘ . var_export ( $b , true ) . ‘;’ );
var_dump ( $new_b );
/*
object(A)#2 (1) <
[«var1»]=>
int(5)
>
*/
?>

public $id = » ;
public $href = » ;
public $target = » ;
public $class = » ;

function __construct ( $href , $label ) <
$this -> href = $href ;
$this -> label = $label ;
>

function nz_arr ( $attrib_arr ) <
$s = » ;
foreach( $attrib_arr as $attrib ) <
$s .= $this -> nz ( $attrib );
>
return $s ;
>

/**
* Print the tag attribute if it is not blank, such as >id»
* @param string $attrib
* @return string
*/
function nz ( $attrib ) <
$s = » ;
if( $this -> $attrib != » ) $s = $attrib . ‘ = «‘ . $this -> $attrib . ‘»‘ ;
return $s ;
>

//This causes RECURSION because of parsing between double quotes. This is a very UNEXPECTED behaviour!
function nz_ ( $attrib ) <
$s = » ;
if( $this -> $attrib != » ) $s = «$attrib = \»$this->$attrib\»» ;
return $s ;
>

//usage
$a = new my_tag_A ( ‘abc.php’ , ‘ABC’ ); $a -> target = ‘_blank’ ;
echo $a ;
//prints:
// ABC
?>

Try this one on.

= new Test ( true );
var_dump ((bool)(string) $ret );
var_dump ( $ret );
$ret = null ;
$ret = new Test ();
var_dump ((bool)(string) $ret );
var_dump ( $ret );

class Test <
protected $state = null ;
function __construct ( $state = null ) <
$this -> state = $state ;
>
function __toString () <
if ( $this -> state ) < return "1" ; >else < return "0" ; >
>
>
?>

You could for instance do..

Alternatively, just make state public, and check it.

There is no automatic way I have found as >
if (!$ret)

But now you have no calling methods, so I hope you passed some data in to get a usable string out.

Of course, if your class isn’t named test, you can add a method..

public function test() <
return $this->state;
>

Logically that will work regardless of the _toString(), but I had hoped to post this to help others see that there are a multitude of ways to check the validity of a class once it is loaded. In __construct you can add any number of checks and set your state appropriately.

__invoke() cannot be used to create fluente interface like in the «D language»

class CallableClass
<
var $next ;
function __invoke ( $x )
<
var_dump ( $x );
return $this ;
>
>
$obj = new CallableClass ;
$obj -> next = new CallableClass ;
var_dump ( $obj ( 5 ) ); // OK!
var_dump ( $obj ( 5 )( 6 ) ); // Parse error
var_dump ( $obj -> next ( 7 ) ); // Fatal error: Call to undefined method CallableClass::next()
var_dump ( < $obj ->next >( 7 ) ); // Parse error
?>

C++-style operator overloading finally makes an appearance with the introduction to __invoke(). Unfortunately, with just ‘()’. In that sense, it is no more useful than having a default class method (probably quite useful actually) and not having to type out an entire method name. Complimenting wbcarts at juno dot com’s point class below, the following allows calculating distance between one or more graph points.

class point <
public $x ;
public $y ;

function __construct ( $x = 0 , $y = 0 ) <
$this -> x = (int) $x ;
$this -> y = (int) $y ;
>

function __invoke () <
$args = func_get_args ();
$total_distance = 0 ;
$current_loc = $this ;
foreach ( $args as $arg ) <
if ( is_object ( $arg ) and ( get_class ( $arg ) === get_class ( $this ))) <
$total_distance += sqrt ( pow ( $arg -> x — $current_loc -> x , 2 ) + pow ((int) $arg -> y — $current_loc -> y , 2 ));
$current_loc = $arg ;
>
else <
trigger_error ( «Arguments must be objects of this class.» );
return;
>
>
return $total_distance ;
>

$p1 = new point ( 1 , 1 );
$p2 = new point ( 23 ,- 6 );
$p3 = new point ( 15 , 20 );
echo $p1 ( $p2 , $p3 , $p1 ); // round trip 73.89

?>

Functionally, __invoke() can also be used to mimic the use of variable functions. Sadly, attempting any calling of __invoke() on a static level will produce a fatal error.

Note a common pitfall when using __wakeup.

If you unserialize a datastructure, you may not rely on the parent object to have been fully unserialized by the time __wakeup is called. Example

class A <
public $b ;
public $name ;
>

class B extends A <
public $parent ;
public function __wakeup () <
var_dump ( $parent -> name );
>
>

$a = new A ();
$a -> name = «foo» ;
$a -> b = new B ();
$a -> b -> parent = $a ;
$s = serialize ( $a );
$a = unserialize ( $s );
?>

Expected output: «foo».
Actual output: NULL.

Reason: $b is unserialized before $name. By the time B::__wakeup is called, $a->name does not yet have a value.

So be aware that the order in which your class variables are defined is important! You need to manually order them by dependencies — or write a __sleep function and order them by depencies there. (Currently I can’t tell which option I hate more)

Concerning __set() with protected/private/overloaded properties, the behavior might not be so intuitive without knowing some underlying rules. Consider this test object for the following examples.

class A <
protected $test_int = 2 ;
protected $test_array = array( ‘key’ => ‘test’ );
protected $test_obj ;

function __construct () <
$this -> test_obj = new stdClass ();
>

Мастер Йода рекомендует:  Регистрация в Одноклассниках и вход на свою страницу

function __get ( $prop ) <
return $this -> $prop ;
>

function __set ( $prop , $val ) <
$this -> $prop = $val ;
>
>

?>

Combined Operators (.=, +=, *=, etc): you must also define a companion __get() method to grant write -and- read access to the property. Remember, «$x += $y» is shorthand for «$x = $x + $y». In other words, «__set($x, (__get($x) + $y))».

Properties that are Arrays: attempting to set array values like «$a->test_array[] = ‘asdf’;» from outside this object will result in an «Indirect modification of overloaded property» notice and the operation completely ignored. You can’t use ‘[]’ for array value assignment in this context (with the exception only if you made __get() return by reference, in which case, it would work fine and bypass the __set() method altogether). You can work around this doing something like unioning the array instead:

-> test_array [] = ‘asdf’ ; // notice given and ignored unless __get() was declared to return by reference
$a -> test_array += array( 1 => ‘asdf’ ); // to add a key/value
$a -> test_array = array( «key» => ‘asdf’ ) + $a -> test_array ; // to overwrite a key/value.

?>

Properties that are Objects: as long as you have that __get() method, you can freely access and alter that sub object’s own properties, bypassing __set() entirely. Remember, objects are assigned and passed by reference naturally.

-> test_obj -> prop = 1 ; // fine if $a did not have a set method declared.

?>

All above tested in 5.3.2.

Warning __toString can be triggerd more then one time

if( strstr ( substr ( $obj , 0 , 1024 ), ‘somestuff’ )
echo $obj ;
return ‘missing somestuff at the start, create container!’ ;

substr () will trigger a __toString aswell as echo $obj ;
?>

wich cause a performance issue since it will gather all data twice.

what i used as a hotfix:

() <
if( null === $this -> sToString )
$this -> sToString = $this -> _show ();
return $this -> sToString ;
>
?>

I think it’s fair to note the undocumented fact that the __invoke magic method can take any number of arguments (or none).

Example:
class InvokeNoParams <
function __invoke ()
<
print __METHOD__ . PHP_EOL ;
$i = 1 ;
foreach ( func_get_args () as $arg ) <
print «The value of \$param <$i>is: » . $arg . PHP_EOL ;
++ $i ;
>
print PHP_EOL ;
>
>

class InvokeSingleParam <
function __invoke ( $param1 )
<
print __METHOD__ . PHP_EOL ;
print «Value of \$param1 is: » . $param1 . PHP_EOL . PHP_EOL ;
>
>

class InvokeMultiParams <
function __invoke ( $param1 , $param2 , $param3 ) <
print __METHOD__ . PHP_EOL ;
print «Value of \$param1 is: » . $param1 . PHP_EOL ;
print «Value of \$param2 is: » . $param2 . PHP_EOL ;
print «Value of \$param3 is: » . $param3 . PHP_EOL . PHP_EOL ;
>
>

$no = new InvokeNoParams ;
$single = new InvokeSingleParam ;
$multi = new InvokeMultiParams ;

$no ( 1 , 2 , 3 );
$single ( ‘one param’ );
$multi ( ‘param 1’ , ‘param 2’ , ‘param 3’ );
?>

This outputs:
InvokeNoParams::__invoke
The value of $param1 is: 1
The value of $param2 is: 2
The value of $param3 is: 3

InvokeSingleParam::__invoke
Value of $param1 is: one param

InvokeMultiParams::__invoke
Value of $param1 is: param 1
Value of $param2 is: param 2
Value of $param3 is: param 3

Invoking a class inside a class results in an error.

class A
<
public function __invoke ()
<
echo «Invoking A() Class» ;
>
>

class B
<
public $a ;

public function __construct ()
<
$this -> a = new A ();
>

public function __invoke ()
<
echo «Invoking B() Class» ;
>
>

?>

returns
Invoking B() Class
PHP Fatal error: Call to undefined method B::a()

Ever wondered why you can’t throw exceptions from __toString()? Yeah me too.

Well now you can! This trick allows you to throw any type of exception from within a __toString(), with a full & correct backtrace.

How does it work? Well PHP __toString() handling is not as strict in every case: throwing an Exception from __toString() triggers a fatal E_ERROR, but returning a non-string value from a __toString() triggers a non-fatal E_RECOVERABLE_ERROR.
Add a little bookkeeping, and can circumvented this PHP deficiency!
(tested to work PHP 5.3+)

(array( ‘My_ToStringFixer’ , ‘errorHandler’ ));
error_reporting ( E_ALL | E_STRICT );

class My_ToStringFixer
<
protected static $_toStringException ;


public static function errorHandler ( $errorNumber , $errorMessage , $errorFile , $errorLine )
<
if (isset( self :: $_toStringException ))
<
$exception = self :: $_toStringException ;
// Always unset ‘_toStringException’, we don’t want a straggler to be found later if something came between the setting and the error
self :: $_toStringException = null ;
if ( preg_match ( ‘

^Method .*::__toString\(\) must return a string value$

‘ , $errorMessage ))
throw $exception ;
>
return false ;
>

public static function throwToStringException ( $exception )
<
// Should not occur with prescribed usage, but in case of recursion: clean out exception, return a valid string, and weep
if (isset( self :: $_toStringException ))
<
self :: $_toStringException = null ;
return » ;
>

self :: $_toStringException = $exception ;

class My_Class
<
public function doComplexStuff ()
<
throw new Exception ( ‘Oh noes!’ );
>

public function __toString ()
<
try
<
// do your complex thing which might trigger an exception
return $this -> doComplexStuff ();
>
catch ( Exception $e )
<
// The ‘return’ is required to trigger the trick
return My_ToStringFixer :: throwToStringException ( $e );
>
>
>

$x = new My_Class ();

try
<
echo $x ;
>
catch ( Exception $e )
<
echo ‘Caught Exception! : ‘ . $e ;
>
?>

You don’t need to serialize the class default values, only those which have changed. It might be important for large objects. Note the example below, for simplicity, always serializes arrays and objects.

class MyBaseClass <
public $name = ‘object’ ; // these are default class values
public $test = 1 ;
public $test2 ; // equals to NULL in fact

public function __construct () <
$this -> test2 = ‘some text’ ; // this is not a default value, although called in the constructor
>

public function __sleep () <
// default class values:
$defaults = get_class_vars ( get_class ( $this )); // not __CLASS__ or self. if you’d like to use in descendant classes
// values of $this object:
$present = get_object_vars ( $this );
$result =array(); // output array
foreach( $present as $key => $value ) <
if(! is_resource ( $defaults [ $key ]) && ( // don’t store resources
is_object ( $defaults [ $key ]) || // always store objects
is_array ( $defaults [ $key ]) || // and arrays
$defaults [ $key ]!== $value ) // and of course all that is not the default value
) // tip: try is_scalar as well
$result []= $key ;
>
return $result ;
>
>

$obj1 =new MyBaseClass ();
echo ( $s1 = serialize ( $obj1 )). «
» ; // only test2 is stored, as it was changed in the constructor

$obj2 =new MyBaseClass ();
$obj2 -> name = ‘object 2’ ; // change default value here
echo ( $s2 = serialize ( $obj2 )). «
» ; // stored name and test2

$obj3 =new MyBaseClass ();
$obj3 -> test2 = NULL ; // switch back to default value
echo ( $s3 = serialize ( $obj3 )). «
» ; // nothing is stored but the class name

// let us check if we can retrieve the objects
unset( $obj1 , $obj2 , $obj3 );
$obj1 = unserialize ( $s1 );
$obj2 = unserialize ( $s2 );
$obj3 = unserialize ( $s3 );
var_dump ( $obj1 );
var_dump ( $obj2 );
var_dump ( $obj3 );
?>

For those of you who have the same trouble as osbertv.
// .

?>

PHP Fatal error: Call to undefined method B::a()

It’s because PHP have bug in parsing syntax (a lot of).
Just make it easier to parse and it would work.
For example, like this:
= $b -> a ;
$c ();
?>

Or this, if you use 5.4 (if you using 5.3 just move call function to the each class which need it or to some base abstract class):
<
function __call ( $method , $args ) <
if (isset( $this -> $method ) && is_callable ( $method )) <
$closure = $this -> $method ;
call_user_func_array ( $closure , $args );
> else <
trigger_error ( ‘Call to undefined method ‘ . __CLASS__ . ‘::’ . $method . ‘()’ , E_USER_ERROR );
>
>
>
class A <
use TInnerClosuresInvoker ;
.
>
?>

It’s a little bit dirty, but it works.

In recent versions of PHP, if you define __toString with arguments it will trigger a Fatal error: «__tostring() cannot take arguments». But, if you really need this (like I needed, because my framework heavily used these arguments), you have a workaround:
class a
<
public function __toString () <
list( $a ) = func_get_args ();
return $a ;
>
>

$a = new a ();
echo $a -> __toString ( ‘PHP’ ); // PHP
?>

To avoid instanciating the parent instead of the inherited class for __set_state() as reported by jsnell, you could use late static binding introduced in PHP 5.3:

class A <
public static function __set_state ( $data ) <
return new static();
>
>

class B extends A <
>

$instance = new B ();
eval( ‘$test = ‘ . var_export ( $instance , true ) . ‘;’ );
var_dump ( $test );
// -> object(B)#2 (0) <
// >
?>

While I was studying Ruby I saw as such interesting things as properties created + its getters and setters in just one line.

I tryied to do the same in PHP and this is the code I have

class Father <
public function __call($name, $args) <
if(isset($this->$name)) <
if(isset($args[0]))
return $this->$name = $args[0];
return $this->$name;
>
return false;
>
>

class Child extends Father <
public $country = «Brazil»;
public $state = «Sao Paulo»;
>

Sometimes we don’t need things like that on all classes but is quite interesting.

A simple API wrapper, using __call() and the PHP 5.6 «. » token.
https://php.net/manual/functions.arguments.php#functions.variable-arg-list

use Exception ;
use ReflectionClass ;
use SomeApiInterface ;
use SomeHttpClient ;
use SomeEndpointHandler ;

/**
* Class SomeApiWrapper
*
* @method SomeEndpointHandler method1(MethodParams $param1)
* @method SomeEndpointHandler method2(MethodParams $param1, AuthParams $param2 = null)
* .
* @method SomeEndpointHandler method42()
*/
class SomeApiWrapper <

/**
* @var \SomeHttpClient
*/
private $httpClient ;

/**
* SomeApiWrapper constructor.
*/
public function __construct () <
$this -> mapApiMethods ();
$this -> httpClient = new SomeHttpClient ();
>

/**
* The API is flat and has

150 endpoints, all of which take optional parameters
* from up to 3 groups (method params, authentication, filters). Instead of
* implementing the interface and adding countless stubs that have basically
* the same signature, i just map its methods here and use __call().
*/
private function mapApiMethods () <
$reflectionClass = new ReflectionClass ( SomeApiInterface ::class);

foreach( $reflectionClass -> getMethods () as $m ) <
$this -> methodMap [] = $m -> name ;
>
>

/**
* Thanks to the PHP 5.6+ «. » token, there’s no hassle with the arguments anymore
* (ugh, bad pun). Just hand the method parameters into the endpoint handler,
* along with other mandatory params — type hints are your friends.
*
* It’s magic!
*
* @param string $method
* @param array $arguments
*
* @return \SomeEndpointHandler
* @throws \Exception
*/
public function __call ( $method , $arguments )<

if( in_array ( $method , $this -> methodMap )) <
return new SomeEndpointHandler ( $this -> httpClient , $method , . $arguments );
>

throw new Exception ( ‘Endpoint «‘ . $method . ‘» does not exist’ );
>

PHP 7+ solves a problem with __invoke noted about 4 yrs ago:

class a <
function __construct () < >
function __invoke () < echo( "Invoked\n" ); >
>

$a = new a ();
$a ();
// Output: Invoked

class b <
private $x ;

function __construct () <
$this -> x = new a ();
( $this -> x )(); // Works in PHP 7+
// $this->x(); // Will blow up in your face: undefined method b::x
>
>

$b = new b ();
// Output: Invoked

__debugInfo is also utilised when calling print_r on an object:

$ cat test.php
class FooQ <

public function __construct ( $val ) <

public function __debugInfo ()
<
return [ ‘_bar’ => $this -> bar ];
>
>
$fooq = new FooQ ( «q» );
print_r ( $fooq );

$ php test . php
FooQ Object
(
[ _bar ] => q
)
$

IMHO a bug or need feature change

providing a object as a array index doesn’t try to us __toString() method so some volatile object identifier is used to index the array, which is breaking any persistency. Type hinting solves that, but while other than «string» type hinting doesn’t work on ob jects, the automatic conversion to string should be very intuitive.

PS: tried to submit bug, but withot patch the bugs are ignored, unfortunately, I don’t C coding

protected $shop_name ;
protected $product_id ;

function __construct ( $shop_name , $product_id ) <
$this -> shop_name = $shop_name ;
$this -> product_id = $product_id ;
>

function __toString () <
return $this -> shop_name . ‘:’ . $this -> product_id ;
>
>

$shop_name = ‘Shop_A’ ;
$product_id = 123 ;
$demo_id = $shop_name . ‘:’ . $product_id ;
$demo_name = ‘Some product in shop A’ ;

$all_products = [ $demo_id => $demo_name ];
$pid = new shop_product_id ( $shop_name , $product_id );

echo «with type hinting: » ;
echo ( $demo_name === $all_products [(string) $pid ]) ? «ok» : «fail» ;
echo «\n» ;


echo «without type hinting: » ;
echo ( $demo_name === $all_products [ $pid ]) ? «ok» : «fail» ;
echo «\n» ;

Наилучшая практика: магические методы PHP __set и __get

Это простые примеры, но представьте, что у вас больше свойств, чем два в вашем классе.

Что было бы лучше всего?

a) Использование __get и __set

b) Использование традиционных сеттеров и геттеров

Автор утверждает, что использование магических методов не является хорошей идеей:

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

Но я хотел бы услышать больше об этом.

Я был в вашем случае в прошлом. И я пошел на магические методы.

Это была ошибка, последняя часть вашего вопроса говорит все:

  • это медленнее (чем getters/seters)
  • нет автозавершения (и это действительно серьезная проблема) и управление типом с помощью IDE для рефакторинга и просмотра кода (под Zend Studio/PhpStorm это можно обработать с помощью аннотации @property phpdoc, но это требует их поддержки: довольно боль).
  • документация (phpdoc) не соответствует тому, как предполагается использовать ваш код, и просмотр вашего класса также не приносит много ответов. Это запутывает.
  • добавлено после редактирования: наличие getters для свойств больше соответствует «реальным» методам, где getXXX() возвращает не только частную собственность, но и делает реальную логику. У вас одинаковое название. Например, у вас есть $user->getName() (возвращает частное свойство) и $user->getToken($key) (вычисляется). В тот день, когда ваш получатель получает больше, чем получатель, и ему нужно сделать какую-то логику, все по-прежнему непротиворечиво.

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

Getters и seters — это боль, чтобы писать (я их ненавижу), но они того стоят.

Урок — Магические методы

↑ PHP5, Объектно-ориентированное программирование
Урок — Магические методы
← Предыдущий урок

Имена методов __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state() and __clone() зарезервированы для «магических» методов в PHP. Не стоит называть свои методы этими именами, если вы не хотите использовать их «магическую» функциональность.

PHP оставляет за собой право все методы, начинающиеся с __, считать «магическими». Не рекомендуется использовать имена методов с __ в PHP, если вы не желаете использовать соответствующий «магический» функционал.

__sleep() и __wakeup()

Функция serialize() проверяет, присутствует ли в вашем классе метод с «магическим» именем __sleep(). Если это так, то этот метод выполняется прежде любой операции сериализации. Он может очистить объект и предполагается, что будет возвращен массив с именами всех переменных объекта, который должен быть сериализован. Если метод ничего не возвращает кроме NULL , то это значит, что объект сериализован и выдается предупреждение E_NOTICE .

Недопустимо возвращать в __sleep() имена приватных свойств объекта в родительский класс. Это приведет к предупреждению E_NOTICE . Вместо этого вы можете использовать интерфейс Serializable.

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

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

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

Пример #1 Sleep и wakeup

class Connection
<
protected $link ;
private $server , $username , $password , $db ;

public function __construct ( $server , $username , $password , $db )
<
$this -> server = $server ;
$this -> username = $username ;
$this -> password = $password ;
$this -> db = $db ;
$this -> connect ();
>

private function connect ()
<
$this -> link = mysql_connect ( $this -> server , $this -> username , $this -> password );
mysql_select_db ( $this -> db , $this -> link );
>

public function __sleep ()
<
return array( ‘server’ , ‘username’ , ‘password’ , ‘db’ );
>

public function __wakeup ()
<
$this -> connect ();
>
>
?>

__toString()

Метод __toString() позволяет классу решать самостоятельно, как он должен реагировать при преобразовании в строку. Например, что напечатает echo $obj;. Этот метод должен возвращать строку, иначе выдастся неисправимая ошибка E_RECOVERABLE_ERROR .

Пример #2 Простой пример

// Объявление простого класса
class TestClass
<
public $foo ;

public function __construct ( $foo )
<
$this -> foo = $foo ;
>

public function __toString ()
<
return $this -> foo ;
>
>

$class = new TestClass ( ‘Привет’ );
echo $class ;
?>

Результат выполнения данного примера:

Ранее, до PHP 5.2.0, метод __toString() вызывался только непосредственно в сочетании с функциями echo или print . Начиная с PHP 5.2.0, он вызывается в любом строчном контексте (например, в printf() с модификатором %s), но не в контекстах других типов (например, с %d модификатором). Начиная с PHP 5.2.0, преобразование объекта в строку при отсутствии метода __toString() вызывает ошибку E_RECOVERABLE_ERROR .

__invoke()

Метод __invoke() вызывается, когда скрипт пытается выполнить объект как функцию.

Данный метод доступен начиная с PHP 5.3.0.

Пример #3 Использование __invoke()

Магические методы PHP

4663 просмотра

2 ответа

57 Репутация автора

На этот вопрос уже есть ответ здесь:

Кто-то, помогите мне понять магические методы более простым способом.

Я знаю, что магические методы запускаются в некоторой точке кода, но я не понимаю, в какой точке они запускаются. Например, в случае __construct () они запускаются в момент создания объекта класса, а передаваемые параметры являются необязательными.

Пожалуйста , скажите мне , когда __get() , __set() , __isset() , __unset() срабатывают в частности. Было бы очень полезно, если бы говорилось о любых других магических методах.

Ответы (2)

плюса

29 Репутация автора

Функции PHP, которые начинаются с двойного подчеркивания — __ , называются магическими функциями (и / или методами) в PHP. Это функции, которые всегда определяются внутри классов, и не являются автономными (вне классов) функциями. Магические функции, доступные в PHP:

__construct (), __destruct (), __call (), __callStatic (), __get (), __set (), __isset (), __unset (), __sleep (), __wakeup (), __toString (), __invoke (), __set_state ( ), __clone () и __autoload ().

Теперь вот пример класса с __construct() магической функцией:

2 плюса

967 Репутация автора

Все магические методы PHP начинаются с «__» и могут использоваться только внутри класса. Я попытался написать пример ниже.

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

Как примечание, __get() и __set() методы будут работать только для частных переменных.

Добавить комментарий