#31525 [Fbk->Opn]: object reference being dropped. $this getting lost.

From: Date: Thu, 27 Jan 2005 01:08:37 +0000
Subject: #31525 [Fbk->Opn]: object reference being dropped. $this getting lost.
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-72906@lists.php.net to get a copy of this message
ID: 31525 User updated by: yml at yml dot com Reported By: yml at yml dot com -Status: Feedback +Status: Open Bug Type: Zend Engine 2 problem Operating System: Linux (redhat 9) PHP Version: 5.0.3 New Comment: Here's the complete test script I put together. The errors are commented in the code. <?php // ----------------------------------------------------------------- // Copyright (C) DTLink, LLC., All Rights Reserved. // // Unless explicitly acquired and licensed from Licensor under the // DTLink Commercial Software License ("DCL") Version 1.0 or greater, the // contents of this file are subject to the Reciprocal Public License // ("RPL") Version 1.1, or subsequent versions as allowed by the RPL, // and You may not copy or use this file in either source code or // executable form, except in compliance with the terms and conditions // of the RPL. // // You may obtain a copy of both the DCL and the RPL (the "Licenses") // from DTLink, LLC. at http://www.formvista.com. // // All software distributed under the Licenses is provided strictly on // an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR // IMPLIED, AND TECHNICAL PURSUIT INC. HEREBY DISCLAIMS ALL SUCH // WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT, // OR NON-INFRINGEMENT. See the Licenses for specific language // governing rights and limitations under the Licenses. // ------------------------------------------------------------------ class fvTree { var $root; // the root node of the tree. var $parent; // parent node. var $children; // array of child nodes. var $nChildren; // number of children var $curChild; // child to be called on getNextChild() call. var $depth; // depth of this child in the tree. // constructor function fvTree( &$root, &$parent ) { $this->children = array(); $this->root = &$root; $this->parent = &$parent; $this->nChildren = 0; $this->curChild = 0; if ( $parent != NULL ) $this->depth = $parent->depth + 1; else $this->depth = 0; } // adds a child node onto the end of the children list. function addChild( &$childNode ) { $this->children[ count( $this->children ) ] =& $childNode; $this->nChildren++; // set the depth of the child. $childNode->depth = $this->depth + 1; $childNode->setParent( $this ); } // returns first child node function &getFirstChild() { if ( $this->nChildren == 0 ) { return( NULL ); } $this->curChild = 1; return( $this->children[ 0 ] ); } // returns NULL if we've reached the end of the list function &getNextChild() { if ( $this->nChildren == 0 ) { return( NULL ); } $this->curChild++; if ( $this->curChild > $this->nChildren ) { return( NULL ); } // our number of children counts start at 1 while the // array offsets start at 0. return( $this->children[ $this->curChild - 1 ] ); } // --------------------------------------------------------- // return reference of children array function &getChildren() { return( $this->children ); } // --------------------------------------------------------- // Used for nested looping // // Ideally I would have liked to put a default value on a reference // parameter so we could just have used the functions above .. but PHP // does not seem to support such a syntax. // returns first child node function &getFirstChildIterator( &$iterator ) { if ( $this->nChildren == 0 ) { return( NULL ); } $iterator = 1; return( $this->children[ 0 ] ); } // returns NULL if we've reached the end of the list function &getNextChildIterator( &$iterator ) { if ( $this->nChildren == 0 ) { return( NULL ); } $iterator++; if ( $iterator > $this->nChildren ) { return( NULL ); } // our number of children counts start at 1 while the // array offsets start at 0. return( $this->children[ $iterator - 1 ] ); } // returns the deepest point in the tree as an integer. function getMaxDepth( $prevDepth ) { $currentDepth = $prevDepth + 1; $maxDepth = $currentDepth; $child = &$this->getFirstChild(); while ( $child != NULL ) { $depth = $child->getMaxDepth( $currentDepth ); if ( $depth > $maxDepth ) $maxDepth = $depth; $child = &$this->getNextChild(); } return( $maxDepth ); } // end of getMaxDepth() // number of children function getNumChildren() { return( $this->nChildren ); } function setParent( &$parent ) { $this->parent =& $parent; } function &getParent() { return( $this->parent ); } function &getRoot() { return( $this->root ); } function getDepth() { return( $this->depth ); } // this is based on the assumption that the root of the tree is passed NULL // as it's root value on creation. function isRoot() { if ( $this->root == NULL ) { return( true ); } return( false ); } } // end of class fvTree // ----------------------------------------------------------------------- // Common base class for all formvista widgets and controls. class fv_baseWidget extends fvTree { /** * a reference to the tag processor. */ var $processor; /** * the current tag */ var $tag; /** * value of any name= attribute. name of component/tag */ var $name; /** * reference to our parent control which may be several levels above us in the widget tree. */ var $parentControl; // ------------------------------------------------------------------ /** * Constructor */ function fv_baseWidget( &$processor, &$parentControl, &$parent, $tag, $attrs ) { $this->fvTree( $parentControl, $parent ); $this->parentControl =& $parentControl; $this->processor =& $processor; $this->tag = $tag; $this->attributes = array(); } // end of constructor. // --------------------------------------------- // Accessors // set name for this widget. function setName( $name ) { $this->name = $name; } // returns an evaluated attribute function getAttributeValue( $name ) { // ############ PHP 5 BUG #################### // // if I comment out the varLookupATTRS() call the $this reference in the // the constructor is unaffected. It goes invalid if varLookupATTRS is called. // // getParentControl is overriden in fv_baseComponent to return a reference to // $this, which is where I believe the problem to be. $parentComponent =& $this->getParentControl(); ddt( "fv_basewidget::getAttributeValue() - $parentComponent is '" . gettype( $parentComponent ) . "'" ); // This seems to be the method call that breaks everything in PHP 5. $value = $parentComponent->varLookupATTRS( $name, $this ); ddt( "fv_baseWidget::getAttributeValue() - value '$value' - type of parentComponent object is '" . gettype( $parentComponent ) . "'" ); return( $value ); } // ------------------------------------------------------------ // returns a reference to the control that manages this object. // // see fv_baseComponent getParentControl() method. function &getParentControl() { return( $this->parentControl ); } } // end of fv_baseWidget // ----------------------------------------------------------------------- /** * Component Base Class. * * This class implements the base of all formVista components. To create a new * component simply subclass this class and override methods. */ class fv_baseComponent extends fv_baseWidget { // ------------------------------------------------------------------ // members used only in components. // // when used in a component, contains the type of the component, // NULL otherwise. It's used to determine in what order we should // look for templates when rendering tags. // // FIXME: we may eventually need a component specific base class. var $componentType; // flag indicating whether or not we're a component (either true or false) var $isComponent; /** * command to process and parameters. * * (Typically used in forms to handle new, edit, search, insert, update * etc. commands. Basically it contains a copy of formvars sent to * this component ... but can also be set into this component by * other components) */ var $cmdVars; // parameters passed into a component (only used at component level) var $parms; // used to lookup variable name spaces more efficiently var $varLookupMap; var $nameSpaces; // ------------------------------------------------------------------ function fv_baseComponent( &$processor, &$parentControl, &$parent, $tag, $attrs ) { $this->fv_baseWidget( $processor, $parentControl, $parent, $tag, $attrs ); $this->varLookupMap = array(); $this->varLookupMap[ "ATTRS" ] = "varLookupATTRS"; // ########################################################### // PHP 5 BUG IS HERE: // // if this is uncommented, the following line will generate a // Attempt to assign property of non-object ddt( "fv_baseComponent Constructor: Before getAttributeValue() call. Type of 'this' is '" . gettype( $this ) . "'" ); $value = $this->getAttributeValue( "defaultlook" ); ddt( "fv_baseComponent Constructor: after getAttributeValue() call. Type of 'this' is '" . gettype( $this ) . "'" ); // $this reference is dropped. At the very least $this should // not get overwritten. $this->nameSpaces = array(); // ########################################################### } // end of constructor Constructor. /** * lookup attributes for this tag * * Note that evaluating the string here is done in the space of the calling widget * not the compnent here. (was a nasty bug) * * In the case of a CDATA tag the attributes of the parent are returned. * * @see fv_cdataWidget */ function varLookupATTRS( $var, &$widget ) { $value = "Hello from varLookupATTRS"; return( $value ); } // returns a reference to the control that manages this object. // // If we are a component, return ourselves. function &getParentControl() { return( $this ); } } // end of fv_baseComponent // END // debug message function. function ddt( $msg ) { print( "<p>" . basename( __FILE__ ) . ":" . __LINE__ . " - <b>$msg</b>\n" ); } // ---------------------------------------------------- $nullVar = NULL; // ---------------------------------------------------- for ( $i=1;$i<1000;$i++ ) { ddt( "Creating fv_baseComponent instance" ); $currentComponent =& new fv_baseComponent( $nullVar, $nullVar, $nullVar, "static", $nullVar ); ddt( "After creating fv_baseComponent - currentComponent type is '" . gettype( $currentComponent ) . "'"); $currentComponent->getAttributeValue( "hello" ); // at this point $this is destroyed. Is "unknown type". ddt( "after getAttributeValue() call - currentComponent type is '" . gettype( $currentComponent ) . "'" ); // this method call will fail. $currentComponent->setName( "test" ); ddt( "The end - currentComponent type is '" . gettype( $currentComponent ) . "'"); } print( "<hr><p>the end</p>" ); ?> Previous Comments: ------------------------------------------------------------------------ [2005-01-26 09:08:22] derick@php.net Just a simple page with the script please, or paste it here. ------------------------------------------------------------------------ [2005-01-26 05:17:23] yml at yml dot com Umm. Check the script I put together for you at: http://www.yml.com/homepage.html?COMP=clog_list&cmd=detail&cs_clog_entries_ref=149 If you have trouble accessing it please let me know. ------------------------------------------------------------------------ [2005-01-26 04:32:00] sniper@php.net Thank you for this bug report. To properly diagnose the problem, we need a short but complete example script to be able to reproduce this bug ourselves. A proper reproducing script starts with <?php and ends with ?>, is max. 10-20 lines long and does not require any external resources such as databases, etc. If possible, make the script source available online and provide an URL to it here. Try to avoid embedding huge scripts into the report. ------------------------------------------------------------------------ [2005-01-13 00:49:01] yml at yml dot com Description: ------------ PHP 5.0.3 using the recommended php.ini with all warnings and errors turned on. RedHat 9. Apache 1.3.33. Config: ./configure --with-apxs=/usr/local/apache/bin/apxs --with-mysql=/usr/local/mysql --prefix=/usr/local/php_5.0.3 --with-mcrypt=/usr/local/lib --with-gd --with-jpeg-dir=/usr/lib --with-zlib-dir=/usr/lib --with-png-dir=/usr/lib --enable-debug --enable-dmalloc --disable-inline-optimization This may be related to bug http://bugs.php.net/31508. Under PHP 4.3.10 the section of code in question causes a busy loop. Under PHP 5 it causes an object to be dropped. More info, sample code and a sample run can be found at: http://www.yml.com/homepage.html?COMP=clog_list&cmd=detail&cs_clog_entries_ref=149 I have a test case that is a radically reduced version of a large body of code. fv_baseComponent extends fv_baseWidget which extends fvTree. I create a minimal fv_baseComponent. It calls a method in the fv_baseWidget base class called getAttributeValue(). getAttributeValue calls getParentControl() which is present in both fv_baseWidget and fv_baseComponent. In fv_baseComponent getParentControl just returns "$this". The returned value is then used to call a method in the fv_baseComponent class. All this happens in the constructor for fv_baseComponent (which I suspect is the cause of the problem). Once the getAttributeValue() is called in the fv_baseComponent constructor, '$this' is no longer a valid object. If I turn on PHP 4 compatibility mode it seems to work. The code works under PHP 4. (however in the full application the same code seems to have problems under PHP 4.3.10 causing a busy loop). If you need to contact me from an email address other than bugs@php.net please use the form at http://www.yml.com/Contact_Yermo.html otherwise I will miss your email in all the spam I get. Reproduce code: --------------- See the download available at: http://www.yml.com/homepage.html?COMP=clog_list&cmd=detail&cs_clog_entries_ref=149 It contains base_widget.php, base_component.php, fvTree.php and test.php. The relevant snippet from test.php contains: $currentComponent =& new fv_baseComponent( $nullVar, $nullVar, $nullVar, "static", $nullVar ); ddt( "After creating fv_baseComponent - currentComponent type is '" . gettype( $currentComponent ) . "'"); $currentComponent->getAttributeValue( "hello" ); // at this point $this is destroyed. Is "unknown type". ddt( "after getAttributeValue() call - currentComponent type is '" . gettype( $currentComponent ) . "'" ); // this method call will fail. $currentComponent->setName( "test" ); ddt( "The end - currentComponent type is '" . gettype( $currentComponent ) . "'"); Expected result: ---------------- PHP should not produce any errors when the above is run. Actual result: -------------- test.php:30 - before class includes test.php:30 - Creating fv_staticCtrl instance test.php:30 - fv_baseComponent Constructor: Before getAttributeValue() call. Type of 'this' is 'object' test.php:30 - fv_basewidget::getAttributeValue() - Object id #1 is 'object' test.php:30 - fv_baseWidget::getAttributeValue() - value 'Hello from varLookupATTRS' - type of parentComponent object is 'object' test.php:30 - fv_baseComponent Constructor: after getAttributeValue() call. Type of 'this' is 'object' Warning: Attempt to assign property of non-object in /usr/local2/WWW/mobie.yml.com/mobie/admin_ui/tests/php_5_test/base_component.php on line 125 test.php:30 - After creating fv_baseComponent - currentComponent type is 'object' test.php:30 - fv_basewidget::getAttributeValue() - Object id #1 is 'object' test.php:30 - fv_baseWidget::getAttributeValue() - value 'Hello from varLookupATTRS' - type of parentComponent object is 'object' test.php:30 - after getAttributeValue() call - currentComponent type is 'unknown type' Fatal error: Call to a member function setName() on a non-object in /usr/local2/WWW/mobie.yml.com/mobie/admin_ui/tests/php_5_test/test.php on line 59 ------------------------------------------------------------------------ -- Edit this bug report at http://bugs.php.net/?id=31525&edit=1

« previous php.bugs (#72906) next »