PHP может автоматически сериализовать большинство своих переменных в строки — что позволяет вам сохранять их в хранилище, например, $ _SESSION. Однако есть некоторые хитрости, которые вы должны знать, чтобы избежать взлома скриптов .php и проблем с производительностью.
Примитивы
Примитивная функция serialize () принимает переменную PHP в качестве аргумента и возвращает строку, из которой эта переменная может быть восстановлена:
$ php -r 'var_dump(serialize(42));'
string(5) "i:42;"
unserialize ($ string) выполняет противоположную работу:
$ php -r 'var_dump(unserialize("i:42;"));'
int(42)
Процесс сериализации происходит автоматически, и вам не нужно реализовывать какой-либо интерфейс маркера. Он работает на скалярах, массивах и объектах одинаково.
Однако, это следует за полевыми ссылками:
$ php -r '$object = new stdClass; $object->field = new stdClass; var_dump(serialize($object));'
string(50) "O:8:"stdClass":1:{s:5:"field";O:8:"stdClass":0:{}}"
поэтому, если вы ссылаетесь на объекты ORM или фреймворка из ваших собственных сериализованных объектов, вы, вероятно, получите всю библиотеку вместе с ней. Простой способ избежать этой зависимости сериализации — передать коллаборатора в стек, а не вставлять его в поле объекта:
class MyObject
{
public function doSomeWork(Zend_View $view) {
// refer to $view instead of $this->view
}
}
Запрещенные типы
Некоторые типы переменных не могут быть легко перенесены из одного процесса ОС в другой, и поэтому не подходят для сериализации:
- переменные типа resource (открытые файлы, потоки, соединения старого стиля)
- объекты, которые хранят ресурсы внутри них (экземпляр PDO, представляющий соединение с базой данных)
- замыкания (по какой-то причине, вероятно, связанные с их ссылками, установленными с помощью оператора use )
Каждый объект, который составляет одну из этих переменных, также сталкивается с одной и той же проблемой: исключение, генерируемое во время сериализации. Только когда это происходит, вам приходится прибегать к нестандартным решениям, таким как два следующих.
__sleep () и __wakeup ()
Эта пара необязательных магических методов может сказать PHP сериализовать только часть объекта, что приводит к реализации шаблона Memento.
__sleep () должен возвращать список строк, которые соответствуют именам полей, представляющих состояние объекта, который мы хотим сохранить. Из руководства по PHP:
class Connection
{
protected $connection;
private $server, $username, $password, $db;
public function __sleep()
{
return array('server', 'username', 'password', 'db');
}
...
Этот метод будет вызываться во время сериализации, чтобы определить, что вставить в представление (исключая, например, замыкания, которые вы определили и сохранили в $ this.)
__wakeup () вызывается вместо конструктора после десериализации, чтобы позволить объекту восстановить ссылку на текущий процесс. У него нет аргументов, поэтому, если вы хотите определить его, вам придется извлечь соавторов из какого-то глобального состояния (синглтон, статическая, глобальная переменная) или воссоздать их самостоятельно.
Альтернативой методу __wakeup () является объект Repository, который передается в соавторы во время восстановления перед возвратом действительного объекта.
Сериализуемый интерфейс
Этот интерфейс является альтернативой __sleep (). Это позволяет вам указать, что сериализовать программно, вызывая serialize () для подмножества полей объекта. Опять же из руководства по PHP:
class obj implements Serializable {
private $data;
public function __construct() {
$this->data = "My private data";
}
public function serialize() {
return serialize($this->data);
}
public function unserialize($data) {
$this->data = unserialize($data);
}
}
Интерфейс является альтернативой __sleep () в том смысле, что вы можете использовать только один или другой метод, но не оба.
Под капотом
Причина, по которой вы должны знать, что нужно для правильной сериализации объекта, заключается в том, что это можно сделать без вашего ведома. Например, все, что сохраняется в $ _SESSION, сериализуется для переноса между различными процессами.
Это делается в конце сценария .php и вне обычного потока выполнения; если ошибка возникает из-за несериализуемого поля (например, замыкания) в вашем объекте $ _SESSION [‘state’], вы увидите такую ошибку, как:
Fatal error: Exception thrown without a stack frame in Unknown on line 0
в ваши журналы. Это очень сложно отладить, если вы не знаете, что хранится в сеансе (или что вообще происходит). Если у вас есть рабочая версия кода (например, предыдущий коммит), запустите ручной тест в Apache и посмотрите на / var / lib / php5 (или ваше значение для session.save_path ), чтобы увидеть существующие сессии на сервере. и сериализованные объекты. Это поможет вам почувствовать, что находится внутри, и выяснить, какой объект является виновником; поскольку все ссылки на поля следуют, это может быть очень далеко от графика, который вы помещаете в $ _SESSION.