php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #52861 unset failes with ArrayObject and deep arrays
Submitted: 2010-09-16 17:23 UTC Modified: 2013-02-27 06:34 UTC
Votes:7
Avg. Score:4.6 ± 0.7
Reproduced:7 of 7 (100.0%)
Same Version:1 (14.3%)
Same OS:0 (0.0%)
From: mep_eisen at web dot de Assigned: stas
Status: Closed Package: SPL related
PHP Version: 5.3.3 OS: Windows Vista 64
Private report: No CVE-ID:
 [2010-09-16 17:23 UTC] mep_eisen at web dot de
Description:
------------
Using deep arrays unset itself or ArrayObject misbehaves. It silently does nothing.
The problem lies in iteration 3 that mixes ArrayObject and classic arrays.

Test script:
---------------
echo "iteration1\n";

$arr = new ArrayObject();
$arr['foo'] = new ArrayObject();
$arr['foo']['bar'] = true;
unset($arr['foo']['bar']);
var_dump($arr);

echo "iteration2\n";

$arr = array();
$arr['foo']['bar'] = true;
unset($arr['foo']['bar']);
var_dump($arr);

echo "iteration3\n";

$arr = new ArrayObject();
$arr['foo']['bar'] = true;
unset($arr['foo']['bar']);
var_dump($arr);

Expected result:
----------------
iteration1
object(ArrayObject)#1 (1) {
  ["storage":"ArrayObject":private]=>
  array(1) {
    ["foo"]=>
    object(ArrayObject)#2 (1) {
      ["storage":"ArrayObject":private]=>
      array(0) {
      }
    }
  }
}
iteration2
array(1) {
  ["foo"]=>
  array(0) {
  }
}
iteration3
object(ArrayObject)#1 (1) {
  ["storage":"ArrayObject":private]=>
  array(1) {
    ["foo"]=>
    array(0) {
    }
  }
}

Actual result:
--------------
iteration1
object(ArrayObject)#1 (1) {
  ["storage":"ArrayObject":private]=>
  array(1) {
    ["foo"]=>
    object(ArrayObject)#2 (1) {
      ["storage":"ArrayObject":private]=>
      array(0) {
      }
    }
  }
}
iteration2
array(1) {
  ["foo"]=>
  array(0) {
  }
}
iteration3
object(ArrayObject)#1 (1) {
  ["storage":"ArrayObject":private]=>
  array(1) {
    ["foo"]=>
    array(1) {
      ["bar"]=>
      bool(true)
    }
  }
}

Patches

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-01-05 18:51 UTC] auke at muze dot nl
It's worse than this, ArrayObject gets confused on where to put the data and mangles any containing object:

test script:

	class IWouldLikeToPointOutABug {

	}

	$buggy = new IWouldLikeToPointOutABug();

	$buggy->bug = new ArrayObject( );

	$buggy->bug['foo']['bar'] = true;

	echo "This looks normal\r\n";

	var_dump( $buggy );

	echo "This should be NULL\r\n";

	var_dump( $buggy->thisIsNotHere );

Results in:

This looks normal

object(IWouldLikeToPointOutABug)#1 (1) {
  ["bug"]=>
  object(ArrayObject)#2 (0) {
  }
}

This should be NULL

array(1) {
  ["bar"]=>
  bool(true)
}
 [2011-01-07 12:07 UTC] auke at muze dot nl
I found a workaround for the problem that suddenly the parent object has any 
property you try to access and has it filled with the previously set array.

If the containing object has its own __get() method which always returns NULL, 
the object is safe again. So this code works as expected:


	class IWouldLikeToPointOutABug {

		function __get( $name ) {
			return NULL;
		}

	}

	$buggy = new IWouldLikeToPointOutABug();

	$buggy->bug = new ArrayObject( );

	$buggy->bug['foo']['bar'] = true;

	echo "This looks normal\r\n";

	var_dump( $buggy );

	echo "This should be NULL\r\n";

	var_dump( $buggy->thisIsNotHere );

One drawback is that you cannot assign properties by reference anymore because 
you're now using the __get() overloading method.
This does not fix the original bug where unsetting fails.

BTW. Our original workaround involved creating our own arrayobject by 
implementing all the array interfaces, but that will not work either. When you 
do this:

	$arrayobject['unsetkey']['key'] = $value;

The offsetSet method is never called for 'unsetKey', just the offsetGet. So 
there is no way to implement the correct behaviour.
 [2011-01-07 12:37 UTC] auke at muze dot nl
Just checked with php 5.3.3 and there the code runs as expected, so at least 
setting works with a nested array. 

For the record I tried the original bugreport code as well and got the same 
result, on a linux system, 64 bits (debian), but also this notice:

PHP Notice:  Indirect modification of overloaded element of ArrayObject has no 
effect in /var/www/test/test.php on line 22

So at least PHP is honest in that it doesn't work :)
 [2011-11-18 13:49 UTC] nemo at ikkoku dot de
One can still use unset if one writes:

----------- >8 -----------
$a = ArrayObject(Array('foo' => Array('bar' => 1)));

// Won't work ($a['foo']['bar'] is still 1)
// Also a notice is displayed: PHP Notice:  Indirect modification of 
// overloaded element of ArrayObject has no effect in php shell code on line ...
var_dump($a);
unset($a['foo']['bar']);
var_dump($a);

// This works
var_dump($a);
$v =& $a['foo'];
unset($v['bar']);
var_dump($a);

// This also works
//$v =& $a['foo']['bar'];
//unset($v);
----------- 8< -----------
 [2013-02-27 06:34 UTC] stas@php.net
The fix for this bug has been committed.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.

 For Windows:

http://windows.php.net/snapshots/
 
Thank you for the report, and for helping us make PHP better.

merged as 61099f85857193c223dd62b0c5302507a77cf0ab
 [2013-02-27 06:34 UTC] stas@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: stas
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Wed Apr 23 09:02:23 2014 UTC