Backward incompatible changes

Changes to error and exception handling

Many fatal and recoverable fatal errors have been converted to exceptions in
PHP 7. These error exceptions inherit from the Error
class, which itself implements the Throwable
interface (the new base interface all exceptions inherit).

This means that custom error handlers may no longer be triggered because
exceptions may be thrown instead (causing new fatal errors for uncaught
Error exceptions).

A fuller description of how errors operate in PHP 7 can be found
on the PHP 7 errors page. This
migration guide will merely enumerate the changes that affect backward
compatibility.

If the handler needs to work on both PHP 5 and 7, you should remove the
type declaration from the handler, while code that is being migrated to
work on PHP 7 exclusively can simply replace the
Exception type declaration with
Throwable instead.

Internal constructors always throw exceptions on failure

Previously, some internal classes would return NULL or an unusable object
when the constructor failed. All internal classes will now throw an
Exception in this case in the same way that user
classes already had to.

Parser errors now throw a ParseError object. Error
handling for eval() should now include a catch block
that can handle this error.

E_STRICT notices severity changes

All of the E_STRICT notices have been reclassified to
other levels. E_STRICT constant is retained, so calls like
error_reporting(E_ALL|E_STRICT) will not cause an error.

E_STRICT notices severity changes

Situation

New level/behaviour

Indexing by a resource

E_NOTICE

Abstract static methods

Notice removed, triggers no error

"Redefining" a constructor

Notice removed, triggers no error

Signature mismatch during inheritance

E_WARNING

Same (compatible) property in two used traits

Notice removed, triggers no error

Accessing static property non-statically

E_NOTICE

Only variables should be assigned by reference

E_NOTICE

Only variables should be passed by reference

E_NOTICE

Calling non-static methods statically

E_DEPRECATED

Changes to variable handling

PHP 7 now uses an abstract syntax tree when parsing source files. This has
permitted many improvements to the language which were previously
impossible due to limitations in the parser used in earlier versions of
PHP, but has resulted in the removal of a few special cases for consistency
reasons, which has resulted in backward compatibility breaks. These cases
are detailed in this section.

Changes to the handling of indirect variables, properties, and methods

Indirect access to variables, properties, and methods will now be
evaluated strictly in left-to-right order, as opposed to the previous mix
of special cases. The table below shows how the order of evaluation has
changed.

Old and new evaluation of indirect expressions

Expression

PHP 5 interpretation

PHP 7 interpretation

$$foo['bar']['baz']

${$foo['bar']['baz']}

($$foo)['bar']['baz']

$foo->$bar['baz']

$foo->{$bar['baz']}

($foo->$bar)['baz']

$foo->$bar['baz']()

$foo->{$bar['baz']}()

($foo->$bar)['baz']()

Foo::$bar['baz']()

Foo::{$bar['baz']}()

(Foo::$bar)['baz']()

Code that used the old right-to-left evaluation order must be rewritten to
explicitly use that evaluation order with curly braces (see the above
middle column). This will make the code both forwards compatible with PHP
7.x and backwards compatible with PHP 5.x.

list() will now assign values to variables in the
order they are defined, rather than reverse order. In general, this only
affects the case where list() is being used in
conjunction with the array [] operator, as shown below:

<?phplist($a[], $a[], $a[]) = [1, 2, 3];var_dump($a);?>

Output of the above example in PHP 5:

array(3) {
[0]=>
int(3)
[1]=>
int(2)
[2]=>
int(1)
}

Output of the above example in PHP 7:

array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}

In general, it is recommended not to rely on the order in which
list() assignments occur, as this is an implementation
detail that may change again in the future.

When used in the default by-value mode, foreach will now operate on a
copy of the array being iterated rather than the array itself. This means
that changes to the array made during iteration will not affect the values
that are iterated.

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:

Out of range bitshifts

Bitwise shifts (in either direction) beyond the bit width of an
integer will always result in 0. Previously, the behaviour of
such shifts was architecture dependent.

Changes to Division By Zero

Previously, when 0 was used as the divisor for either the divide (/) or
modulus (%) operators, an E_WARNING would be emitted and
false would be returned. Now, the divide operator
returns a float as either +INF, -INF, or NAN, as specified by IEEE 754. The modulus operator E_WARNING
has been removed and will throw a DivisionByZeroError
exception.

<?phpvar_dump(3/0);var_dump(0/0);var_dump(0%0);?>

Output of the above example in PHP 5:

Warning: Division by zero in %s on line %d
bool(false)
Warning: Division by zero in %s on line %d
bool(false)
Warning: Division by zero in %s on line %d
bool(false)

Output of the above example in PHP 7:

Warning: Division by zero in %s on line %d
float(INF)
Warning: Division by zero in %s on line %d
float(NAN)
PHP Fatal error: Uncaught DivisionByZeroError: Modulo by zero in %s line %d

