php容易犯错的10个地方
原文地址:
http://www.toptal.com/php/10-most-common-mistakes-php-programmers-make
译文地址:http://codecloud.net/php-2056.html
Common Mistake #1: Leaving dangling array references after foreach
loops
Not sure how to use foreach loops in PHP? Using references in foreach
loops can be useful if you want to operate on each element in the array that you are iterating over. For example:
<code>$arr = array(1, 2, 3, 4);foreach ($arr as &$value) { $value = $value * 2;}// $arr is now array(2, 4, 6, 8)</code>
The problem is that, if you’re not careful, this can also have some undesirable side effects and consequences. Specifically, in the above example, after the code is executed, $value
will remain in scope and will hold a reference to the last element in the array. Subsequent operations involving $value
could therefore unintentionally end up modifying the last element in the array.
The main thing to remember is that foreach
does not create a scope. Thus, $value
in the above example is areference within the top scope of the script. On each iteration foreach
sets the reference to point to the next element of $array
. After the loop completes, therefore, $value
still points to the last element of $array
and remains in scope.
Here’s an example of the kind of evasive and confusing bugs that this can lead to:
<code>$array = [1, 2, 3];echo implode(',', $array), "\n";foreach ($array as &$value) {} // by referenceecho implode(',', $array), "\n";foreach ($array as $value) {} // by value (i.e., copy)echo implode(',', $array), "\n";</code>
The above code will output the following:
<code>1,2,31,2,31,2,2</code>
No, that’s not a typo. The last value on the last line is indeed a 2, not a 3.
Why?
After going through the first foreach
loop, $array
remains unchanged but, as explained above, $value
is left as a dangling reference to the last element in $array
(since that foreach
loop accessed $value
by reference).
As a result, when we go through the second foreach
loop, “weird stuff” appears to happen. Specifically, since $value
%本文@来源gao@!dai!ma.com搞$$代^@码!网搞代gaodaima码is now being accessed by value (i.e., by copy), foreach
copies each sequential $array
element into $value
in each step of the loop. As a result, here’s what happens during each step of the second foreach
loop:
- Pass 1: Copies
$array[0]
(i.e., “1”) into$value
(which is a reference to$array[2]
), so$array[2]
now equals 1. So$array
now contains [1, 2, 1]. - Pass 2: Copies
$array[1]
(i.e., “2”) into$value
(which is a reference to$array[2]
), so$array[2]
now equals 2. So$array
now contains [1, 2, 2]. - Pass 3: Copies
$array[2]
(which now equals “2”) into$value
(which is a reference to$array[2]
), so$array[2]
still equals 2. So$array
now contains [1, 2, 2].
To still get the benefit of using references in foreach
loops without running the risk of these kinds of problems, call unset()
on the variable, immediately after the foreach
loop, to remove the reference; e.g.:
<code>$arr = array(1, 2, 3, 4);foreach ($arr as &$value) { $value = $value * 2;}unset($value); // $value no longer references $arr[3]</code>