SitePoint Sponsor

User Tag List

Results 1 to 1 of 1
  1. #1
    SitePoint Member
    Join Date
    Dec 2002
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Strange behavior with extract() and EXTR_REFS

    Pardon the long posting. I wanted to get some other eyes on this weird behavior we're seeing with the extract() function when you specify the EXTR_REFS flag. I can't tell if this is a bug in PHP or if I'm misunderstanding the docs.

    EXTR_REF "extracts variables as references. This effectively means that the values of the imported variables are still referencing the values of the var_array parameter." (from http://us2.php.net/manual/en/function.extract.php)

    From some experiments, I think that this is actually pretty broken, or at least has issues when you pass it an array that has been given a reference or pass in a reference to an array itself.

    First testcase, with an array that was copied by reference:

    PHP Code:
         echo "1: array copy by reference\n";
         
    $a = array( 'foo' => 'bar');
         
    $b =& $a;
         
    print_r($a);
         
    print_r($b);
       
         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);
       
         echo 
    "\n-----\nnow extract from b with EXTR_REFS\n";
       
         
    extract($b,EXTR_REFS);
         echo 
    "post extract, foo = $foo\n";
         
    $foo 'bark';
         echo 
    "post reassign, foo = $foo\n";
         
    print_r($a);
         
    print_r($b); 
    Outputs:

    PHP Code:
       1: array copy by reference
       
    Array
       (
           [
    foo] => bar
       
    )
       Array
       (
           [
    foo] => bar
       
    )
       
       -----
       
    now extract from a with EXTR_REFS
       post extract
    foo bar
       post reassign
    foo noo
       
    Array
       (
           [
    foo] => bar
       
    )
       Array
       (
           [
    foo] => bar
       
    )
       
       -----
       
    now extract from b with EXTR_REFS
       post extract
    foo bar
       post reassign
    foo bark
       
    Array
       (
           [
    foo] => bar
       
    )
       Array
       (
           [
    foo] => bar
       

    It would appear that the extracted variable $foo is NOT actually a reference into $a['foo'].

    Next testcase, doing an array copy by value:

    PHP Code:
         echo "\n\n\n2: array copy by value\n";
       
         
    $a = array( 'foo' => 'bar');
         
    $b $a;
         
    print_r($a);
         
    print_r($b);
       
         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);
       
         echo 
    "\n-----\nnow extract from b with EXTR_REFS\n";
       
         
    extract($b,EXTR_REFS);
         echo 
    "post extract, foo = $foo\n";
         
    $foo 'bark';
         echo 
    "post reassign, foo = $foo\n";
         
    print_r($a);
         
    print_r($b); 
    Outputs:

    PHP Code:
       2: array copy by value
       
    Array
       (
           [
    foo] => bar
       
    )
       Array
       (
           [
    foo] => bar
       
    )
       
       -----
       
    now extract from a with EXTR_REFS
       post extract
    foo bar
       post reassign
    foo noo
       
    Array
       (
           [
    foo] => noo
       
    )
       Array
       (
           [
    foo] => noo
       
    )
       
       -----
       
    now extract from b with EXTR_REFS
       post extract
    foo noo
       post reassign
    foo bark
       
    Array
       (
           [
    foo] => bark
       
    )
       Array
       (
           [
    foo] => bark
       

    I was a bit confused here: $foo appears to be a reference into $a['foo'], but also $b appears to be a reference to $a. Turns out PHP does lazy copies -- copy-by-value is actually copy-by-reference until the script actually modifies one of the copies. And apparently modifying an array value via a reference to one of its member doesn't trigger the lazy copy.

    So actually, this testcase passes.

    Third case: Copy array by reference, then modify, then extract:

    PHP Code:
         echo "\n\n\n3: array copy by reference, with array modification\n";
       
         
    $a = array( 'foo' => 'bar');
         
    $b =& $a;
         
    print_r($a);
         
    print_r($b);
       
         echo 
    "\n-----\nnow modify b\n";
       
         
    $b['fin'] = 'baz';
         
    print_r($a);
         
    print_r($b);
       
         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);
       
         echo 
    "\n-----\nnow extract from b with EXTR_REFS\n";
       
         
    extract($b,EXTR_REFS);
         echo 
    "post extract, foo = $foo\n";
         
    $foo 'bark';
         echo 
    "post reassign, foo = $foo\n";
         
    print_r($a);
         
    print_r($b); 
    Output:

    PHP Code:
       3: array copy by referencewith array modification
       
    Array
       (
           [
    foo] => bar
       
    )
       Array
       (
           [
    foo] => bar
       
    )
       
       -----
       
    now modify b
       
    Array
       (
           [
    foo] => bar
           
    [fin] => baz
       
    )
       Array
       (
           [
    foo] => bar
           
    [fin] => baz
       
    )
       
       -----
       
    now extract from a with EXTR_REFS
       post extract
    foo bar
       post reassign
    foo noo
       
    Array
       (
           [
    foo] => bar
           
    [fin] => baz
       
    )
       Array
       (
           [
    foo] => bar
           
    [fin] => baz
       
    )
       
       -----
       
    now extract from b with EXTR_REFS
       post extract
    foo bar
       post reassign
    foo bark
       
    Array
       (
           [
    foo] => bar
           
    [fin] => baz
       
    )
       Array
       (
           [
    foo] => bar
           
    [fin] => baz
       

    Again, $foo appears to not be a reference to $a['foo'].

    Last case: copy by value, with array modification:

    PHP Code:
         echo "\n\n\n4: array copy by value, with array modification\n";
       
         
    $a = array( 'foo' => 'bar');
         
    $b $a;
         
    print_r($a);
         
    print_r($b);
       
         echo 
    "\n-----\nnow modify b\n";
       
         
    $b['fin'] = 'baz';
         
    print_r($a);
         
    print_r($b);
       
         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);
       
         echo 
    "\n-----\nnow extract from b with EXTR_REFS\n";
       
         
    extract($b,EXTR_REFS);
         echo 
    "post extract, foo = $foo\n";
         
    $foo 'bark';
         echo 
    "post reassign, foo = $foo\n";
         
    print_r($a);
         
    print_r($b); 
    Output:

    PHP Code:
       4: array copy by valuewith array modification
       
    Array
       (
           [
    foo] => bar
       
    )
       Array
       (
           [
    foo] => bar
       
    )
       
       -----
       
    now modify b
       
    Array
       (
           [
    foo] => bar
       
    )
       Array
       (
           [
    foo] => bar
           
    [fin] => baz
       
    )
       
       -----
       
    now extract from a with EXTR_REFS
       post extract
    foo bar
       post reassign
    foo noo
       
    Array
       (
           [
    foo] => noo
       
    )
       Array
       (
           [
    foo] => bar
           
    [fin] => baz
       
    )
       
       -----
       
    now extract from b with EXTR_REFS
       post extract
    foo bar
       post reassign
    foo bark
       
    Array
       (
           [
    foo] => noo
       
    )
       Array
       (
           [
    foo] => bark
           
    [fin] => baz 
    This testcase does everything the way I expect it to. $foo appears to be a reference into $a['foo'] or $b['foo'] after the extract() calls.

    So, here are my questions:

    1) Is there a bug in extract() with EXTR_REFS when more than one symbol points to an array?

    2) Am I missing some nuance to arrays or references to arrays here? You can pass a reference to an array to is_array() and it will return true. I assumed that meant you could pass an array ref to extract() as well. It does appear to do the extraction, but just not correctly with EXTR_REFS.

    The testcases return the same results under PHP 4.3.4, PHP 4.3.8 and PHP 5.0.0 on all the FreeBSD, Linux and Windows systems I have access to. I have not found any reported bugs at bugs.php.net that speak to this behavior, though there is one EXTR_REFS bug that was supposedly fixed in 5.0.0.
    Last edited by raincrow; Aug 3, 2004 at 09:13.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •