justsomeguy Posted May 30, 2012 Share Posted May 30, 2012 If you want to check your knowledge of debugging and PHP, see how long it takes you to explain why this happens: <?php $ar = array(strval(1.1), strval(2.1));echo print_r($ar, true) . '<br>'; foreach ($ar as &$v) $v = intval($v);echo print_r($ar, true) . '<br>'; for ($i = 0; $i < count($ar); $i++) echo $ar[$i] . ' ';echo '<br>';foreach ($ar as $v) echo $v . ' ';echo '<br>'; print_r($ar);?> See if you can explain why the array starts with the string values '1.1', '2.1', and ends up with the integer values 1, 1. Don't just point out how you can fix it, try to explain why it happens. Here's the output: Array ( [0] => 1.1 [1] => 2.1 )Array ( [0] => 1 [1] => 2 )1 21 1Array ( [0] => 1 [1] => 1 ) If you can do it in less than 15 minutes, then you're faster than me at the end of my work day. If you have a solution, please use the spoiler tag to not spoil it for others. Link to comment Share on other sites More sharing options...
Ingolme Posted May 30, 2012 Share Posted May 30, 2012 Alright, got it. The $v variable is still pointing to the second element of the array. The "as" operator then assigned the first value of the array to $v which, since it's a reference, overwrote the last value of the array with its first value. Link to comment Share on other sites More sharing options...
niche Posted May 30, 2012 Share Posted May 30, 2012 Can you say what the ampersand is doing in the foreach? If not, can you show how to google for it? Link to comment Share on other sites More sharing options...
Ingolme Posted May 30, 2012 Share Posted May 30, 2012 Here's an explanation of the ampersand: http://es2.php.net/m...nces.whatdo.php Link to comment Share on other sites More sharing options...
Guest So Called Posted May 30, 2012 Share Posted May 30, 2012 I don't have the patience and focus to try it now but I think you might be spoiling it by discussing ... um, (I was just about to spoil it myself)... what you're discussing. I'd like to try JSG's challenge tomorrow when I'm fresh and awake. (It's currently dinner time in my locale.) Link to comment Share on other sites More sharing options...
niche Posted May 30, 2012 Share Posted May 30, 2012 I think I have the answer. How do I use the spoiler? Link to comment Share on other sites More sharing options...
Guest So Called Posted May 30, 2012 Share Posted May 30, 2012 I couldn't find it either on IP.Board's help system. I think it's just like SPOILER AND /SPOILER except with brackets like most bb codes. Link to comment Share on other sites More sharing options...
Ingolme Posted May 30, 2012 Share Posted May 30, 2012 I think I have the answer. How do I use the spoiler?The third buton from the left on the quick reply says "special BBcode". Click on it and select "spoiler" from the dropdown list. 1 Link to comment Share on other sites More sharing options...
justsomeguy Posted May 30, 2012 Author Share Posted May 30, 2012 Can you say what the ampersand is doing in the foreach? If not, can you show how to google for it?Like with any other question about operators in PHP, you start in the manual in the section on operators and look for it. http://www.php.net/m...e.operators.php You'll find it in the operator precedence table, and if you click through the table of contents it's also in the assignment and bitwise operator sections. Only one of those uses applies to this example. Link to comment Share on other sites More sharing options...
niche Posted May 30, 2012 Share Posted May 30, 2012 Sorry, but can't find the quick reply button. I thought I had one. I've clicked, searched and hovered everything I can find. Is it possible it's disabled somehow? Link to comment Share on other sites More sharing options...
Don E Posted May 30, 2012 Share Posted May 30, 2012 (edited) Going to take a stab at it... Edit: After further testing, it looks like whatever value is in the first element in the array( at index 0 ), that value is being stored at index 1 as well. foreach ($ar as &$v) $v = intval($v); I think it's because the first element(0) is being referenced via &$v because of the foreach AS.. Because of this, $v is going to point to the first value in the array, 1 ,which is at index/element 0. So since $v points to the value 1 at index 0, in the foreach loop above, the index after, it's value is going to be set to the value of what $v points to, which is 1. Hopefully I'm close. heh Edited May 30, 2012 by Don E Link to comment Share on other sites More sharing options...
TheGallery Posted May 30, 2012 Share Posted May 30, 2012 I wasn't familiar with the assignment by reference operator (i've seen it before but i've never used it so i forgot about it) so after reading the php documentation it took me a while to realize what's going on. So i run into the foreach documention and there was a reference about this issue. Now, i know i didn't find the solution using logic (as i said i haven't used the operator before and i didn't know how it functions) but i took a lesson today. Thanks for the test. Link to comment Share on other sites More sharing options...
niche Posted May 30, 2012 Share Posted May 30, 2012 The foreach loops obviously have a reference problem per hlavac's 09-Oct-2007 post at http://es2.php.net/manual/en/language.references.whatdo.php#language.references.whatdo.assign However, I can't explain the result of the for loop. Even a var_dump() didn't shed light. Please explain it justsomeguy. It isn't behaving at all the way I expect. Changing it to: for ($i = 0; $i < count($ar); $i++) $x = $ar[$i]; echo $x . ' ';echo '<br>'; Added to my mystery. Could it be a bug? Link to comment Share on other sites More sharing options...
birbal Posted May 30, 2012 Share Posted May 30, 2012 (edited) foreach ($ar as &$v) $v = intval($v); $v here is referenced so it will make changes directly to array element as $v is pointed to each of array index on each iteration. after array has been itertared the reference will be still there in $v will point to $ar[1] which value is 2 ($v=$ar[1]=2) foreach ($ar as $v) echo $v . ' ' here when foreach loop through array it assigns index value to $v first on each iteraation and then move the internal pointer.so 1st index of array ($ar[0] which value is 1) is assigned to $v. but as $v is referencing to the second index as described previously so the changes will happend in 2nd index $ar[1]. which means $ar[1] will be 1. after that it will iterate second time the loop. this time $ar[1] value will be assigned to $v. but in previous iteration $ar[1] value has been changed to 1 so it will now return 1 and will be assigned to $v which will again write $ar[1] same way as previous iteration and will made change to $ar[1] and it will come out from the loop. solution: unset() the $v or using different variable in last foreach loop will solve it edit: $v = intval($v); when intval is used the left most character if a digit it will return that left most value if it is not then it will return 0.so that 1.1 becomes 1 and 2.1 becomes 2 Edited May 30, 2012 by birbal Link to comment Share on other sites More sharing options...
justsomeguy Posted May 30, 2012 Author Share Posted May 30, 2012 Ingolme and Birbal have it right if people want to see what the issue is. The thing that caught me by surprise is that when the second foreach loop executes, it does not redefine the variable $v, it simply copies each value in the array to that existing variable. I had expected the variable to be defined as a new variable and then get the values from the array. The string casts and the for loop I added just to make the example a little bit more complex, to try and throw you off. There was additional code between the 2 foreach loops in my code, so I wanted to add something there to make it less obvious. This is part of the original code I was debugging: foreach($cids as &$c) $c = intval($c); $cids = array_unique(array_filter($cids)); // clear assignment cache $db->sql('DELETE FROM assignment_cache WHERE uid=%d'); $db->add_param($uid, false); $db->delete(); // get previously assigned content for this user $db->sql('SELECT cid FROM assigned_dates WHERE uid=%d'); $db->add_param($uid, false); $assigned_dates = $db->select(); foreach ($assigned_dates as &$v) { $v = $v['cid']; } $now = time(); $assign_cache = array(); foreach ($cids as $c) { if (!in_array($c, $assign_cache)) { $db->insert('assignment_cache', array('uid' => $uid, 'cid' => $c)); $assign_cache[] = $c; } } 1 Link to comment Share on other sites More sharing options...
niche Posted May 30, 2012 Share Posted May 30, 2012 (edited) By the numbers then, in Ingolme's #2, the first value is always the reference, when the ampersand is used, to pass a series of values? If so, why does $v echo as "2" after the first foreach? Per Ingolme #2, hasn't $v been re-assigned by "as" to the value of the reference, in this case "1"? Edited May 30, 2012 by niche Link to comment Share on other sites More sharing options...
justsomeguy Posted May 30, 2012 Author Share Posted May 30, 2012 If so, why does $v echo as "2" after the first foreach? After the first foreach $v is set to be a reference to the last element in the array. After the first foreach, when you change $v, you also change the last element in the array because that is what $v was left at after the first foreach ended. The last element in the array gets overwritten the first time the next foreach loop executes, when it assigns the first value in the array to $v. Since $v is a reference to the last element, it assigns the value of the first element to the last element. If the array had more than 2 elements, the last element in the array would end up with the value of the previous element (e.g., element 5 would be set to the value of element 4). 1 Link to comment Share on other sites More sharing options...
niche Posted May 30, 2012 Share Posted May 30, 2012 (edited) That was a great explanation jsg. I understand. Thanks Now, what about the for loop? Please explain how Array ( [0] => 1 [1] => 2 ) gets turned into this by the for loop? I haven't used for loops much, but I expected something different than: 1 21 1 EDIT: It's like it ran twice. once without the ref and once with, but the second foreach hasn't run. Edited May 30, 2012 by niche Link to comment Share on other sites More sharing options...
Don E Posted May 30, 2012 Share Posted May 30, 2012 JSG, Check my work/explanation here and let me know if it's correct. I made my own version for testing it. So after the first foreach, when coming out of the loop, $key will bevalue 4. When the next foreach runs, this: $num as $key overwrites $key which was equaled to4 and is now set to equal the first value in the array, which is 5. So now since$key is set to 5, you'll get 5 5 output. I made a quick example of your code to a more understanding one: $num = array(5.1, 4.1);foreach($num as &$key){ $key = intval($key); //intval returns leftmost integer thus removing .1}//echo $key; // this would display 4. So at this point before going into the next foreach it is 4.foreach ($num as $key) //Here $key with a value of 4(which is referencing the last value in the above array because of the loop, will be overwritten to the first index value in the array, which is 5{ echo $key . ' '; // Since $key equals 5 now and there's two index elements in this array, you should get 5 5 }Output: 5 5 Correct? Link to comment Share on other sites More sharing options...
justsomeguy Posted May 30, 2012 Author Share Posted May 30, 2012 The for loop doesn't have any effect in this example, I added it in just for filler. The same issue happens here: <?php$ar = array('1', '2');echo print_r($ar, true) . '<br>';foreach ($ar as &$v) $v = intval($v);echo print_r($ar, true) . '<br>';foreach ($ar as $v) echo $v . ' ';echo '<br>';print_r($ar);?> The issue doesn't occur until the second foreach loop runs. The first foreach loop and the for loop don't change the array. Check my work/explanation here and let me know if it's correct. I made my own version for testing it.It's closer, but you're missing a key point: You said: So after the first foreach, when coming out of the loop, $key will be value 4. This isn't correct. $key is not set to a value, $key is a reference to another variable. If you think of variables as representing addresses in memory, when you create a variable and assign a value to it then you're storing that value in a certain memory address which the variable points to. The actual variable may only hold the memory address, so when it gets the value it checks which address in memory the variable points to, goes there, and gets the value stored there. This isn't technically true for PHP, PHP abstracts a lot of that behavior, but it's true for a language like C. Now, when you do this: $a = &$b; You're not setting the value of $a to the value of $b. You're setting the address that $a points to to be the same address that $b points to. They literally point to the same thing. If you change the value of $a, the value of $b also changes and vice-versa. So after that first foreach loop runs, $v is set to the same address as the last value in the array. When you change $v, you change the last value in the array. So in the second foreach loop, the first time it runs through and sets the first value in the array to $v, it has the side-effect of also setting the last element in the array to the same value because that's where $v is pointing. That took me by surprise because I thought that PHP would use a new variable for that second loop, where it would unset the variable each time before setting a new value, instead of just using a variable that already exists and, in this case, is a reference to another value. 1 Link to comment Share on other sites More sharing options...
niche Posted May 30, 2012 Share Posted May 30, 2012 (edited) Viola! For some reason the echo inside the second foreach didn't register and I thought it was coming from the for loop. I can't say for a fact I would've made the same mistake if I'd worked the problem earlier in the day. Any way, OUTSTANDING EXERCISE! We can't have too many "go find it in the manual" opportunities and I love the spoiler thing.. I think something like this would be great every two or three weeks. Hopefully, there would be some unifying theme for consecutive exercises and only moderators would initiate the exercises with suggestions from the membership. Bravo justsomeguy! Edited May 30, 2012 by niche Link to comment Share on other sites More sharing options...
Don E Posted May 30, 2012 Share Posted May 30, 2012 I agree with niche.Thanks JSG for that detailed explanation/clarification. Link to comment Share on other sites More sharing options...
Guest So Called Posted May 30, 2012 Share Posted May 30, 2012 It's amusing to think how complicated and misdirecting code could be if the author intended it to be confusing. You could even write self-modifying code that changes on the fly as it executes. I've seen a lot of code written by others and I've sometimes wondered if the author made things deliberately obscure and confusing, as some sort of attempt at job security, perhaps believing that if it was too complicated for somebody else to fix then the author would have a guaranteed job. Link to comment Share on other sites More sharing options...
boen_robot Posted May 31, 2012 Share Posted May 31, 2012 Interesting exercise indeed... to avoid similar problems, I tend to recall PHP's scoping rule whenever I introduce a new (pseudo) variable into a large piece of code. The rule is simpler than in most other languages, and yet is the source of much confusion, since it's different (and this is also a hint for people who haven't looked at the spoilers yet):All variables within a function/method share the same scope, and variables outside of a function are all in the same scope. Constructs like for, foreach, if, etc. are not adding their own scope.Combine that with references (which I almost get a panic attack from, every time I see one in PHP, because you just know this will be a problem, if it's not already a problem), and there you have it. Link to comment Share on other sites More sharing options...
Don E Posted June 1, 2012 Share Posted June 1, 2012 In light of JSG exercise post that began this thread, here's one for the fun of it that I saw somewhere once:What is the output of the following PHP script? <?php define('VALUE', 0); if(strlen(VALUE) > 0 ) { echo 'Hello'; } else { echo 'Goodbye'; } ?> I'm sure you PHP veterans will get this one with ease. If you know the answer, do the spoiler thing. BUT, be honest with your answer and post your answer first before testing the above. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now