ID: 29493 User updated by: msm at manley dot org Reported By: msm at manley dot org Status: Open Bug Type: Unknown/Other Function Operating System: Linux, FreeBSD PHP Version: 5.0.0 New Comment:
Sent patch to [EMAIL PROTECTED] that passes all of the PHP distribution testcases for bug 25708 and all of the testcases above. Previous Comments: ------------------------------------------------------------------------ [2004-08-05 00:44:03] msm at manley dot org That patch is no good. It causes the testcase for bug 25708 to fail. ------------------------------------------------------------------------ [2004-08-04 23:46:02] msm at manley dot org The problem lies in extract()'s use of the SEPARATE_ZVAL macro in ext/standard/array.c. That macro actually makes a full copy of a ZVAL if the ZVAL's refcount is > 1. I assume that's used when doing lazy copies, normally. The existing extract() function uses SEPARATE_ZVAL_TO_MAKE_IS_REF to set is_ref = 1 in the ZVAL. When the refcount on the extract()ed array is 1, no copy is made and the extracted variables are refs to the array member. When the refcount is > 1, a copy of the array entry gets made by SEPARATE_ZVAL and the extracted variable end up as refs to the copy. Here is a patch that I believe fixes the problem. So far a modified version of PHP 5.0.0 has passed all the testcases with this patch in place. --- array.c Wed Aug 4 15:54:40 2004 +++ array.c.msm Wed Aug 4 16:42:01 2004 @@ -1372,7 +1372,7 @@ if (extract_refs) { zval **orig_var; - SEPARATE_ZVAL_TO_MAKE_IS_REF(entry); + (*(entry))->is_ref = 1; zval_add_ref(entry); if (zend_hash_find(EG(active_symbol_table), final_name.c, final_name.len+1, (void **) &orig_var) == SUCCESS) { ------------------------------------------------------------------------ [2004-08-03 22:59:58] msm at manley dot org Realizing I'm mostly conversing with myself here: I'm not completely certain, but I think the issue is on line 1375 of ext/standard/array.c SEPARATE_ZVAL_TO_MAKE_IS_REF(entry); If I follow the logic back through the twisty little maze of zend.h macros, it would appear that when that macro is called and the refcount on the original entry is > 1, SEPARATE_ZVAL ends up copying the entry entirely. But that would mean the individual entries in a array/hash have the same refcount as the array in general. Perhaps that's true? At this point I am in way deeper than I can figure, having never even looked at the PHP source before today. ------------------------------------------------------------------------ [2004-08-03 20:04:19] msm at manley dot org I shortened my testcases down to the following: <?php rc1(); rc2(); rc3(); function rc1() { echo "\n\n1: array copy by value\n"; $a = array( 'foo' => 'bar'); $b = $a; $b['foo'] = 'diff'; print_r($a); echo "\n-----\nnow extract from a with EXTR_REFS\n"; extract($a,EXTR_REFS); echo "post extract, foo = $foo\n"; $foo = 'noo'; echo "post reassign, foo = $foo\n"; print_r($a); print_r($b); } function rc2() { echo "\n\n2: array copy by reference\n"; $a = array( 'foo' => 'bar'); $b =& $a; print_r($a); echo "\n-----\nnow extract from a with EXTR_REFS\n"; extract($a,EXTR_REFS); echo "post extract, foo = $foo\n"; $foo = 'noo'; echo "post reassign, foo = $foo\n"; print_r($a); } function rc3() { echo "\n\n3: array copy by reference then unset copy\n"; $a = array( 'foo' => 'bar'); $b =& $a; unset($b); print_r($a); echo "\n-----\nnow extract from a with EXTR_REFS\n"; extract($a,EXTR_REFS); echo "post extract, foo = $foo\n"; $foo = 'noo'; echo "post reassign, foo = $foo\n"; print_r($a); } ?> Testcases 1 and 3 pass - $foo is a ref to $a['foo']. Testcase 2 fails -- $foo does not appear to be a ref to $a['foo']. ------------------------------------------------------------------------ [2004-08-03 18:18:25] msm at manley dot org Ignore testcase #2. The behavior there is correct. PHP's lazy copy behavior, in combination with modifying the array via the ref to an entry not triggering the copy, threw me. So it would appear that there is only an issue with extract(array, EXTR_REFS) when there are multiple entries in the symbol table for that array, which would be the first and third testcases. I've looked at ext/standard/array.c and the extract() code, but I'm not familar enough with the code in there to figure out what's wrong. ------------------------------------------------------------------------ The remainder of the comments for this report are too long. To view the rest of the comments, please view the bug report online at http://bugs.php.net/29493 -- Edit this bug report at http://bugs.php.net/?id=29493&edit=1