PHP 5 - сериализация объектов и сохранение их отношений - PullRequest
2 голосов
/ 23 августа 2009

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

Можно ли при сериализации объектов сохранять их взаимосвязи? Например

$equipmentHandler = new EquipmentHandler();
$character = new Character();
$character->subscribeOnEquipmentChanged($equipmentHandler);

$_SESSION['character'] = serialize($character);
$_SESSION['subscriber'] = serialize($equipmentHandler);

Сохранятся ли отношения после десериализации? Или я должен объединить их всех в один объект?

$cache['character'] = $character;
$cache['subscriber'] = $equipmentHandler;
$_SESSION['cache'] = serialize($cache);

Любой совет будет оценен.

(PS. Для создания символьных данных требуется много запросов к БД, и я думаю сохранить их, выполняя запись в кэш и в БД, но только для чтения из политики кеша, поэтому они все равно будут сериализованы)

Ответы [ 3 ]

4 голосов
/ 23 августа 2009

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

<?php

echo "BEFORE SERIALIZE:\n";
class A { }
class B { }

$a = new A;
$b = new B;
$a -> b = $b;

$a2 = new A;
$a2 -> b = $b;

var_dump($a->b);
var_dump($a2->b);

echo "AFTER SERIALIZE:\n";
$a3 = unserialize(serialize($a));
$a4 = unserialize(serialize($a2));

var_dump($a3->b);
var_dump($a4->b);

Вывод этого:

BEFORE SERIALIZE:
object(B)#2 (0) {
}
object(B)#2 (0) {
}
AFTER SERIALIZE:
object(B)#5 (0) {
}
object(B)#7 (0) {
}

Ищите число после фунта. Это относится к идентификатору объекта в PHP. Перед сериализацией $ a-> b и $ a2-> b обращаются к объекту с идентификатором объекта # 2: один и тот же экземпляр. Но после сериализации они ссылаются на идентификаторы объектов № 5 и № 7: разные экземпляры.

Это может или не может быть проблемой для вас.

Чтобы восстановить соединение с одним B-объектом, вам придется немного усложниться. Вы можете использовать обработчик __sleep () в A, чтобы сгладить фактическую ссылку на МОМЕНТ B только упоминанием B: «У меня была ссылка на B». Затем реализуйте обработчик __wakeup (), используя упоминание экземпляра B в A, чтобы получить один экземпляр нового объекта B.

КСТАТИ. Расширение сеанса PHP уже выполняет сериализацию автоматически, вам не нужно предварительно сериализовать его самостоятельно:)

0 голосов
/ 23 мая 2013

У вас действительно есть решение в вашем вопросе! В более сложных случаях может потребоваться использовать __sleep и __wakeup ... но, учитывая предоставленную вами информацию, все, что вам нужно сделать, - как вы предлагаете - "объединить их все в один объект".

Объяснение

В ответ на аналогичный вопрос , я сказал:

Сериализация будет поддерживать «относительные» ссылки. (Технически, в PHP нет такого понятия, как относительная ссылка, но это хороший способ осмыслить его.)

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

Пример

// example objects
class A {}
class B {}
$a    = new A();
$b    = new B();
$b->a = $a;
// collect referenced and referencing objects in array
$cache = array( 'a' => $a, 'b' => $b );
// flatten and recreate cache (represents data stored & retrieved from db)
$cached = unserialize( serialize( $cache ) );
// overwrite local variables from cache
extract( $cached, EXTR_OVERWRITE );

Затем, если вы выполните var_dump( $a ); var_dump( $b->a );, обратите внимание на вывод ниже, как идентификаторы объектов для $a и $b->a оба равны '3', указывая, что они оба ссылаются на один и тот же экземпляр A.

object(A)#3 (0) {
}
object(A)#3 (0) {
}
0 голосов
/ 23 августа 2009

В соответствии с руководством по функции сериализации:

Значение для сериализации. serialize () обрабатывает все типы, кроме типа ресурса. Вы даже можете сериализировать () массивы, которые содержат ссылки на себя. Циркулярные ссылки внутри массива / объекта, который вы сериализуете, также будут сохранены. Любая другая ссылка будет потеряна.

При сериализации объектов PHP будет пытаться вызвать функцию-член __sleep до сериализации. Это позволяет объекту выполнять очистку в последнюю минуту и ​​т. Д. Перед сериализацией. Аналогично, когда объект восстанавливается с использованием unserialize (), вызывается функция-член __wakeup.

Так что я думаю, что это невозможно, если вы не сделаете что-то умное в _sleep и _wakeup

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...