Jump to content

Debugging test


justsomeguy

Recommended Posts

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

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

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

Guest So Called

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

I think I have the answer. How do I use the spoiler?

Link to comment
Share on other sites

Guest So Called

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

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.
  • Like 1
Link to comment
Share on other sites

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

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

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 by Don E
Link to comment
Share on other sites

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

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

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 by birbal
Link to comment
Share on other sites

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;    }  }

  • Like 1
Link to comment
Share on other sites

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 by niche
Link to comment
Share on other sites

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).

  • Like 1
Link to comment
Share on other sites

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 by niche
Link to comment
Share on other sites

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

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.

  • Like 1
Link to comment
Share on other sites

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 by niche
Link to comment
Share on other sites

Guest So Called

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

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

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

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...