PHP5: Объект теряет структуру при сериализации/десериализации

  • Автор темы Автор темы camka
  • Дата начала Дата начала

camka

не самка
PHP5: Объект теряет структуру при сериализации/десериализации

Имею объект "дерево" достаточно комплексной структуры: свойства объекта являются ссылками на объекты того же класса. Так же имеется такое свойство как "индекс" для быстрого доступа к узлу по его имени (id). В индексе содержатся массив ссылок на все узлы дерева. Все индексы, кроме индекса корневого узла ссылаются на индекс корневого узла. То есть структура примерно такая:

PHP:
class node{
public $parent; // type node
public $first_sibling; // type node
public $last_sibling; // type node
public $first_child; // type node
public $index = array();
}

При сохранении объекта между запросами в сессию, структура сохраняется идеально и работа с объектом проходит без каких-либо экстцессов. Однако при сохранении объекта в фаил в сериализованном виде и последующей его десериализации и присвоения некой переменной начинаются аномалии. Некотрые ссылки теряются. Например при обращении к некоторому узлу через свойство "индекс" не происходит надлежащего изменения самого узла.

я начинаю грешить на сериализацию/десереализацию, поскольку проведенные испытаня открыли для меня интересный факт. А именно:

Беру объект из сессии, сериализую, записываю результат в файл, десереализую результат из файла, в результате объект получается ломаный. сериализую его сново - результат сериализации не совпадает с изначальным результатом сериализации (это при беглом сравнении строк).

Так же код вида:
PHP:
echo "\n\r",'<br>H: ', serialize ( $_SESSION['node'] ) ;
echo '<br>C: ', serialize ( clone $_SESSION['node'] ) ;
echo '<br>X: ', serialize ( unserialize( serialize ( $_SESSION['node'] ) ) ) ;
echo '<br>X: ', serialize ( unserialize( serialize ( unserialize( serialize ( $_SESSION['node'] ) ) ) ) ) ;

выдает достаточно подозрительные результаты.

Код:
H: O:4:"node":7:{s:5:"props";a:4:{s:2:"id";i:0;s:4:"name";s:4:"root";s:6:"hidden";b:1;s:5:"perms";i:0;}s:6:"parent";N;s:13:"first_sibling";R:1;s:12:"last_sibling";R:1;s:11:"first_child";O:4:"node":7:{s:5:"props";a:4:{s:2:"id";i:1;s:4:"name";s:1:"1";s:6:"hidden";b:0;s:5:"perms";i:0;}s:6:"parent";R:1;s:13:"first_sibling";R:8;s:12:"last_sibling";R:8;s:11:"first_child";N;s:5:"index";a:2:{i:0;R:1;i:1;R:8;}s:7:"counter";i:2;}s:5:"index";R:15;s:7:"counter";R:16;}
C: O:4:"node":7:{s:5:"props";a:4:{s:2:"id";i:0;s:4:"name";s:4:"root";s:6:"hidden";b:1;s:5:"perms";i:0;}s:6:"parent";N;s:13:"first_sibling";O:4:"node":7:{s:5:"props";a:4:{s:2:"id";i:0;s:4:"name";s:4:"root";s:6:"hidden";b:1;s:5:"perms";i:0;}s:6:"parent";N;s:13:"first_sibling";R:8;s:12:"last_sibling";R:8;s:11:"first_child";O:4:"node":7:{s:5:"props";a:4:{s:2:"id";i:1;s:4:"name";s:1:"1";s:6:"hidden";b:0;s:5:"perms";i:0;}s:6:"parent";R:8;s:13:"first_sibling";R:15;s:12:"last_sibling";R:15;s:11:"first_child";N;s:5:"index";a:2:{i:0;R:8;i:1;R:15;}s:7:"counter";i:2;}s:5:"index";R:22;s:7:"counter";R:23;}s:12:"last_sibling";R:8;s:11:"first_child";R:15;s:5:"index";R:22;s:7:"counter";R:23;}
X: O:4:"node":7:{s:5:"props";a:4:{s:2:"id";i:0;s:4:"name";s:4:"root";s:6:"hidden";b:1;s:5:"perms";i:0;}s:6:"parent";N;s:13:"first_sibling";r:1;s:12:"last_sibling";r:1;s:11:"first_child";O:4:"node":7:{s:5:"props";a:4:{s:2:"id";i:1;s:4:"name";s:1:"1";s:6:"hidden";b:0;s:5:"perms";i:0;}s:6:"parent";r:1;s:13:"first_sibling";R:10;s:12:"last_sibling";R:10;s:11:"first_child";N;s:5:"index";a:2:{i:0;r:1;i:1;R:10;}s:7:"counter";i:2;}s:5:"index";R:18;s:7:"counter";R:20;}
X: O:4:"node":7:{s:5:"props";a:4:{s:2:"id";i:0;s:4:"name";s:4:"root";s:6:"hidden";b:1;s:5:"perms";i:0;}s:6:"parent";N;s:13:"first_sibling";r:1;s:12:"last_sibling";r:1;s:11:"first_child";O:4:"node":7:{s:5:"props";a:4:{s:2:"id";i:1;s:4:"name";s:1:"1";s:6:"hidden";b:0;s:5:"perms";i:0;}s:6:"parent";r:1;s:13:"first_sibling";R:10;s:12:"last_sibling";R:10;s:11:"first_child";N;s:5:"index";a:2:{i:0;r:1;i:1;R:10;}s:7:"counter";i:2;}s:5:"index";R:18;s:7:"counter";R:20;}

