查询foreach内部是否可以?

I searched a lot in the Stack/Google and didn't find a proper answer to my question, so here I go:

I know that using some calculations and stuff in for is utterly bad practice like:

for ($i = 0; $i < count($items); $i++) 

Recently I saw a strange practice in the code that I'm working:

foreach (SomeModel::find() as $item)

It's ok to put the find directly in the foreach, like a count($items) or sort of? I don't like the idea, so instinctively I avoid that putting in some var, but now my task is improve/review the code and I want to put a end in my ignorance about that, someone can clarify me if it's acceptable that implementation and why?

I know that foreach is an intelligent implementation, just don't know if this structure do that kind of optimization.

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

If you have an for loop, the condition is checked in every iteration. So instead of just comparing a var against an value, you have to call an function (count) in every iteration. So moving the count condition before the loop makes a difference

$count = count($items);
for ($i = 0; $i < $count; ++$i) { ... }

foreach behaves different. Instead of evaluating SomeModel::find() in every iteration, this part is only evaluated once and saved just for the loop. You could also save the result of SomeModel::find() in an var and use that var for the foreach loop, but that doesn't make an difference. If you use the var only for the loop, the compiler should do exactly the same.

But keep in mind. That kind of optimization should be part of the compiler and the timing benefits should be near to 0, if you only have a few iterations.