ID:               29493
 Updated by:       [EMAIL PROTECTED]
 Reported By:      msm at manley dot org
-Status:           Open
+Status:           Verified
 Bug Type:         Unknown/Other Function
 Operating System: Linux, FreeBSD
 PHP Version:      5.0.0
-Assigned To:      
+Assigned To:      moriyoshi
 New Comment:

Seems a side-effect of the fix I did for bug #25708.



Previous Comments:
------------------------------------------------------------------------

[2004-08-06 16:35:22] msm at manley dot org

Sent patch to [EMAIL PROTECTED] that passes all of the PHP distribution
testcases for bug 25708 and all of the testcases above.

------------------------------------------------------------------------

[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'].

------------------------------------------------------------------------

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

Reply via email to