Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP loop through array of object gives unexpected result

I have the following PHP code to set parentId for each post. The parentId of each data all become the last post ID. What's wrong with my logic?

btw, if I change it to array, everythings becomes ok. Please help!

$data = array(
    (object)array('name' => 'myname')
);
$posts = array(
    (object)array('ID' => 1, 'data'=>$data),
    (object)array('ID' => 2, 'data'=>$data),
    (object)array('ID' => 3, 'data'=>$data)
);
foreach($posts as &$post){
    $post->data[0]->parentId = $post->ID;
}
print '<pre>';print_r($posts);die;
die;

Results:

Array
(
    [0] => stdClass Object
        (
            [ID] => 1
            [data] => Array
                (
                    [0] => stdClass Object
                        (
                            [name] => myname
                            [parentId] => 3 // expect to be 1
                        )

                )

        )

    [1] => stdClass Object
        (
            [ID] => 2
            [data] => Array
                (
                    [0] => stdClass Object
                        (
                            [name] => myname
                            [parentId] => 3 // expect to be 2 !!!
                        )

                )

        )

    [2] => stdClass Object
        (
            [ID] => 3
            [data] => Array
                (
                    [0] => stdClass Object
                        (
                            [name] => myname
                            [parentId] => 3
                        )

                )

        )

)
like image 634
user1884811 Avatar asked Feb 16 '23 10:02

user1884811


2 Answers

All things considered, the real issue here is, after a second glance at your code, the way you're setting the data property. Since PHP5, objects are passed/assigned by reference by default. Remember the PHP4 days? ($newInstance = &new SomeClass();), PHP5 uses references for objects now, so when you do:

$data = array(
    (object)array('name' => 'myname')//create object
);

Then, all three objects are assigned the same object (By reference!), so if you change it for the first time around, all three instances will reflect the same change!

I've recently posted a lengthy answer on references in loops here, it might be worth a look, because looping by reference isn't the best way to go about your business.

Some code-review:
Instead of constructing all these arrays, and cast them to objects individually, I'd just do this:

$data = array(
    array('name' => 'myname')
);
$posts = array(
    array('ID' => 1, 'data'=>$data),
    array('ID' => 2, 'data'=>$data),
    array('ID' => 3, 'data'=>$data)
);
foreach($posts as $k => $post)
{
    $posts[$k]['data'][0]['parentId'] = $posts[$k]['ID'];
}
$posts = json_decode(json_encode($posts));//turns everything into objects
print_r($posts);

At first, it might seem inefficient to json_encode something, just to json_decode it, but json_decode returns objects, instead of associative arrays by default. I've ran a few tests scripts not too long ago, as it turned out: the encode-decode approach was actually faster than casting each associative array...

like image 169
Elias Van Ootegem Avatar answered Feb 17 '23 22:02

Elias Van Ootegem


Okay, i misunderstood your problem, due to the fact that you reuse the data object you end up with a reference problem, this can be avoided by using clone, as seen below

<?php

$data = (object) array('name' => 'myname');

$posts = array(
    (object) array('ID' => 1, 'data'=> array(clone $data)),
    (object) array('ID' => 2, 'data'=> array(clone $data)),
    (object) array('ID' => 3, 'data'=> array(clone $data))
);

foreach($posts as $postKey => $post){
    $posts[$postKey]->data[0]->parentId = $posts[$postKey]->ID;
}
print '<pre>';
print_r($posts); 
like image 27
Hannes Avatar answered Feb 18 '23 00:02

Hannes