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

Reply via email to