I am playing a bit with recursion in php. Unfortunately php value smart copying idea and passing objects by reference by default is not making it easier. Problem occur because modifications made by iteration number X are visible in iteration ex X-2
For example:
/**
* Silly function to find last element in array
* @param ArrayObject $input
*/
function process(ArrayObject $input) {
if ($input->count() == 1) {
return $input->getIterator()->current();
}
$ar = $input->getArrayCopy();
array_shift($ar);
$input->exchangeArray($ar);
return process($input);
}
$in = new ArrayObject(range('a', 'd'));
echo 'Before ' . PHP_EOL;
var_dump($in);
echo PHP_EOL . 'Process - last element is: ' . process($in) . PHP_EOL;
echo 'After ' . PHP_EOL;
var_dump($in);
output is
Before
object(ArrayObject)#1 (1) {
["storage":"ArrayObject":private]=>
array(4) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
[3]=>
string(1) "d"
}
}
Process - last element is: d
After
object(ArrayObject)#1 (1) {
["storage":"ArrayObject":private]=>
array(1) {
[0]=>
string(1) "d"
}
}
as you see recursion modified also original $in variable, what I expect is that every new iteration will operate on copy of value. In this example it is not needed, but what in case that recursion function is more complicated.
Simple solution - clone
function process(ArrayObject $input) {
$input = clone ($input);
if ($input->count() == 1) {
return $input->getIterator()->current();
}
$ar = $input->getArrayCopy();
array_shift($ar);
$input->exchangeArray($ar);
return process($input);
}
ok, works but what if input is very complicated multi-object nested recurred structure? Ok then I can serialize and unserialze value as a substitute for deep copy
function process(ArrayObject $input) {
$input = unserialize(serialize($input));
if ($input->count() == 1) {
return $input->getIterator()->current();
}
$ar = $input->getArrayCopy();
array_shift($ar);
$input->exchangeArray($ar);
return process($input);
}
perfect - it works, but it is a bit waste of time and cpu to un/serialize in every iteration (especially with huge $input).
Is there any other way to do it, something what lets me not worry that I need to 'hack a bit' to use php for standard usage.
//EDIT example with Tigrang suggestion
Tigrang your suggestion was very interesting, but
function process(ArrayObject $input) {
$input = new ArrayObject(($input));
$input->offsetSet(null, 'f');
}
$in = new ArrayObject(range('a', 'd'));
echo 'Before ' . PHP_EOL;
var_dump($in);
process($in);
echo 'After ' . PHP_EOL;
var_dump($in);
After
object(ArrayObject)#1 (1) {
["storage":"ArrayObject":private]=>
array(5) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
[3]=>
string(1) "d"
[4]=>
string(1) "f"
}
}
it still keeps reference to external variable
//edit 2
class Engine {
public $power = 999;
}
class Car {
public $name = '';
public $engine = '';
public function __construct($name, $power) {
$this->name = $name;
$this->engine = new Engine();
$this->engine->power = $power;
}
}
function process(ArrayObject $input) {
$input = new ArrayObject(($input->getArrayCopy()));
$ford = $input[0];
$ford->name = 'Audi';
$ford->engine->power = 1500;
}
$ar = array(new Car('Ford', 130));
$in = new ArrayObject($ar);
echo 'Before ' . PHP_EOL;
var_dump($in);
process($in);
echo 'After ' . PHP_EOL;
var_dump($in);
clone?