RE: [PHP] foreach weirdness
Note that somewhat similar error was discussed on this list a few months ago[1]. You could probably have solved it yourself if you searched the mailing list archives. - Matijn [1] http://www.mail-archive.com/php-general@lists.php.net/msg269552.html --- Thanks Matijn, I missed that discussion, not following the list that actively. I'll try search next time before raising an issue. Cheers Arno -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
RE: [PHP] foreach weirdness
Requesting that will at least require a major-release (f.e. PHP 6.0) ... but I would rather request to add a notice or warning to the documentation of references to remind stuff like that. http://www.php.net/manual/en/language.references.php I think this is stuff more people will stumble over ... Bye Simon -- I agree it would be wrong to change php's handling of call by reference in foreach loops because there may be a need to access that reference after completing the loop. I was going to suggest there should be a warning in the manual to unset the reference after the foreach loop is completed, but I see the current online manual has that warning prominently displayed on the foreach page. I had a version of the manual from July last year that didn't have the warning, so I'll update my local manual and make sure I keep it up to date. Lesson learned (both manual and foreach references). Cheers Arno -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] foreach weirdness
2012/3/25 Arno Kuhl : > > will not only give the wrong result, it will corrupt the array for *any* > further use of that array. I still think it’s a bug according to the > definition of foreach in the php manual. Maybe php needs to do an implicit > unset at the closing brace of the foreach where was an assign $value by > reference, to remove the reference to the last element (or whatever element > it was pointing to if there was a break) so that it doesn't corrupt the > array, because any assign to $value after the foreach loop is completed will > corrupt the array (confirmed by testing). The average user (like me) wouldn't > think twice about reusing $value after ending the foreach loop, not realising > that without an unset the array will be corrupted. > Hi, Arno Requesting that will at least require a major-release (f.e. PHP 6.0) ... but I would rather request to add a notice or warning to the documentation of references to remind stuff like that. http://www.php.net/manual/en/language.references.php I think this is stuff more people will stumble over ... Bye Simon -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] foreach weirdness
On Sun, Mar 25, 2012 at 4:11 PM, Arno Kuhl wrote: > From: Simon Schick [mailto:simonsimc...@googlemail.com] > Sent: 24 March 2012 12:30 AM > To: Robert Cummings > Cc: a...@dotcontent.net; php-general@lists.php.net > Subject: Re: [PHP] foreach weirdness > > 2012/3/23 Robert Cummings >> >> On 12-03-23 11:16 AM, Arno Kuhl wrote: >>> >>> >>> it still does not produce the correct result: >>> 0 1 3 6 10 15 21 >>> 0 1 3 6 10 15 15 >> >> >> This looks like a bug... the last row should be the same. What version >> of PHP are you using? Have you checked the online bug reports? >> >> > > Hi, Robert > > Does not seem like a bug to me ... > http://schlueters.de/blog/archives/141-References-and-foreach.html > > What you should do to get the expected result: > Unset the variable after you don't need this reference any longer. > > Bye > Simon > > -- > > Hi Simon, unsetting the $value does fix the problem, but I found that any > time you assign $value by reference in a foreach loop you have to do an unset > to avoid corrupting the array unless you continue to assign $value by > reference (as illustrated in the article you linked). > > So doing something as simple as: > $array = array(0, 1, 2, 3, 4, 5, 6); > foreach ($array as $key=>&$value) { > echo "Key: $key; Value: $value\n"; > } > > and then follow with (from the php manual): > foreach ($array as $key=>$value) { > echo "Key: $key; Value: $value\n"; > } > > will not only give the wrong result, it will corrupt the array for *any* > further use of that array. I still think it’s a bug according to the > definition of foreach in the php manual. Maybe php needs to do an implicit > unset at the closing brace of the foreach where was an assign $value by > reference, to remove the reference to the last element (or whatever element > it was pointing to if there was a break) so that it doesn't corrupt the > array, because any assign to $value after the foreach loop is completed will > corrupt the array (confirmed by testing). The average user (like me) wouldn't > think twice about reusing $value after ending the foreach loop, not realising > that without an unset the array will be corrupted. > > BTW thanks for that reference, it was quite an eye-opener on the pitfalls of > using assign by reference, not only in the foreach loop. > > Cheers > Arno Note that somewhat similar error was discussed on this list a few months ago[1]. You could probably have solved it yourself if you searched the mailing list archives. - Matijn [1] http://www.mail-archive.com/php-general@lists.php.net/msg269552.html -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
RE: [PHP] foreach weirdness
From: Simon Schick [mailto:simonsimc...@googlemail.com] Sent: 24 March 2012 12:30 AM To: Robert Cummings Cc: a...@dotcontent.net; php-general@lists.php.net Subject: Re: [PHP] foreach weirdness 2012/3/23 Robert Cummings > > On 12-03-23 11:16 AM, Arno Kuhl wrote: >> >> >> it still does not produce the correct result: >> 0 1 3 6 10 15 21 >> 0 1 3 6 10 15 15 > > > This looks like a bug... the last row should be the same. What version > of PHP are you using? Have you checked the online bug reports? > > Hi, Robert Does not seem like a bug to me ... http://schlueters.de/blog/archives/141-References-and-foreach.html What you should do to get the expected result: Unset the variable after you don't need this reference any longer. Bye Simon -- Hi Simon, unsetting the $value does fix the problem, but I found that any time you assign $value by reference in a foreach loop you have to do an unset to avoid corrupting the array unless you continue to assign $value by reference (as illustrated in the article you linked). So doing something as simple as: $array = array(0, 1, 2, 3, 4, 5, 6); foreach ($array as $key=>&$value) { echo "Key: $key; Value: $value\n"; } and then follow with (from the php manual): foreach ($array as $key=>$value) { echo "Key: $key; Value: $value\n"; } will not only give the wrong result, it will corrupt the array for *any* further use of that array. I still think it’s a bug according to the definition of foreach in the php manual. Maybe php needs to do an implicit unset at the closing brace of the foreach where was an assign $value by reference, to remove the reference to the last element (or whatever element it was pointing to if there was a break) so that it doesn't corrupt the array, because any assign to $value after the foreach loop is completed will corrupt the array (confirmed by testing). The average user (like me) wouldn't think twice about reusing $value after ending the foreach loop, not realising that without an unset the array will be corrupted. BTW thanks for that reference, it was quite an eye-opener on the pitfalls of using assign by reference, not only in the foreach loop. Cheers Arno -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
RE: [PHP] foreach weirdness
> See this following example that illustrates the problem: > $array = array(0, 1, 2, 3, 4, 5, 6); > foreach ($array as $index=>$value) { > if ( ($index+1) < count($array) ) { > $array[$index+1] += $value; > } > echo $value." "; > } > echo ""; > foreach ($array as $index=>$value) { > echo $value." "; > } > > You'd expect the output to be: > 0 1 3 6 10 15 21 > 0 1 3 6 10 15 21 > > But it's actually: > 0 1 2 3 4 5 6 > 0 1 3 5 7 9 11 Hey, the ouput you get is right. If you want your ouput you have to rewrite your code. Currently you are adding the value of the current index to the next indexes value (e.g. $array[0] + $array[1]; $array[1] + $array[2]; etc.). As I understand it you want to add the current value with the next index (e.g. $array[0] + 1; $array[1] + 2; $array[2] + 3). -- Hi Stephan, the first foreach loop adds the next value to the current value, not the next index. The reason you don't get the expected result in the first foreach loop is because you need to assign the $value by reference, as per the php manual foreach ($array as $index=>&$value) But then to get the correct output from the second simple foreach loop, you also have to assign the $value by reference, contrary to the php manual. If you don't you get the wrong result and the array becomes corrupted. That could be classified as a bug, or at least the manual needs to elaborate on using a foreach when you assign the $value by reference. Cheers Arno -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] foreach weirdness
On 12-03-24 11:15 AM, Al wrote: On 3/23/2012 10:11 PM, Robert Cummings wrote: On 12-03-23 06:30 PM, Simon Schick wrote: 2012/3/23 Robert Cummings On 12-03-23 11:16 AM, Arno Kuhl wrote: it still does not produce the correct result: 0 1 3 6 10 15 21 0 1 3 6 10 15 15 This looks like a bug... the last row should be the same. What version of PHP are you using? Have you checked the online bug reports? Hi, Robert Does not seem like a bug to me ... http://schlueters.de/blog/archives/141-References-and-foreach.html What you should do to get the expected result: Unset the variable after you don't need this reference any longer. Ah yes... that clued me in. I disagree with the article's generalization with respect to references since references accomplish some things that cannot be accomplished otherwise, but even I missed the fact that the second loop was using a variable that was a reference to the last element of the array as created in the first loop *lol*. The user's very act of checking their results was confounding the result... I love it :) Cheers, Rob. Re, your "...that cannot be accomplished otherwise,..." Can you provide some examples? The only ones I've found are when using create_function() and the arguments for callback functions. I can't even remember or find in my code an example of my foreach()loops needed it. Seems, I recall earlier versions of PHP [<4? ]required references for variables. After I submitted "...that cannot be accomplished otherwise,...", I realized it was a patently false statement (a turing machine is a turing machine :). The intent of the statement though was to indicate the greater difficulty in achieving something relatively simple with references. See the other thread "Thinking out loud - continuation". My post dated 2012-03-24 00:24 shows a process that is cumbersome and inefficient to implement in another fashion. References are like pointers... very powerful but with cautions for the unwary. Cheers, Rob. -- E-Mail Disclaimer: Information contained in this message and any attached documents is considered confidential and legally protected. This message is intended solely for the addressee(s). Disclosure, copying, and distribution are prohibited unless authorized. -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] foreach weirdness
On 3/23/2012 10:11 PM, Robert Cummings wrote: On 12-03-23 06:30 PM, Simon Schick wrote: 2012/3/23 Robert Cummings On 12-03-23 11:16 AM, Arno Kuhl wrote: it still does not produce the correct result: 0 1 3 6 10 15 21 0 1 3 6 10 15 15 This looks like a bug... the last row should be the same. What version of PHP are you using? Have you checked the online bug reports? Hi, Robert Does not seem like a bug to me ... http://schlueters.de/blog/archives/141-References-and-foreach.html What you should do to get the expected result: Unset the variable after you don't need this reference any longer. Ah yes... that clued me in. I disagree with the article's generalization with respect to references since references accomplish some things that cannot be accomplished otherwise, but even I missed the fact that the second loop was using a variable that was a reference to the last element of the array as created in the first loop *lol*. The user's very act of checking their results was confounding the result... I love it :) Cheers, Rob. Re, your "...that cannot be accomplished otherwise,..." Can you provide some examples? The only ones I've found are when using create_function() and the arguments for callback functions. I can't even remember or find in my code an example of my foreach()loops needed it. Seems, I recall earlier versions of PHP [<4? ]required references for variables. -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] foreach weirdness
On 12-03-23 06:30 PM, Simon Schick wrote: 2012/3/23 Robert Cummings On 12-03-23 11:16 AM, Arno Kuhl wrote: it still does not produce the correct result: 0 1 3 6 10 15 21 0 1 3 6 10 15 15 This looks like a bug... the last row should be the same. What version of PHP are you using? Have you checked the online bug reports? Hi, Robert Does not seem like a bug to me ... http://schlueters.de/blog/archives/141-References-and-foreach.html What you should do to get the expected result: Unset the variable after you don't need this reference any longer. Ah yes... that clued me in. I disagree with the article's generalization with respect to references since references accomplish some things that cannot be accomplished otherwise, but even I missed the fact that the second loop was using a variable that was a reference to the last element of the array as created in the first loop *lol*. The user's very act of checking their results was confounding the result... I love it :) Cheers, Rob. -- E-Mail Disclaimer: Information contained in this message and any attached documents is considered confidential and legally protected. This message is intended solely for the addressee(s). Disclosure, copying, and distribution are prohibited unless authorized. -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] foreach weirdness
2012/3/23 Robert Cummings > > On 12-03-23 11:16 AM, Arno Kuhl wrote: >> >> >> it still does not produce the correct result: >> 0 1 3 6 10 15 21 >> 0 1 3 6 10 15 15 > > > This looks like a bug... the last row should be the same. What version of > PHP are you using? Have you checked the online bug reports? > > Hi, Robert Does not seem like a bug to me ... http://schlueters.de/blog/archives/141-References-and-foreach.html What you should do to get the expected result: Unset the variable after you don't need this reference any longer. Bye Simon -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] foreach weirdness
On 12-03-23 02:04 PM, Arno Kuhl wrote: Hi Rob I'm using php 5.3.5. What result do you get when you run this code? I haven't checked any bug reports, I'll google to see where I would do that. Your code gets round the problem, but I was specifically referring to the use of foreach with its unexpected side-effects. I know... but when I first started doing things like what you tried to do, there were no references for foreach values and so I've just naturally been in the habit of explicitly accessing the values in the array by index. Apparently that will serve me well since my stuff won't blow up with this bug ;) There are a few different designs like look-ahead where it seemed the obvious way to go. I know I've used this type of foreach coding in the past, and have this nagging feeling there's a whole bunch of code just waiting to explode. I always just assumed it worked because it's pretty simple. I'd previously been caught out forgetting the assign by reference in the foreach loop that modified the array but I always caught it long before it went live, but I never considered having to also use assign by reference in subsequent foreach loops because it so obviously wasn't necessary. Now I'm searching through my scripts to see if there are any potential problems caused by this (already found one), and wondering what else I've done where I no longer have access to the sources. BTW I'm told on another forum this issue has been discussed multiple times on this mailing list - did I miss it? Was there a resolution? I must have missed it too... but then I've not been very active in the past year or so :) Cheers, Rob. -- E-Mail Disclaimer: Information contained in this message and any attached documents is considered confidential and legally protected. This message is intended solely for the addressee(s). Disclosure, copying, and distribution are prohibited unless authorized. -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
RE: [PHP] foreach weirdness
-Original Message- From: Robert Cummings [mailto:rob...@interjinn.com] Sent: 23 March 2012 06:11 PM To: a...@dotcontent.net Cc: php-general@lists.php.net Subject: Re: [PHP] foreach weirdness On 12-03-23 11:16 AM, Arno Kuhl wrote: > The following snippet is copied from the php manual: > foreach ($arr as $key => $value) { > echo "Key: $key; Value: $value\n"; } > > I've always used the foreach loop that way. > But recently I started hitting some really odd problems. > > See this following example that illustrates the problem: > $array = array(0, 1, 2, 3, 4, 5, 6); > foreach ($array as $index=>&$value) {//<- assign $value by reference > if ( ($index+1)< count($array) ) { > $array[$index+1] += $value; > } > echo $value." "; > } > echo ""; > foreach ($array as $index=>$value) { > echo $value." "; > } > > it still does not produce the correct result: > 0 1 3 6 10 15 21 > 0 1 3 6 10 15 15 This looks like a bug... the last row should be the same. What version of PHP are you using? Have you checked the online bug reports? . -- Hi Rob I'm using php 5.3.5. What result do you get when you run this code? I haven't checked any bug reports, I'll google to see where I would do that. Your code gets round the problem, but I was specifically referring to the use of foreach with its unexpected side-effects. There are a few different designs like look-ahead where it seemed the obvious way to go. I know I've used this type of foreach coding in the past, and have this nagging feeling there's a whole bunch of code just waiting to explode. I always just assumed it worked because it's pretty simple. I'd previously been caught out forgetting the assign by reference in the foreach loop that modified the array but I always caught it long before it went live, but I never considered having to also use assign by reference in subsequent foreach loops because it so obviously wasn't necessary. Now I'm searching through my scripts to see if there are any potential problems caused by this (already found one), and wondering what else I've done where I no longer have access to the sources. BTW I'm told on another forum this issue has been discussed multiple times on this mailing list - did I miss it? Was there a resolution? Cheers Arno -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] foreach weirdness
On 12-03-23 11:16 AM, Arno Kuhl wrote: The following snippet is copied from the php manual: foreach ($arr as $key => $value) { echo "Key: $key; Value: $value\n"; } I've always used the foreach loop that way. But recently I started hitting some really odd problems. See this following example that illustrates the problem: $array = array(0, 1, 2, 3, 4, 5, 6); foreach ($array as $index=>$value) { if ( ($index+1)< count($array) ) { $array[$index+1] += $value; } echo $value." "; } echo ""; foreach ($array as $index=>$value) { echo $value." "; } You'd expect the output to be: 0 1 3 6 10 15 21 0 1 3 6 10 15 21 But it's actually: 0 1 2 3 4 5 6 0 1 3 5 7 9 11 This is what I would expect since the value is a copy. As such, one would expect it to be the value before you made modifications to the array. If you assign the $value by reference in the first loop as someone pointed out (and confirmed by the manual: "As of PHP 5, you can easily modify array's elements by preceding $value with&. This will assign reference instead of copying the value") $array = array(0, 1, 2, 3, 4, 5, 6); foreach ($array as $index=>&$value) {//<- assign $value by reference if ( ($index+1)< count($array) ) { $array[$index+1] += $value; } echo $value." "; } echo ""; foreach ($array as $index=>$value) { echo $value." "; } it still does not produce the correct result: 0 1 3 6 10 15 21 0 1 3 6 10 15 15 This looks like a bug... the last row should be the same. What version of PHP are you using? Have you checked the online bug reports? If I watch the $array in a debugger I see odd behaviour for the last element $array[6] when stepping through the second foreach loop. Just before entering the second loop $array[6] == 21 which is correct. When I move to the next line (echo $value." ";) $array[6] changes to 0 !! As I step through the second loop $array[6] keeps on changing for each iteration, with the following values: 0, 1, 3, 6, 10, 15, 15 And once I've left the second loop $array[6] is permanently changed from 21 to 15, even though there's no code in the second loop to change $array[6]. So what's going on here? I confirm this by echoing $array[6] in each iteration in the second loop: $array = array(0, 1, 2, 3, 4, 5, 6); foreach ($array as $index=>&$value) { if ( ($index+1)< count($array) ) { $array[$index+1] += $value; } echo $value." "; } echo ""; foreach ($array as $index=>$value) { echo $array[6]." "; } echo ""; echo $array[6]; the result is: 0 1 3 6 10 15 21 0 1 3 6 10 15 15 15 Note that $array[6] is still 15 even after completing the second foreach loop. If you break out of the second loop then $array[6] will be at whatever value it was at the time you break out (ouch!) If you assign the $value by reference in the second loop as well: $array = array(0, 1, 2, 3, 4, 5, 6); foreach ($array as $index=>&$value) { if ( ($index+1)< count($array) ) { $array[$index+1] += $value; } echo $value." "; } echo ""; foreach ($array as $index=>&$value) {//<- assign $value by reference echo $array[6]." "; } echo ""; echo $array[6]; you finally get the correct result: 0 1 3 6 10 15 21 21 21 21 21 21 21 21 21 You can test this with multiple foreach loops and get the same results. If you modify the array in the first foreach loop, then use an assign $value by reference in the next 9 foreach loops to get the correct values (without modifying the array), and then in the 10th foreach loop you don't use an assign $value by reference (without modifying the array), the array becomes corrupted. I sort of understand the need to assign the $value by reference in the first loop, but why is it also required in every subsequent loop where the array is not being modified? Especially since all the examples in the manual show it's not needed? It would appear that once you've modified an array's elements in a foreach loop you always have to assign $value by reference in any subsequent foreach loop using that array. And if you don't, not only will you get the wrong results but the array itself is actually altered, even if there's no code in the loop to alter it. Is that correct or is it a bug? At what stage can you start using the array in the "normal" way again? That could create hair-pulling havoc for anyone maintaining code if they haven't noticed that somewhere previously there was code that modified the array in a foreach loop. Maybe the answer is to always assign $value by reference in a foreach loop regardless of what you do in that loop, but I'm not sure what the implications are. Here's how you should do it (IMHO) to avoid all sorts of side effects, magic behaviour, and unnecessary complications: Cheers, Rob. -- E-Mail Disclaimer: Information contained in this message and any attached documents is considered confidential and legal