ID: 38315 Updated by: [EMAIL PROTECTED] Reported By: [EMAIL PROTECTED] -Status: Assigned +Status: Closed Bug Type: Scripting Engine problem Operating System: Linux (N/A) PHP Version: 5.1.4 Assigned To: dmitry New Comment:
The bug is fixed in CVS HEAD and PHP_5_2. Previous Comments: ------------------------------------------------------------------------ [2006-08-03 18:47:19] [EMAIL PROTECTED] Dmitry, could you plz check it out? ------------------------------------------------------------------------ [2006-08-03 18:03:10] [EMAIL PROTECTED] This happens because of the following code segment in function zend_objects_store_call_destructors: for (i = 1; i < objects->top ; i++) { if (objects->object_buckets[i].valid) { .... obj->dtor(obj->object, i TSRMLS_CC); The problem being that the destructor allocates a new object, which due to chance puts the new object in objects->object_buckets[2] This dtor call originated from the zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor TSRMLS_CC); for the element in the global scope. Then after object#2 has been inserted into the objects store, by the dtor of #1 zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC); again starts to walk the objects->object_buckets to dtor the objects. It discovers object#2 and calls the dtor, which yet again constructs a new object, which yet again occupies bucket 1 of the objects_store (call it object#1a). This bucket is passed over already in the loop and the current call_destructors will never destroy this object. Then, zend_hash_graceful_reverse_destroy(&EG(symbol_table)); in zend_execute_API.c:235 again hits the symbol tables, to discover object#1a. Calls the dtor, which allocates object#3. zend_objects_store_free_object_storage is called during shutdown_executor and which free(object#3) without calling its destructor. Now, there are two problems here. One, the object bucket loop does not handle self-modifying destructors. Maybe a linking the buckets, like the usual hashes maintain order in php, might help. Now, I don't know how erealloc works, but the self-modifying nature might have further problems if EG(objects_store).object_buckets is moved while the following "local" value (obj) isn't, in function zend_objects_store_del_ref(): obj = &EG(objects_store).object_buckets[handle].bucket.obj; .... obj->dtor(obj->object, handle TSRMLS_CC); .... if (obj->refcount == 1) { if (obj->free_storage) { Second, the free_storage code does not check if the bucket destructor was called. And finally, why the hell do you call them buckets when they are just arrays indexed by integer offsets ? (confused me into thinking they were chained hashes for a while). I hope I understood everything right. ------------------------------------------------------------------------ [2006-08-03 15:55:50] [EMAIL PROTECTED] Description: ------------ Hi, I already discussed this with Gopal, so he'll probably have something more enlightening to say about it. When calling a class' constructor from one of its object's destructors, the engine behaves strangely. I expected it to recurse forever and segfault like PHP normally does on infinite recursion. Yes, I know I /shouldn't/ be doing this. I just want to make sure something bad isn't happening internally (and document this behaviour). S Reproduce code: --------------- class Foo { function __construct() { echo "constructing\n"; } function __destruct() { $GLOBALS['foo'] = new Foo(); } } $foo = new Foo(); Expected result: ---------------- constructing constructing ... (some large number of times) constructing constructing [[Segmentation Fault]] Actual result: -------------- constructing constructing constructing constructing (yes, only 4 times. 4 is a strange number here, no?) ------------------------------------------------------------------------ -- Edit this bug report at http://bugs.php.net/?id=38315&edit=1