Собственно говоря, я и хотел проконсультироваться, нормальное ли это поведение пхп, если один и тот же объект при двойной сериализации/десериализации изменяется. Копал в коментариях мануала. Нашел там подобные проблемы, но ни одно решение не помогло. Посему обращаюсь сюда.
 
Вообще удивительно, что подобные структуры можно сериализовать..
Даже представить немогу как сериализатор будет избавлятся от рекурсии типа $node->parent->child->parent->...

имхо, рекурсивные объекты лучше не сереализовать вообще. пхп "знает" про ссылки на объекты, но сериализатор ничего о них не знает и воспринимает как объекты.
 
а как же тогда данные хранятся в сессии? они же там, насколько я знаю, в том же сериализованном виде (или похожем), однако как-то же восстанавливаются, и без проблем?
 
Восстанавливаются, но уже не как ссылки на объекты, а как объекты.
А сий факт может очень навредить, например когда в родетеле вызываем метод, который _должен_ влиять на вызывающий объект, в итоге вызывающий не меняется (меняется его копия).
 
Меня бы вполне устроил такой вариант, поскольку он работоспособен в моей ситуации. И, как мне все-таки кажется, и есть тому подтверждение в мануале, ссылки восстанавливаются именно как ссылки, а не как объекты. Интуитивно подозреваю, что "R" в сериализованной строке как раз-таки и значит - reference. Поправте меня, если я заблуждаюсь.

[manual]
Код:
To make the serialized string into a PHP value again, use 
unserialize(). serialize() handles all types, except the resource-
type. You can even serialize() arrays that contain references to 
itself. References inside the array/object you are serialize()ing will 
also be stored.
[/manual]
 
да, в 4-ке работает. ссылки сериализуются нормально, сам это использую. сомневаюсь что 5-ке что-то изменилось, может просто баг... ?
 
Однако, загадка. Ничего не делал, а баги и след простыл. Пытаюсь ее выцепить, а не удается. Вот вам и мистика. Будем ждать, когда всплывет.

А вот еще один казус на закуску:

при присваивании одной и той же переменной какого-то ее свойства того же типа, что и сама переменная, вылазит сегфолт.

Переменная и является вышеописаным объктом "Дерево"

PHP:
$MENU = $MENU->index[ $someid ];

обошел пока клонированием, а не присвоением:
PHP:
$MENU = clone $MENU->index[ $someid ];
 
Незнаю в тему ли, но у меня тоже не получилось ни сохранить в сессию, ни серелизовать штатными методоми объект DOMDocument, видимо по той же причине (это тоже дерево). Там был здоровенный XML, а в серелизованном виде это занимало 20 байт от силы.
 
Miky
ни один объект встроенного класса (как например DOMDocument) не сериализуется вообще. По крайней мере пока. В php 5.1 они, возможно, станут сериализоваться.
 
resurrection

Поднимаю вопрос сново, потому как проблемы вернулись. Сразу извиняюся за длинный код, но надеюсь он будет понятен.

PHP:
<?php
class node
{
	public $props		= array();	// array of node properties
	public $parent		= null;		// reference to parent node
	public $first_sibling	= null;		// reference to right sibling node 
	public $last_sibling	= null;		// reference to left sibling node
	public $first_child	= null;		// reference to first child node
	public $index		= array();	// reference to root index array
	public $counter		= 0;		// first available id
}
/*
structure
---------

0. root
	1. home
	2. login
		4. register
		5. forgot_pass
	3. menu_editor

*/

