Intereting Posts
Объединение пар в группы несогласованность в преобразовании строки в целое число, когда строка hex, префикс '0x' Сделать запрос HTTPS через PHP и получить ответ Highchart – отображение данных JSON – MYSQL / PHP Функция незаписанного сдвига вправо не работает для отрицательного входа какое лучшее место для изучения php Бесконечное соответствие подвыражений (регулярных выражений) возвращает только одно совпадение Как использовать подготовленный оператор pdo для порядка и ограничений? Как конвертировать UTC datetime в другой часовой пояс? Как убедиться, что пользователь не может прервать выполнение php-кода, вызванного «register_shutdown_function»? Каков алгоритм, лежащий в основе вложенных комментариев? Это хорошая идея объединить Ajax / UI JS Framework (ext, jquery-ui) с фреймворком MVC PHP (zend, symfony)? Создание CSV-файла, а затем принудительное скачивание файла могу ли я отметить уведомление как уже прочитанное, используя график api? Msgstr "Предупреждение: mysql_fetch_array () ожидает, что параметр 1 будет ресурсом, boolean given" error при попытке создать корзину покупок php

«Переходные» свойства в классе PHP?

Я работал с PHP уже несколько лет, но до сих пор мне не приходилось иметь дело с сериализацией явно, используя только $_SESSION . Теперь у меня есть проект, который требует от меня вручную реализовать механизм сериализации для определенных данных – и я понимаю, что проблема применима и к $_SESSION .

У меня есть класс, который содержит ряд свойств. Большинство из этих свойств малы (как в потреблении памяти): числа, относительно короткие строки и т. Д. Однако класс также содержит некоторые свойства, которые могут содержать массивы HUGE (например, весь дамп таблицы базы данных: 100 000 строк по 100 полей каждый ). Как это бывает, это один из классов, который должен быть сериализован / десериализован – и, к счастью, свойства, содержащие большие массивы, не нужно сериализовать, поскольку они по существу являются временными работами и при необходимости восстанавливаются.

В таких обстоятельствах в Java я просто объявлял бы свойство transient – и он был бы исключен из serialisaion. К сожалению, PHP не поддерживает такие квалификаторы.

Один из способов иметь дело с этим:

 class A implements Serializable { private $var_small = 1234; private $var_big = array( ... ); //huge array, of course, not init in this way public function serialize() { $vars = get_object_vars($this); unset($vars['var_big']); return serialize($vars); } public function unserialize($data) { $vars = unserialize($data); foreach ($vars as $var => $value) { $this->$var = $value; } } } не class A implements Serializable { private $var_small = 1234; private $var_big = array( ... ); //huge array, of course, not init in this way public function serialize() { $vars = get_object_vars($this); unset($vars['var_big']); return serialize($vars); } public function unserialize($data) { $vars = unserialize($data); foreach ($vars as $var => $value) { $this->$var = $value; } } } 

Однако это довольно громоздко, так как мне нужно будет обновлять метод serialize каждый раз, когда я добавляю другое свойство переходного процесса. Кроме того, как только наследование вступит в игру, это становится еще более сложным – иметь дело, поскольку переходные свойства могут быть как в подклассе, так и в родительском. Я знаю, это все еще выполнимо, однако я бы предпочел делегировать как можно больше языку, а не изобретать колесо.

Итак, каков наилучший способ справиться с переходными свойствами? Или я что-то упускаю, и PHP поддерживает это из коробки?

Solutions Collecting From Web of "«Переходные» свойства в классе PHP?"

Php предоставляет метод __sleep magic, который позволяет вам выбирать, какие атрибуты должны быть сериализованы.

EDIT Я тестировал, как работает __sleep() когда наследование находится в игре:

 <?php class A { private $a = 'String a'; private $b = 'String b'; public function __sleep() { echo "Sleep A\n"; return array( 'a'); } } class B extends A { private $c = 'String c'; private $d = 'String d'; public function __sleep() { echo "Sleep B\n"; return array( 'c'); } } class C extends A { private $e = 'String e'; private $f = 'String f'; public function __sleep() { echo "Sleep C\n"; return array_merge( parent::__sleep(), array( 'e')); } } $a = new A(); $b = new B(); $c = new C(); echo serialize( $a) ."\n"; // Result: O:1:"A":1:{s:4:"Aa";s:8:"String a";} // called "Sleep A" (correct) echo serialize( $b) ."\n"; // Result: O:1:"B":1:{s:4:"Bc";s:8:"String c";} // called just "Sleep B" (incorrect) echo serialize( $c) ."\n"; // Caused: PHP Notice: serialize(): "a" returned as member variable from __sleep() but does not exist ... // When you declare `private $a` as `protected $a` that class C returns: // O:1:"C":2:{s:4:"*a";s:8:"String a";s:4:"Ce";s:8:"String e";} // which is correct and called are both: "Sleep C" and "Sleep A" 

Таким образом, кажется, что вы можете сериализовать родительские данные только в том случае, если они объявлены как защищенные: – /

EDIT 2 Я пробовал его с интерфейсом Serializable со следующим кодом:

 <?php class A implements Serializable { private $a = ''; private $b = ''; // Just initialize strings outside default values public function __construct(){ $this->a = 'String a'; $this->b = 'String b'; } public function serialize() { return serialize( array( 'a' => $this->a)); } public function unserialize( $data){ $array = unserialize( $data); $this->a = $array['a']; } } class B extends A { private $c = ''; private $d = ''; // Just initialize strings outside default values public function __construct(){ $this->c = 'String c'; $this->d = 'String d'; parent::__construct(); } public function serialize() { return serialize( array( 'c' => $this->c, '__parent' => parent::serialize())); } public function unserialize( $data){ $array = unserialize( $data); $this->c = $array['c']; parent::unserialize( $array['__parent']); } } $a = new A(); $b = new B(); echo serialize( $a) ."\n"; echo serialize( $b) ."\n"; $a = unserialize( serialize( $a)); // C:1:"A":29:{a:1:{s:1:"a";s:8:"String a";}} $b = unserialize( serialize( $b)); // C:1:"B":81:{a:2:{s:1:"c";s:8:"String c";s:8:"__parent";s:29:"a:1:{s:1:"a";s:8:"String a";}";}} print_r( $a); print_r( $b); /** Results: A Object ( [a:A:private] => String a [b:A:private] => ) B Object ( [c:B:private] => String c [d:B:private] => [a:A:private] => String a [b:A:private] => ) */ 

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

Вы можете использовать __sleep и __wakeup . Для первого вы предоставляете массив имен свойств объектов, которые вы хотите сериализовать. Опустите «переходные» члены из этого списка.

__wakeup вызывается немедленно, когда экземпляр неэтериализован. Вы можете использовать это, например, для пополнения непереходных свойств при некоторых условиях.