Arrays
foreach
One of the most commonly used ways to iterate over an array in
PHP is using the foreach
statement. Up to version 7,
however, the behavior of foreach
has been a bit
unexpected in some edge cases. This has changed in PHP 7:
PHP 7 will not affect the internal pointer when iterating with
foreach
When adding additional elements to an array while iterating over it, the new elements will now be iterated over
Multiple iterations over the same array with
foreach
now work independently of each otherUsing
array_pop()
,array_push()
,array_shift()
, orarray_unshift()
on arrays while iterating over them by reference now works
We would not expect your code to rely on PHP’s behavior in any of the above edge cases, but if it does, then you need to adjust the code before migrating to PHP 7.
Let us look at some examples:
$array = [0, 1, 2];
foreach ($array as &$value) {
var_dump(current($array));
}
Executed with PHP 5, the above code would output:
int(1)
int(2)
bool(false)
Executed with PHP 7, the output will be:
int(0)
int(0)
int(0)
As you can see, foreach
no longer changes the
internal array pointer in PHP 7.
When iterating by-reference, foreach will now do a better job of tracking changes to the array made during iteration. For example, appending to an array while iterating will now result in the appended values being iterated over as well:
$array = [0];
foreach ($array as &$val) {
var_dump($val);
$array[1] = 1;
}
This example, executed with PHP 5, would output:
int(0)
PHP 7, will iterate over both values:
int(0)
int(1)
When you do not use the reference operator &
,
PHP will operate on a copy of the array, so any changes made to the
(original) array will not affect the values that are iterated.
When iterating over a non-traversable object, the behavior will be the same as when iterating array by reference. This means that properties that are added while iterating will also be iterated over. When properties that have not yet been iterated over are removed, they will not be iterated over anymore.
Invalid
list()
Usage
The list()
function can be used to easily
dereference various elements of an array into individual variables.
To make this feature more robust, various use cases got removed.
Starting with PHP 7, the following examples are all invalid and do
no longer work:
list() = [1,2,3];
list(,,) = [1,2,3];
list($a, list(), $b) = [1,2,3];
Support for unpacking strings using list also got removed:
list($a, $b) = "ab";
Executing the above sample will now result in $a
and
$b
to be null
, rather than ‘a’ and
‘b’.
Parsing Invalid Octal Numbers
Integer values can be specified in various representations, for instance in hexadecimal or octal form. The later are detected by PHP based on their leading zero:
$int = 0123;
var_dump($int);
var_dump()
will not output 123
but the
correct integer value 83
.
As the octal system only knows the digits 0 through 7, the following is not a valid octal representation of an integer value:
$invalid = 0191;
var_dump($invalid);
Previous versions of PHP silently discarded the invalid digit and all that follow regardless whether they were valid or not. Given the example above this behavior lead to an incorrect result:
int(1)
Starting with PHP 7, invalid octal literals now produce compile-time errors, thus the above example would no longer work:
PHP Parse error: Invalid numeric literal in ...
Changed Order
for list()
when Assigning to Array
PHP’s list()
function can be used to conveniently
unpack array elements into single variables:
list($a, $b, $c) = [1, 2, 3];
var_dump($a);
var_dump($b);
var_dump($c);
Executing the code shown above will print the output shown below:
int(1)
int(2)
int(3)
Unbeknownst to most PHP developers, before PHP 7 the assignments were done in reverse order, assigning the last value to the last variable in the list first. This could lead to unexpected behavior when new array elements are used as target variables:
list($array[], $array[], $array[]) = [1, 2, 3];
var_dump($array);
Executed with PHP 5, the output was:
array(3) {
[0] =>
int(3)
[1] =>
int(2)
[2] =>
int(1)
}
As can be seen, due to the reverse order of the assignment using
list()
the order of values within the array is
effectively reversed as well.
PHP 7 changed the order of assignment to be starting on the left, resulting in a backwards compatibility break for this use case. Consequently, when run with PHP 7, the output now looks like this:
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
If you want to be on the safe side, you might want to use
array_reverse()
to fix the element order.
[]
Operator and Empty
Strings
Prior to PHP 7.1, a variable that holds an empty string was
silently converted to an empty array when the []
operator was used on it:
$string = '';
$string[] = 'foo';
var_dump($string);
Executing the code shown above with PHP 5.6 or PHP 7.0 prints the following output:
array(1) {
[0]=>
string(3) "foo"
}
PHP 7.1 and later print the error message shown below for the same code:
Fatal error:
Uncaught Error: [] operator not supported for strings
Using the []
operator on non-empty strings results
in an error since PHP 5.6.
Invalid Array Read Access
Elements of an array
are accessed using square
bracket notation: $foo[0]
. The same syntax works for
variables of type string
to access a specific char and
objects that implement ArrayAccess
. Surprisingly
though, PHP did not have any issues with trying to apply the same
access logic on values of type int
, bool
,
float
or even null
. As this does not make
sense, PHP simply returned null
.
PHP 7.4 will now emit an E_NOTICE
error for these
invalid read accesses. While this change does not seem to qualify as
a break of backward compatibility in itself, it may have breaking
side effects in case a custom, userland error handler is in place:
these are often implemented to unconditionally translate errors into
exceptions and with that change the behavior of the code.
compact()
One of the more “interesting” functions in PHP is
compact()
. It creates an array from variable names that
are specified as strings. Clearly, compact()
dates back
to the dark times when register_global
ruled PHP world,
and should be used very rarely these days.
$a = 1;
$b = 2;
var_dump(compact('a', 'b'));
This will give you an array of $a
and
$b
, with the variable names as keys:
array(2) {
["a"] => int(1)
["b"] => int(2)
}
Up to PHP 7.3, referencing undefined variables would be silently ignored by PHP. As of PHP 7.3, a notice will be emitted:
var_dump(compact('a'));
PHP Notice:
compact(): Undefined variable: a in ...
array(0) {
}