|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2002-05-24 16:47 UTC] wdseelig at ncms dot org
Complex objects are not correctly stored in a session. This report is similar to 8676, which was reported fixed. It now appears to be broken in 4.2.1.
Here is a code scriplet to demonstrate the problem:
<?
class Alpha
{
var $alpha1;
var $betavar;
function Alpha()
{
$this->betavar =& new Beta(&$this); //Let Beta use Alpha's methods and properties
$this->alpha1 = 22;
}
function getvar()
{
return $this->alpha1;
}
}
class Beta
{
var $beta1;
var $alphacall;
function Beta(&$par)
{
$this->alphacall = &$par; //alphacall will let Beta use Alpha's methods and properties
$this->beta1 = 5;
}
function getalphavar()
{
print("<br>The value of the alphavar from within Beta is: " . $this->alphacall->alpha1 );
}
}
session_start();
if (! session_is_registered("pm") )
{
session_register("pm"); // register and instantiate the variable
$pm =& new Alpha();
}
print("<br>The vaue of alpha1 is: " . $pm->getvar() );
print("<br>The value of beta1 is: " . $pm->betavar->beta1);
print("<br>" . $pm->betavar->getalphavar() );
?>
OBSERVED BEHAVIOR:
When this page is loaded the first time, everything works as expected, and the
values of alpha1, and beta 1 print out just fine. However, attempts
to refresh the page yield a browser error message "This page cannot be displayed".
This structure worked (flakily) on 4.04pi1, and I was hoping that the fix of 8676 would
have made it solid in 4.2.1. However, the situation is now worse--the above structure
NEVER works.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sun Nov 09 15:00:02 2025 UTC |
Consider the following sample script: <?php session_start(); class alpha { var $b; function alpha() { $this->b = &new beta(&$this); } } class beta { function beta($t) { $this->t = &$t; } } if($_GET['set']) { $_SESSION['a'] = &new alpha(); } else { print_r($_SESSION['a']); } ?> First, call this script with ?set=1. Then, call it without any arguments and nothing will happen. The connection will close inmediately: $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /test.php?PHPSESSID=dcfffa113d892c4320d6109c6bd07795 HTTP/1.1 Host: localhost Connection closed by foreign host. This was exactly what happend with the sample simpleclass.php script provided by wseelig@ncms.org Apache logs show a couple of memleaks and a segfault: home/sander/php/head/Zend/zend_hash.c(262) : Freeing 0x0821CD4C (37 bytes), script=/home/sander/public_html/test.php Last leak repeated 1 time /home/sander/php/head/Zend/zend_hash.c(178) : Freeing 0x0821B1BC (32 bytes), script=/home/sander/public_html/test.php Last leak repeated 1 time /home/sander/php/head/Zend/zend_API.c(597) : Freeing 0x0821B15C (44 bytes), script=/home/sander/public_html/test.php /home/sander/php/head/Zend/zend_API.c(585) : Actual location (location was relayed) Last leak repeated 1 time /home/sander/php/head/Zend/zend_execute.c(1937) : Freeing 0x0821B11C (12 bytes), script=/home/sander/public_html/test.php Last leak repeated 1 time /home/rasmus/php4/ext/standard/url_scanner_ex.re(409) : Freeing 0x08219B6C (13 bytes), script=/home/sander/public_html/test.php [Sat Jun 1 12:06:13 2002] [notice] child pid 4079 exit signal Segmentation fault (11) Backtrace: Program received signal SIGSEGV, Segmentation fault. 0x4010cf96 in malloc () from /lib/libc.so.6 (gdb) bt #0 0x4010cf96 in malloc () from /lib/libc.so.6 #1 0x403d9a4f in _emalloc (size=35, __zend_filename=0x404b0c80 "/home/sander/php/head/Zend/zend_hash.c", __zend_lineno=406, __zend_orig_filename=0x0, __zend_orig_lineno=0) at /home/sander/php/head/Zend/zend_alloc.c:165 #2 0x403f46cc in zend_hash_index_update_or_next_insert (ht=0xbfffef34, h=23815, pData=0xbf800108, nDataSize=4, pDest=0x0, flag=4) at /home/sander/php/head/Zend/zend_hash.c:406 #3 0x4038f81a in php_add_var_hash (var_hash=0xbfffef34, var=0x8219b9c, var_old=0xbf800264) at /home/sander/php/head/ext/standard/var.c:393 #4 0x4038ed6e in php_var_serialize_intern (buf=0xbfffef60, struc=0x8219b10, var_hash=0xbfffef34) at /home/sander/php/head/ext/standard/var.c:497 #5 0x4038f277 in php_var_serialize_intern (buf=0xbfffef60, struc=0x8219d00, var_hash=0xbfffef34) at /home/sander/php/head/ext/standard/var.c:606 #6 0x4038f277 in php_var_serialize_intern (buf=0xbfffef60, struc=0x8219b10, var_hash=0xbfffef34) at /home/sander/php/head/ext/standard/var.c:606 #7 0x4038f277 in php_var_serialize_intern (buf=0xbfffef60, struc=0x8219d00, var_hash=0xbfffef34) at /home/sander/php/head/ext/standard/var.c:606 #8 0x4038f277 in php_var_serialize_intern (buf=0xbfffef60, struc=0x8219b10, var_hash=0xbfffef34) at /home/sander/php/head/ext/standard/var.c:606 #9 0x4038f277 in php_var_serialize_intern (buf=0xbfffef60, struc=0x8219d00, var_hash=0xbfffef34) at /home/sander/php/head/ext/standard/var.c:606 etc etc etcMy apologies for taking so long to respond. The Christmas season intervened. Here is an answer to the question: THE BASICS A circular reference occurrs when an object passes a reference to itself to another object. Imagine a script in which an object of one class instantiates an object of another class. Call the first object the "parent" and the second object the "child". What happens if the parent object passes a reference to itself to the child object? Could the child object then use this reference to access the methods and properties of the parent object? The code would look something like this: <? class Parent { var $childvar; var $parentvar; function Parent() { $this->childvar =& new Child(&$this); $this->parentvar = 22; } } class Child { var $parentcall; function Child(&$par) { $this->parentcall = &$par; } function getparentvar() { print("<br>The value of the parent variable from within the child is" . $this->parentcall->parentvar; } } >? Note that the parent passes a reference to itself to the child which then stores this reference in a variable called $parentcall. When the child function getparentvar is called, the child uses the stored reference to its parent to access a property ($parentvar) of the parent. Will this work? The answer is yes, BUT. The "yes" is that if you run this code it will work fine. The "BUT" is that if you ask PHP's serializer to reconstruct this structure, it will NOT work. What happens, in other words, if you add to the above script code that looks like this: <? if ( ! session_is_registered("sess") )// Create a session variable to store parent and child { session_register("sess"); $sess = new Parent(); } print ("The vaue of parentvar from within the child is: " . $sess->childvar->getparentvar()); ?> Answer: the the first time that you run this, it will work fine. HOWEVER, if you try to refresh your browser screen, PHP uses its seralizer to reconstruct the $sess variable and is unable to do so. As a consequence, you will get error messages, and, in some cases, PHP will crash. The reason is that the serializer considers this structure to be a circular reference [the child class is referencing the parent] and is not able to handle such constructs. A FURTHER EXPLANATION Why would one try to create a Parent/Child structure like that outline above? Perhaps a brief explanation of what I was trying to do will shed further light on the issue. In my case, the "parent" class was a page manager, and was responsible for sending html to the browser. The child classes were a connection manager and a recordset manager. I wanted all of the classes: the page manager, the connection manager, and the recordset manager, to have access to a set of common error handling routines. So, in my first attempt, I included all of the error handling machinery in the page manager class, and passed a reference to the page manager class down to the connection and recordset manager classes. [Much like the Parent/Child code above]. When this didn't work, I removed the error handling code from the page manager class, and created a new error handling class. However, I did not want each object to insantiate its own error object, so I was still confronted with the problem of letting the recordset and connection objects know where the error object was. The solution to this dilemma was to instantiate error, recordset manager, and connection objects in the page manager, and then pass a reference to the error object to both the recordset and the connection objects. Now this may seem like exactly the same kind of thing that got me in trouble in the first place, but it is not. In this case, the reference that is being passed is NOT a reference to the parent, but rather it is a reference to another child object--and this, for some reason, works just fine. I don't know how clear this is; if you would like to ask me questions, please feel free to do so. Wyckham Seelig