$str = 	'O:4:"node":7:{s:5:"props";a:8:{s:2:"id";i:0;s:4:"name";'.
	's:4:"root";s:6:"hidden";b:1;s:5:"perms";i:0;s:6:"active"'.
	';b:0;s:5:"cache";i:0;s:5:"alias";s:4:"root";s:10:"standa'.
	'lone";b:0;}s:6:"parent";N;s:13:"first_sibling";R:1;s:12:'.
	'"last_sibling";R:1;s:11:"first_child";O:4:"node":7:{s:5:'.
	'"props";a:8:{s:2:"id";i:1;s:4:"name";s:4:"home";s:6:"hid'.
	'den";b:0;s:5:"perms";i:0;s:6:"active";b:1;s:5:"alias";s:'.
	'4:"home";s:5:"cache";i:-1;s:10:"standalone";b:0;}s:6:"pa'.
	'rent";R:1;s:13:"first_sibling";O:4:"node":7:{s:5:"props"'.
	';a:8:{s:2:"id";i:2;s:4:"name";s:5:"login";s:6:"hidden";b'.
	':1;s:5:"perms";i:0;s:6:"active";b:1;s:5:"alias";s:5:"log'.
	'in";s:5:"cache";i:0;s:10:"standalone";b:0;}s:6:"parent";'.
	'R:1;s:13:"first_sibling";O:4:"node":7:{s:5:"props";a:8:{'.
	's:2:"id";i:3;s:4:"name";s:11:"menu_editor";s:6:"hidden";'.
	'b:0;s:5:"perms";i:128;s:6:"active";b:1;s:5:"alias";s:11:'.
	'"menu_editor";s:5:"cache";i:0;s:10:"standalone";b:0;}s:6'.
	':"parent";R:1;s:13:"first_sibling";R:12;s:12:"last_sibli'.
	'ng";R:22;s:11:"first_child";N;s:5:"index";a:6:{i:0;R:1;i'.
	':1;R:12;i:2;R:22;i:3;R:32;i:4;O:4:"node":7:{s:5:"props";'.
	'a:8:{s:2:"id";i:4;s:4:"name";s:8:"register";s:6:"hidden"'.
	';b:1;s:5:"perms";i:0;s:6:"active";b:1;s:5:"alias";s:8:"r'.
	'egister";s:5:"cache";i:0;s:10:"standalone";b:0;}s:6:"par'.
	'ent";R:22;s:13:"first_sibling";O:4:"node":7:{s:5:"props"'.
	';a:8:{s:2:"id";i:5;s:4:"name";s:11:"forgot_pass";s:6:"hi'.
	'dden";b:1;s:5:"perms";i:0;s:6:"active";b:1;s:5:"alias";s'.
	':11:"forgot_pass";s:5:"cache";i:0;s:10:"standalone";b:0;'.
	'}s:6:"parent";R:22;s:13:"first_sibling";R:44;s:12:"last_'.
	'sibling";R:44;s:11:"first_child";N;s:5:"index";R:43;s:7:'.
	'"counter";i:6;}s:12:"last_sibling";R:54;s:11:"first_chil'.
	'd";N;s:5:"index";R:43;s:7:"counter";R:65;}i:5;R:54;}s:7:'.
	'"counter";R:65;}s:12:"last_sibling";R:12;s:11:"first_chi'.
	'ld";R:44;s:5:"index";R:43;s:7:"counter";R:65;}s:12:"last'.
	'_sibling";R:32;s:11:"first_child";N;s:5:"index";R:43;s:7'.
	':"counter";R:65;}s:5:"index";R:43;s:7:"counter";R:65;}';

$node = unserialize($str);

echo '<pre>';
/*
foreach( $node->index as $k => $v )
	echo $k, ' - ', $v->props['name'], "\n";
*/

/* 1. */ #$node = &$node->index[4];
/*
0 - 
Notice: Trying to get property of non-object in clone.php on line 38
1 - home
2 - login
3 - menu_editor
4 - register
5 - forgot_pass
*/


/* 2. */ #$node = $node->index[4];
/*
0 - register
1 - home
2 - login
3 - menu_editor
4 - register
5 - forgot_pass
*/


/* 3. */ #$node = clone $node->index[4];
/*
0 - register
1 - home
2 - login
3 - menu_editor
4 - register
5 - forgot_pass
*/


/* 4. */ #$node = unserialize(serialize($node->index[4]));
/*
0 - root
1 - home
2 - login
3 - menu_editor
4 - register
5 - forgot_pass
*/

foreach( $node->index as $k => $v )
	echo $k, ' - ', $v->props['name'], "\n";

?>

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

В итоге требуемый (корректный) результат получается только при сериализации/десериализации.

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

Если кто-то понял о чем речь, буду очень рад услышать комментарии.
 
Попробовал. Отваливается. Далее переименовал $node > $node1, прекратил отваливаться. На первый взгляд гдето в движке неправильно освобождается память.

зы. Тут без 100гр не разобраться.
 
При переименовании ссылка остается все-таки на старую переменную, поскольку если сделать unset($node); , то история повторяется.

а 100гр - это 100 гринов?
 
Нет, 100грамм :)

Старая история с (не)освобождением памяти и/или сохранения целостности ссылок. Похоже так и не пофиксили.
 
Сколько не пытался, не удалось составить примитивного примера для повторения ошибки. Поэтому я пока склоняюсь к мысли, что может быть мой класс какой-то нездоровый. Однако, хотелось бы надеятся, что это не так.
 
Не заморачивайся на багах ПХП, просто пытайся обойти их. Ради этого приходится жертвовать всякими наворотами в коде, но, это лучше чем терять драгоценное время.
 
То есть ты предлагаешь забить на форум и сочинять разного рода воркараунды для каждой проблемы. Не согласен. Я думал может кто сведущий подсобит. Ок. Буду рубить на отмаш.
 
Ну есть же вариант (в твоем примере) переименовать (закомментированные) $node в другое название и все будет работать.
 
Это не вариант, поскольку необходимо хранить и старую переменную. А если у меня таких присвоений несколько, а объект, в свою очередь, объемный.
 
Вобщем, тогда запускай пхп с --enable-debug изучай С (если надо) и смотри что не так с памятью делает ПХП. Вступай в группу саппорта пхп :)

Кстати пробовал портировать под пхп4? Может там лучше дела обстоят?
 
Сверху