2

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);
10
  • 1
    Have you considered using arrays instead of objects? Commented Aug 22, 2012 at 22:20
  • 2
    How about using clone? Commented Aug 22, 2012 at 22:21
  • @Truth - yes but I would like to use objects Commented Aug 22, 2012 at 22:26
  • @zerkms - please read description under third grey box, copy is not a perfect solution (unfortunately) Commented Aug 22, 2012 at 22:27
  • @mrok: Why? There's no advantage whatsoever in iterating objects instead of arrays. In fact, the opposite is true, arrays have better performance, and are easier to traverse and manipulate using array functions. Commented Aug 22, 2012 at 22:31

2 Answers 2

3

AFAIK, this is a limitation in PHP. And as you pointed out this happens because of two things in PHP 5x which are not complimentary.

  • Objects are always passed as references
  • clone() only does shallow copy and there is no deep copy mechanism inherent in the language

unserialize(serialize($obj) is the only (highly inefficient) work-around currently available. Lets hope the Gods-that-be sort out some of these issues in the upcoming versions, instead of only breaking their head about how to implement unicode in the much-hyped (and delayed) version 6!

Sign up to request clarification or add additional context in comments.

Comments

0

Why not $input = new ArrayObject($input);

As far as I can tell it copies deeply nested and is faster than serialize: http://viper-7.com/kHLCCP

4 Comments

Interesting, but still does not solve the problem, please look at question update.
Then try $input = new ArrayObject($input->getArrayCopy()); Still faster than serialize
a bit better - but still does not work with objects, please check 2nd update
Ah I see the issue with it now. I guess serializing is the only option.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.