As an alternative to checking if the current item is the last one (which the other answers show), you could use array_slice() to get the start of the array to loop over and then end() to get the last element of the array.
$data = [/*...*/]
foreach ($item as array_splice($data, 0, -1, true) {
$item->foo();
}
if (($item = end($data) !== false) {
$item->bar();
}
In my opinion, this code is easier to read (and metrics like cyclomatic complexity agree) than the nested if $item === end($data)
check. If the same is true on your specific case will depend on what, exactly is in the loop and how much of it is different.
In addition, if your array is large, this approach may offer (slightly) better performance (but if your array is large and a small performance difference is important, don't take my word for this - benchmark both solutions with read data).