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.
All new Registrations are manually reviewed and approved, so a short delay after registration may occur before your account becomes active.
PHP references
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); ?>
Comments
as per http://phpfiddle.org/
@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.
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?
Its pretty clearly documented, so yes.. I guess?
http://php.net/manual/en/control-structures.foreach.php
What should it do in you opinion?
It does excactly what I thought.
Yes, actually. This is how PHP has worked for ages.
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).
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:
Result:
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)
@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.
The first iteration of the loop does this (while also assigning description):
The second iteration does this:
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:
The last assignment doesn't do anything, since it copies the same value. So $things[2] ends up being $things[1].
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."
Still, looking at the code:
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.
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.
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.
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.:
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.
I would do that as:
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 .
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).
Hm, well, if you like, try http://stackoverflow.com/questions/3307409/php-pass-by-reference-in-foreach (especially the second and third answers).
I think that constant fear might be necessary for a PHP programmer. :P (then again, maybe true for any programmer)
Well, I've read that thread. Almost got it.
And I think this behavior is awful.
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.
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.
yes and i was being serious. passing variables by reference is a good feature to have.
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.
He's not talking about the feature.. He's talking about the implementation.