\u{ may cause errors

Due to the addition of the new
Unicode codepoint escape syntax,
strings containing a literal \u{ followed by an invalid
sequence will cause a fatal error. To avoid this, the leading backslash
should be escaped.

ASP and script PHP tags removed

Support for using ASP and script tags to delimit PHP code has been removed.
The affected tags are:

Removed ASP and script tags

Opening tag

Closing tag

<%

%>

<%=

%>

<script language="php">

</script>

Calls from incompatible context removed

Previously deprecated in PHP 5.6,
static calls made to a non-static method with an incompatible context will
now result in the called method having an undefined
$this variable and a deprecation warning being issued.

# comments in INI files removed

Support for prefixing comments with # in INI files has been
removed. ; (semi-colon) should be used instead. This change
applies to php.ini, as well as files handled by
parse_ini_file() and parse_ini_string().

JSON extension replaced with JSOND

The JSON extension has been replaced with JSOND, causing three minor BC
breaks. Firstly, a number must not end in a decimal point (i.e.
34. must be changed to either 34.0 or
34). Secondly, when using scientific notation, the
e exponent must not immediately follow a decimal point
(i.e. 3.e3 must be changed to either
3.0e3 or 3e3).
Finally, an empty string is no longer considered valid JSON.

Internal function failure on overflow

Previously, internal functions would silently truncate numbers produced from
float-to-integer coercions when the float was too large to represent as an
integer. Now, an E_WARNING will be emitted and NULL will be returned.

Fixes to custom session handler return values

Any predicate functions implemented by custom session handlers that return
either FALSE or -1 will be fatal errors. If any value
from these functions other than a boolean, -1, or
0 is returned, then it will fail and an E_WARNING will be
emitted.

Sort order of equal elements

The internal sorting algorithm has been improved, what may result in
different sort order of elements, which compare as equal, than before.

Note:

Don't rely on the order of elements which compare as equal; it might change
anytime.

Although $x/0 is technically not infinity in a purely mathematical sense, when you understand why the IEEE float includes a value for infinity, and returns infinity in this case, it makes sense that PHP would agree with this.

The reason is that programmers don't usually divide by 0 on purpose. A value of 0 as a divisor usually occurs due to underflow, i.e. a value which is too small to be represented as a float. So, for example, if you have values like:$x = 1;$y = 1e-15 * 1e-15;$z = $x/$y;Because $y will have underflowed to 0, the division operation will throw the division by zero warning, and $z will be set to INF. In a better computer, however, $y would not have the value 0 (it would be 1e-30) and $z would not have the value INF (it would be 1e30).

In other words, 0 is not only representative of an actual 0, but also a very small number which float cannot represent correctly (underflow), and INF does not only represent infinity, but also a very big number which float cannot represent correctly (overflow). We do the best we can within the limitations of floating point values, which are really just good approximations of the actual numbers being represented.

What does bother me is that division by zero is handled in two different ways depending on the operator. I would have preferred the new DivisionByZeroError exception to be thrown in all cases, for consistency and to enforce good programming practices.

NOTE: the new variable handling in PHP 7 also has side effects on the COM .NET extension. Numeric variants (in the Windows application space) now must be quoted when passed over from PHP. This is the case when the receiving application expects a variable of type variant.

Here is an example:

<?php $word = new COM('Word.Application');

// now load a document, ...

// the following works in PHP 5 but will throw an exception in PHP 7$word->ActiveDocument->PrintOut(false, false, 0, $outfile);

// the following works in PHP 7 as well, please note the quotes around the '0'$word->ActiveDocument->PrintOut(false, false, '0', $outfile);?>

When used in the default by-value mode, foreach will now operate on a copy of the array being iterated rather than the array itself. This means that changes to the array made during iteration will not affect the values that are iterated."

Please note that this is not exactly true. New foreach operates on a copy of the array, by-value or by-reference. It seems that in the latter case, the array copy is simply moved over (to) the original array before it is presumably destroyed.

As a consequence of this, you may not "dereference" an array containing values - e.g. for use with ReflectionMethod::invokeArgs() or the good ole' call_user_func().Consider the snippet below:

<?phpfunction deref(Array $inputArray){$retVal = [];

foreach ($inputArray as &$inputValue) {$retVal[] = $inputValue; }

return $retVal;}?>

As of PHP 7.0, this *will no longer work*. You will get the usual suspect:

PHP Warning: Parameter n to whatever() expected to be a reference, value given in baz.php on line x

You need to convert it to explicitly reference the original array:<?phpfunction deref(Array $inputArray){$retVal = [];

[Editor's Note: that change is listed in the "Changed functions" section.]

The substr function has also been changed in a backward incompatible way.

<?php
substr("test",4); # false in PHP 5, "" in PHP 7
?>

In fact, this is the only thing we had to change in a number of places for our code base to run on PHP 7. It's definitely an improvement though, as the old behavior tended to cause bugs in border cases.

In the section captioned "Changes to the handling of indirect variables, properties, and methods" there are parentheses used in the table directly beneath "PHP 7 interpretation."

The parentheses are intended to show the evaluation order, but they are not part of the syntax, and should not be used in the variable definition or reference. This juxtaposition confused one of my colleagues; hopefully this note will save someone else some time.

Examples of the correct curly-brace syntax is further down the page, in the section captioned "global only accepts simple variables."