I just got through tracking down a very pernicious bug.
The problem was that an array assigned to an object was retaining its original link after cloning. The original array was created with its values being assigned by reference. This wouldn’t have been a problem in and of itself, but additionally there was a second array being created with a reference to the same variable. This was both unnecessary and certain death to my script.
The takeaway from this is to avoid unnecessary variable assignment by reference. The original code was written over two years ago.
If I recall correctly my justification for using references was to save memory.
This is wrong.
Don’t think about references in a context of memory consumption (especially since PHP allocates on write), think about references in a context of “How do I need to treat this data?”
Here is the full script that is broken. Notice in the foreach two referential assignments.
$in = array( 'a' => '123', 'b' => '456' );
$vars = array();
foreach( $in as $key => $val )
{
$_GET[$key] =& $val;
$vars[] =& $val;
unset( $val );
}
class test
{
protected $_data;
public function set( $data )
{
$this->_data = $data;
}
public function setVar( $key, $val )
{
$this->_data[$key] = $val;
}
}
header( 'content-type: text/plain' );
echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );
$test = new test();
$test->set( $_GET );
echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );
$second = clone $test;
$second->setVar( 'a', 'abc' );
echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );
Output
Should be 123 and 456
array(2) {
["a"]=>
&string(3) "123"
["b"]=>
&string(3) "456"
}
Should be 123 and 456
array(2) {
["a"]=>
&string(3) "123"
["b"]=>
&string(3) "456"
}
Should be 123 and 456
array(2) {
["a"]=>
&string(3) "abc"
["b"]=>
&string(3) "456"
}
Corrected Script
$in = array( 'a' => '123', 'b' => '456' );
foreach( $in as $key => $val )
{
$_GET[$key] = $val;
}
class test
{
protected $_data;
public function set( $data )
{
$this->_data = $data;
}
public function setVar( $key, $val )
{
$this->_data[$key] = $val;
}
}
header( 'content-type: text/plain' );
echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );
$test = new test();
$test->set( $_GET );
echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );
$second = clone $test;
$second->setVar( 'a', 'abc' );
echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );
Correct Output
Should be 123 and 456
array(2) {
["a"]=>
string(3) "123"
["b"]=>
string(3) "456"
}
Should be 123 and 456
array(2) {
["a"]=>
string(3) "123"
["b"]=>
string(3) "456"
}
Should be 123 and 456
array(2) {
["a"]=>
string(3) "123"
["b"]=>
string(3) "456"
}
Like this:
Like Loading...