Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!


PHP references
New on LowEndTalk? Please Register and read our Community Rules.

All new Registrations are manually reviewed and approved, so a short delay after registration may occur before your account becomes active.

PHP references

perennateperennate Member, Host Rep

A simple PHP script. What does it print at the end?

<?php

$things = array(
        array('name' => 'Thing 1'),
        array('name' => 'Thing 2'),
        array('name' => 'Thing 3'),
);

foreach($things as &$thing) {
        $thing['description'] = $thing['name'];
}

$copythings = array();

foreach($things as $thing) {
        $copythings[] = $thing;
}

print_r($copythings);

?>
«1

Comments

  • Array ( [0] => Array ( [name] => Thing 1 [description] => Thing 1 ) [1] => Array ( [name] => Thing 2 [description] => Thing 2 ) [2] => Array ( [name] => Thing 2 [description] => Thing 2 ) )
    

    as per http://phpfiddle.org/

  • perennateperennate Member, Host Rep
    edited August 2016

    @seenu and you don't think that is a bit weird? Doesn't make you go "fuck PHP references", or more concisely, "fuck PHP"?

    Thanked by 2GCat JasperNL
  • perennate said: @seenu and you don't think that is a bit weird? Doesn't make you go "fuck PHP references", or more concisely, "fuck PHP"?

    No. We like it exactly as it is thank you very much.

  • perennateperennate Member, Host Rep

    Abdussamad said: No. We like it exactly as it is thank you very much.

    Can't tell if you're being serious or not o.o

    But regardless, could you tell what the script would print before running it?

  • @perennate said:
    But regardless, could you tell what the script would print before running it?

    Its pretty clearly documented, so yes.. I guess?
    http://php.net/manual/en/control-structures.foreach.php

  • SvenSven Member

    @perennate said:

    Abdussamad said: No. We like it exactly as it is thank you very much.

    Can't tell if you're being serious or not o.o

    But regardless, could you tell what the script would print before running it?

    What should it do in you opinion?
    It does excactly what I thought.

  • perennate said: But regardless, could you tell what the script would print before running it?

    Yes, actually. This is how PHP has worked for ages.

  • perennateperennate Member, Host Rep
    edited August 2016

    Sven said: What should it do in you opinion? It does excactly what I thought.

    Maybe I've been spoiled by languages that have straightforward semantics. I'd think that the natural thing to expect would be for the second foreach loop to treat $thing as a unreferenced variable. Not that this would make sense either, given the way that references work in PHP, but I've always found it quite silly that the only correct way to write a foreach loop with reference in PHP is with an explicit unset following the loop (sure, you can drop the unset if you don't reuse the variable name, but that is very dangerous -- another programmer might come later and add something further down in the function and encounter unexpected behavior). So, why require the programmer to explicitly add an unset when it could be done implicitly? Things like this drive me away from PHP.

    Almost every other language uses pointers instead of references (actually, "references" usually refers to pointers), which don't have problems like this. Assigning the pointer and assigning the value of the pointer are two separate operations (I suppose you could say that it's the same in PHP, except that assigning the pointer requires a clunky unset followed by an assignment).

  • rm_rm_ IPv6 Advocate, Veteran
    edited August 2016

    mpkossen said: Yes, actually. This is how PHP has worked for ages.

    Sven said: What should it do in you opinion? It does excactly what I thought.

    Sorry, but I don't get it. Maybe you don't either, from the poor formatting in which result has been posted? Here's a simpler version:

    <?php
    $things = array(
            array('name' => 'Thing 1'),
            array('name' => 'Thing 2'),
            array('name' => 'Thing 3'),
    );
    
    foreach($things as &$thing) {
            $thing['description'] = $thing['name'];
    }
    
    print_r($things);
    
    foreach($things as $thing) {
        print_r($thing);
    }
    ?>

    Result:

    Array
    (
        [0] => Array
            (
                [name] => Thing 1
                [description] => Thing 1
            )
    
        [1] => Array
            (
                [name] => Thing 2
                [description] => Thing 2
            )
    
        [2] => Array
            (
                [name] => Thing 3
                [description] => Thing 3
            )
    
    )
    Array
    (
        [name] => Thing 1
        [description] => Thing 1
    )
    Array
    (
        [name] => Thing 2
        [description] => Thing 2
    )
    Array
    (
        [name] => Thing 2
        [description] => Thing 2
    )
  • perennateperennate Member, Host Rep
    edited August 2016

    rm_ said: Sorry, but I don't get it. Maybe you don't either, from the poor formatting in which result has been posted? Here's a simpler version:

    The link @Jonchun posted explains it -- http://php.net/manual/en/control-structures.foreach.php (see big red warning box)

    Of course I much prefer this link -- http://schlueters.de/blog/archives/125-do-not-use-php-references.html

    (or maybe I am misunderstanding what you mean by "I don't get it", in which case I apologize)

  • rm_rm_ IPv6 Advocate, Veteran
    edited August 2016

    @perennate I'm still unsure how that explains the way my example works.
    I did a print_r and it shows the array is fine (1/2/3). But then do a loop over it, and it's 1/2/2.

  • perennateperennate Member, Host Rep
    edited August 2016

    rm_ said: I did a print_r and it shows the array is fine (1/2/3). But then immediately after that loop over it, and it's 1/2/2.

    The first iteration of the loop does this (while also assigning description):

    $thing = &$things[0];
    $thing = &$things[1];
    $thing = &$things[2];

    The second iteration does this:

    $thing = $things[0];
    $thing = $things[1];
    $thing = $things[2];

    However, the last assignment to $thing was to make it a reference to $things[2], which functions as an alias. So really, the second iteration is doing this:

    $things[2] = $things[0]; // $thing = $things[0]
    $things[2] = $things[1]; // $thing = $things[1]
    $things[2] = $things[2]; // $thing = $things[2]

    The last assignment doesn't do anything, since it copies the same value. So $things[2] ends up being $things[1].

  • raindog308raindog308 Administrator, Veteran

    Abdussamad said: No. We like it exactly as it is thank you very much.

    The only people who like PHP are people who haven't been exposed to something better.

    By logical extension, we can rewrite this statement as "The only people who like PHP are people who have never programmed in another language."

  • rm_rm_ IPv6 Advocate, Veteran
    edited August 2016

    Still, looking at the code:

    print_r($things);
    
    foreach($things as $thing) {

    No "write" operations to $things here, and still the content seemingly changed between these. If that's not a straight up bug, then at least for sure it's not "the most obvious behavior ever" or "the way I expected it to work" type of response that your post has so far attracted.

  • rm_rm_ IPv6 Advocate, Veteran
    edited August 2016

    raindog308 said: The only people who like PHP are people who haven't been exposed to something better

    And yes I enjoy programming in PHP daily. It's the sweetest spot between C(++) and shell scripts for me. Python could work too, but it feels way more awkward in some aspects, and back when I was trying it out considering to migrate from PHP, its documentation was utter trash (compared to awesome PHP's CHM docs). Wouldn't be surprised if it still is.

    Thanked by 1doghouch
  • perennateperennate Member, Host Rep
    edited August 2016

    rm_ said: No "write" operations to $things here, and still the content seemingly changed between these. If that's not a straight up bug, then at least for sure it's not "the most obvious behavior ever" or "the way I expected it to work" type of response that your post has so far attracted.

    The write operations to $things[2] happen because $thing is defined earlier as a reference to $things[2], and continues to be a reference through the second foreach loop (which then assigns each element of $things to that reference, an assignment which modifies $things[2]).

    I guess people who mostly code in PHP, or who have coded enough PHP, are used to these things. To me it is very unintuitive, and going further than that, to me it seems like very bad language design (as I said above, the only correct way to write a foreach loop with reference is to unset the reference after the loop, otherwise you might later on accidentally reuse the variable name and get a bug), but other people don't agree.

    I don't doubt that most of the people who said "this is what I would have expected" are aware of how this works though. I mean, Jonchun posted the foreach documentation, so he/she is clearly aware. I was definitely surprised that it was obvious to so many people though, certainly was not obvious to me, and this isn't the first time I've had a bug like this relating to failing to unset references.

  • rm_rm_ IPv6 Advocate, Veteran

    perennate said: I guess people who mostly code in PHP, or who have coded enough PHP, are used to these things.

    Personally I don't use foreach by references at all (or references entirely for that matter), and know well that foreach without reference works on local copies.

    If you absolutely have to pass something by reference, it might be a sign that you are doing something wrong (e.g. passing a super object or array around from function to function). And I hope nobody passes just simple strings by reference "for performance".

  • perennateperennate Member, Host Rep
    edited August 2016

    rm_ said: Personally I don't use foreach by references at all (or references entirely for that matter), and know well that foreach without reference works on local copies.

    If you absolutely have to pass something by reference, it might be a sign that you are doing something wrong (e.g. passing a super object or array around from function to function). And I hope nobody passes just simple strings by reference "for performance".

    Mm, probably the most elegant way would be to use array_map with inline function, i.e.:

    $things = array_map(function($thing) {
        $thing['description'] = $thing['name'];
        return $thing;
    }, $things);

    I'd argue that, if PHP had pointers, a foreach loop with pointers would be more readable than a foreach loop over values that reconstructs the array. The array_map is also readable IMO though, so there's that.

  • rm_rm_ IPv6 Advocate, Veteran
    edited August 2016

    I would do that as:

    foreach(array_keys($things) as $i) {
      $things[$i]['description'] = $things[$i]['name'];
    }

    Not sure if the most elegant but looks clear and obvious.

    Thanked by 2perennate yomero
  • perennateperennate Member, Host Rep
    edited August 2016

    rm_ said: Not sure if the most elegant but looks clear and obvious.

    Ah fair enough, that doesn't require defining a new array variable. Thanks for the idea, I think that's pretty elegant (and that clear+obvious=elegant :).

  • perennateperennate Member, Host Rep
    edited August 2016

    raindog308 said: The only people who like PHP are people who haven't been exposed to something better.

    By logical extension, we can rewrite this statement as "The only people who like PHP are people who have never programmed in another language."

    Ha, I used to defend PHP. "Just use ===, what's the problem?"

  • I still don't get it to be honest. And I use this php thing daily.

    Now I am afraid of introducing bugs for this unexpected behavior (and I guess there are other glitches around probably).

  • perennateperennate Member, Host Rep
    edited August 2016

    yomero said: I still don't get it to be honest. And I use this php thing daily.

    Hm, well, if you like, try http://stackoverflow.com/questions/3307409/php-pass-by-reference-in-foreach (especially the second and third answers).

    yomero said: Now I am afraid of introducing bugs for this unexpected behavior (and I guess there are other glitches around probably).

    I think that constant fear might be necessary for a PHP programmer. :P (then again, maybe true for any programmer)

    Thanked by 1yomero
  • Well, I've read that thread. Almost got it.

    And I think this behavior is awful.

  • @raindog308 said:
    The only people who like PHP are people who haven't been exposed to something better.

    By logical extension, we can rewrite this statement as "The only people who like PHP are people who have never programmed in another language."

    You logic is flawed. Even if we assume you premise is correct, the conclusion you made from it assumes every single programming language is better than PHP, so you need at least one other premise to make that conclusion.

  • doghouchdoghouch Member
    edited August 2016

    @raindog308 said:

    Abdussamad said: No. We like it exactly as it is thank you very much.

    The only people who like PHP are people who haven't been exposed to something better.

    By logical extension, we can rewrite this statement as "The only people who like PHP are people who have never programmed in another language."

    I use PHP regularly in conjunction with NodeJS, and for a few front-end applications :P

    What about Solus? The front-end runs on PHP.

  • Its programmer fault ;) no matter what language do you use. Another example sql injection, that not sql (mysql) fault, but programmer fault.

  • AbdussamadAbdussamad Member
    edited August 2016

    perennate said: But regardless, could you tell what the script would print before running it?

    yes and i was being serious. passing variables by reference is a good feature to have.

  • AbdussamadAbdussamad Member
    edited August 2016

    raindog308 said: The only people who like PHP are people who haven't been exposed to something better.

    If you say so. I've used a bunch of languages over the years. Pascal, Java and python for example. I like PHP because I'm comfortable using it and it makes a lot of things easy.

  • @Abdussamad said:

    perennate said: But regardless, could you tell what the script would print before running it?

    yes and i was being serious. passing variables by reference is a good feature to have.

    He's not talking about the feature.. He's talking about the implementation.

Sign In or Register to comment.