Bug #62373 [Opn]: serialize() generates wrong reference to the object
| From: | sebastian@php.net | Date: | Wed, 20 Jun 2012 17:37:19 +0000 |
| Subject: | Bug #62373 [Opn]: serialize() generates wrong reference to the object | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-171392@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=62373&edit=1
ID: 62373
Updated by: sebastian@php.net
Reported by: miau dot jp at gmail dot com
Summary: serialize() generates wrong reference to the object
Status: Open
Type: Bug
Package: Class/Object related
Operating System: any
PHP Version: 5.3.14
Block user comment: N
Private report: N
New Comment:
It is worth noting that this occurs in PHP_Depend, see https://github.com/pdepend/pdepend/issues/94
for details. And yes, I have repeatedly experienced the same issue in the past.
Previous Comments:
------------------------------------------------------------------------
[2012-06-20 17:13:33] miau dot jp at gmail dot com
Description:
------------
With many (more than 21760 on 32-bit environment) objects on a PHP environment,
serialize() occasionally generates wrong reference to object.
When an object is passed to serialize() function, a hash value is
caluculated. If the hash value has been seen before, the object is
considered as the same object that is already serialized and serialize()
generates the reference representation such as "r:2".
Hash value is calculated with the logic below.
https://github.com/php/php-src/blob/PHP-5.3.14/ext/standard/var.c#L545
(((size_t)Z_OBJCE_P(var) << 5)
| ((size_t)Z_OBJCE_P(var) >> (sizeof(long) * 8 - 5))) # always 0 on 32-bit
environment
+ (long) Z_OBJ_HANDLE_P(var)
Z_OBJCE_P(var) is the address of zend_class_entry structure for each class.
Z_OBJ_HANDLE_P(var) is the sequencial number that begins with 1 and
increased by 1 for each object.
sizeof(zend_class_entry) is 680 on 32-bit environment. When you declare
two empty class A and B, and construct object $a and $b, hash values of
two object can be the same.
|object|Z_OBJ_HANDLE_P(var) |class|Z_OBJCE_P(var)|hash value |
|------+--------------------+-----+--------------+--------------------+
|$b |1 |B |x + 680 |((x + 680) << 5) + 1|
|(any) |2 | | | |
|(any) |3 | | | |
| |: | | | |
|$a |1 + (680 << 5) |A |x |((x + 680) << 5) + 1|
In such a case, serialize() generates wrong reprensenation.
Test script:
---------------
<?php
class A {}
class B {}
$dummy = array();
$b = new B();
for ($i = 1; $i <= 100000; $i++) {
$a = new A();
$s = serialize(array($b, $a));
if (strpos($s, 'r:') !== false) {
echo "conflict occures at the {$i}th loop!\n";
echo "$s\n";
var_dump(unserialize($s));
exit;
}
$dummy[] = $a;
}
echo "not conflict with each other\n";
Expected result:
----------------
a:2:{i:0;O:1:"B":0:{}i:1;O:1:"A":0:{}}
array(2) {
[0] =>
class B#100002 (0) {
}
[1] =>
class A#100003 (0) {
}
}
Actual result:
--------------
conflict occures at the 21760th loop!
a:2:{i:0;O:1:"B":0:{}i:1;r:2;}
array(2) {
[0] =>
class B#21762 (0) {
}
[1] =>
class B#21762 (0) {
}
}
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=62373&edit=1