In contrast to for()
, where each instruction is evaluated in each iteration, foreach()
gets the array
at the beginning, and then moves the pointer to iterate through it:
The first form loops over the array given by array_expression. On each iteration, the value of the current element is assigned to $value and the internal array pointer is advanced by one (so on the next iteration, you'll be looking at the next element).
Emphasis mine
So your function gets called just once. You can see this behavior looking at the opcodes.
Without assignment:
<?php
function find() {
return [1, 2, 3, 4, 5, 6, 7, 8];
}
foreach (find() as $n) {
echo $n.PHP_EOL;
}
Opcodes:
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
3 0 E > NOP
7 1 INIT_FCALL 'find'
2 DO_FCALL 0 $1
3 > FE_RESET_R $2 $1, ->8
4 > > FE_FETCH_R $2, !0, ->8
8 5 > CONCAT ~3 !0, '%0A'
6 ECHO ~3
7 > JMP ->4
8 > FE_FREE $2
9 9 > RETURN 1
And with assignment:
<?php
function find() {
return [1, 2, 3, 4, 5, 6, 7, 8];
}
$f = find();
foreach ($f as $n) {
echo $n.PHP_EOL;
}
Opcodes:
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
3 0 E > NOP
6 1 INIT_FCALL 'find'
2 DO_FCALL 0 $2
3 ASSIGN !0, $2
7 4 > FE_RESET_R $4 !0, ->9
5 > > FE_FETCH_R $4, !1, ->9
8 6 > CONCAT ~5 !1, '%0A'
7 ECHO ~5
8 > JMP ->5
9 > FE_FREE $4
9 10 > RETURN 1
As you can see, the only difference is the ASSIGN
instruction, which is when the assignment to $f
is made.
So this is mostly a readability issue. In my opinion, it's a bit more readable to assign the return of the method in a variable with an appropriate name. Uncle Bob, however, states the contrary in Clean Code:
Control variables for loops should usually be declared within the loop statement, as in this cute little function from the same source.
public int countTestCases() {
int count= 0;
for (Test each : tests)
count += each.countTestCases();
return count;
}