不向后兼容的变更

错误和异常处理相关的变更

在 PHP 7 中,很多致命错误以及可恢复的致命错误,都被转换为异常来处理了。 这些异常继承自 Error 类,此类实现了 Throwable 接口 (所有异常都实现了这个基础接口)。

这也意味着,当发生错误的时候,以前代码中的一些错误处理的代码将无法被触发。 因为在 PHP 7 版本中,已经使用抛出异常的错误处理机制了。 (如果代码中没有捕获 Error 异常,那么会引发致命错误)。

PHP 7 中的错误处理的更完整的描述,请参见 PHP 7 错误处理。 本迁移指导主要是列出对兼容性有影响的变更。

当内部构造器失败的时候,总是抛出异常

在之前版本中,如果内部类的构造器出错,会返回 NULL 或者一个不可用的对象。 从 PHP 7 开始,如果内部类构造器发生错误, 那么会抛出异常。

解析错误会抛出 ParseError 异常

解析错误会抛出 ParseError 异常。 对于 eval() 函数,需要将其包含到一个 catch 代码块中来处理解析错误。

E_STRICT 警告级别变更

原有的 E_STRICT 警告都被迁移到其他级别。 E_STRICT 常量会被保留,所以调用 error_reporting(E_ALL|E_STRICT) 不会引发错误。

E_STRICT 警告级别变更
场景 新的级别/行为
将资源类型的变量用作键来进行索引 E_NOTICE
抽象静态方法 不再警告,会引发错误
重复定义构造器函数 不再警告,会引发错误
在继承的时候,方法签名不匹配 E_WARNING
在两个 trait 中包含相同的(兼容的)属性 不再警告,会引发错误
以非静态调用的方式访问静态属性 E_NOTICE
变量应该以引用的方式赋值 E_NOTICE
变量应该以引用的方式传递(到函数参数中) E_NOTICE
以静态方式调用实例方法 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.

Changes to list() handling

list() no longer assigns variables in reverse order

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:

<?php
list($a[], $a[], $a[]) = [123];
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.

Empty list() assignments have been removed

list() constructs can no longer be empty. The following are no longer allowed:

<?php
list() = $a;
list(,,) = 
$a;
list(
$x, list(), $y) = $a;
?>
list() cannot unpack strings

list() can no longer unpack string variables. str_split() should be used instead.

Array ordering when elements are automatically created during by reference assignments has changed

The order of the elements in an array has changed when those elements have been automatically created by referencing them in a by reference assignment. For example:

<?php
$array 
= [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);
?>

Output of the above example in PHP 5:

array(2) {
  ["b"]=>
  &int(1)
  ["a"]=>
  &int(1)
}

Output of the above example in PHP 7:

array(2) {
  ["a"]=>
  &int(1)
  ["b"]=>
  &int(1)
}

global only accepts simple variables

Variable variables can no longer be used with the global keyword. The curly brace syntax can be used to emulate the previous behaviour if required:

<?php
function f() {
    
// Valid in PHP 5 only.
    
global $$foo->bar;

    
// Valid in PHP 5 and 7.
    
global ${$foo->bar};
}
?>

As a general principle, using anything other than a bare variable with global is discouraged.

Parentheses around function arguments no longer affect behaviour

In PHP 5, using redundant parentheses around a function argument could quiet strict standards warnings when the function argument was passed by reference. The warning will now always be issued.

<?php
function getArray() {
    return [
123];
}

function 
squareArray(array &$a) {
    foreach (
$a as &$v) {
        
$v **= 2;
    }
}

// Generates a warning in PHP 7.
squareArray((getArray()));
?>

以上例程会输出:

Notice: Only variables should be passed by reference in /tmp/test.php on line 13

foreach的变化

foreach发生了细微的变化,控制结构, 主要围绕阵列的内部数组指针和迭代处理的修改。

foreach不再改变内部数组指针

在PHP7之前,当数组通过 foreach 迭代时,数组指针会移动。现在开始,不再如此,见下面代码

<?php
$array 
= [012];
foreach (
$array as &$val) {
    
var_dump(current($array));
}
?>

Output of the above example in PHP 5:

int(1)
int(2)
bool(false)

Output of the above example in PHP 7:

int(0)
int(0)
int(0)

foreach 通过值遍历时,操作的值为数组的副本

当默认使用通过值遍历数组时,foreach 实际操作的是数组的迭代副本,而非数组本身。这就意味着,foreach 中的操作不会修改原数组的值。

foreach通过引用遍历时,有更好的迭代特性

当使用引用遍历数组时,现在 foreach 在迭代中能更好的跟踪变化。例如,在迭代中添加一个迭代值到数组中,参考下面的代码:

<?php
$array 
= [0];
foreach (
$array as &$val) {
    
var_dump($val);
    
$array[1] = 1;
}
?>

Output of the above example in PHP 5:

int(0)

Output of the above example in PHP 7:

int(0)
int(1)

Traversable 对象的遍历

迭代一个非Traversable对象将会与迭代一个引用数组的行为相同。 这将导致在对象添加或删除属性时,foreach通过引用遍历时,有更好的迭代特性也能被应用

Changes to integer handling

Invalid octal literals

Previously, octal literals that contained invalid numbers were silently truncated (0128 was taken as 012). Now, an invalid octal literal will cause a parse error.

Negative bitshifts

Bitwise shifts by negative numbers will now throw an ArithmeticError:

<?php
var_dump
(>> -1);
?>

Output of the above example in PHP 5:

int(0)

Output of the above example in PHP 7:

Fatal error: Uncaught ArithmeticError: Bit shift by negative number in /tmp/test.php:2
Stack trace:
#0 {main}
  thrown in /tmp/test.php on line 2

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.

<?php
var_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

string处理上的调整

十六进制字符串不再被认为是数字

含十六进制字符串不再被认为是数字。例如:

<?php
var_dump
("0x123" == "291");
var_dump(is_numeric("0x123"));
var_dump("0xe" "0x1");
var_dump(substr("foo""0x1"));
?>

Output of the above example in PHP 5:

bool(true)
bool(true)
int(15)
string(2) "oo"

Output of the above example in PHP 7:

bool(false)
bool(false)
int(0)

Notice: A non well formed numeric value encountered in /tmp/test.php on line 5
string(3) "foo"

filter_var() 函数可以用于检查一个 string 是否含有十六进制数字,并将其转换为integer:

<?php
$str 
"0xffff";
$int filter_var($strFILTER_VALIDATE_INTFILTER_FLAG_ALLOW_HEX);
if (
false === $int) {
    throw new 
Exception("Invalid integer!");
}
var_dump($int); // int(65535)
?>

\u{ 可能引起错误

由于新的 Unicode codepoint escape syntax语法, 紧连着无效序列并包含\u{ 的字串可能引起致命错误。 为了避免这一报错,应该避免反斜杠开头。

被移除的函数(Removed functions)

call_user_method() and call_user_method_array()

这两个函数从PHP 4.1.0开始被废弃,应该使用call_user_func()call_user_func_array()。 你也可以考虑使用 变量函数 或者 ... 操作符。

mcrypt aliases

已废弃的 mcrypt_generic_end() 函数已被移除,请使用mcrypt_generic_deinit()代替。

此外,已废弃的 mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb()mcrypt_ofb() 函数已被移除,请配合恰当的MCRYPT_MODE_* 常量来使用 mcrypt_decrypt()进行代替。

set_magic_quotes_runtime()

set_magic_quotes_runtime(), 和它的别名 magic_quotes_runtime()已被移除. 它们在PHP 5.3.0中已经被废弃,并且 在in PHP 5.4.0也由于魔术引号的废弃而失去功能。

set_socket_blocking()

已废弃的 set_socket_blocking() 函数已被移除,请使用stream_set_blocking()代替。

dl() in PHP-FPM

dl()在 PHP-FPM 不再可用,在 CLI 和 embed SAPIs 中仍可用。

GD Type1 functions

Support for PostScript Type1 fonts has been removed from the GD extension, resulting in the removal of the following functions:

Using TrueType fonts and their associated functions is recommended instead.

被移除掉的 INI 配置指令

被移除的功能

以下 INI 配置指令已经被移除,同时移除的还有其对应的功能

xsl.security_prefs

xsl.security_prefs 指令被移除 在预处理的时候,取而代之的方法 XsltProcessor::setSecurityPrefs() 应该被调用到

其他向后兼容相关的变更

new 操作符创建的对象不能以引用方式赋值给变量

new 语句创建的对象不能 以引用的方式赋值给变量。

<?php
class {}
$c =& new C;
?>

Output of the above example in PHP 5:

Deprecated: Assigning the return value of new by reference is deprecated in /tmp/test.php on line 3

Output of the above example in PHP 7:

Parse error: syntax error, unexpected 'new' (T_NEW) in /tmp/test.php on line 3

无效的类、接口以及 trait 命名

不能以下列名字来命名类、接口以及 trait:

此外,也不要使用下列的名字来命名类、接口以及 trait。虽然在 PHP 7.0 中, 这并不会引发错误, 但是这些名字是保留给将来使用的。

移除了 ASP 和 script PHP 标签

使用类似 ASP 的标签,以及 script 标签来区分 PHP 代码的方式被移除。 受到影响的标签有:

被移除的 ASP 和 script 标签
开标签 闭标签
<% %>
<%= %>
<script language="php"> </script>

从不匹配的上下文发起调用

在不匹配的上下文中以静态方式调用非静态方法, 在 PHP 5.6 中已经废弃, 但是在 PHP 7.0 中, 会导致被调用方法中未定义 $this 变量,以及此行为已经废弃的警告。

<?php
class {
    public function 
test() { var_dump($this); }
}

// 注意:并没有从类 A 继承
class {
    public function 
callNonStaticMethodOfA() { A::test(); }
}

(new 
B)->callNonStaticMethodOfA();
?>

Output of the above example in PHP 5:

Deprecated: Non-static method A::test() should not be called statically, assuming $this from incompatible context in /tmp/test.php on line 8
object(B)#1 (0) {
}

Output of the above example in PHP 7:

Deprecated: Non-static method A::test() should not be called statically in /tmp/test.php on line 8

Notice: Undefined variable: this in /tmp/test.php on line 3
NULL

yield 变更为右联接运算符

在使用 yield 关键字的时候,不再需要括号, 并且它变更为右联接操作符,其运算符优先级介于 print=> 之间。 这可能导致现有代码的行为发生改变:

<?php
echo yield -1;
// 在之前版本中会被解释为:
echo (yield) - 1;
// 现在,它将被解释为:
echo yield (-1);

yield 
$foo or die;
// 在之前版本中会被解释为:
yield ($foo or die);
// 现在,它将被解释为:
(yield $foo) or die;
?>

可以通过使用括号来消除歧义。

函数定义不可以包含多个同名参数

在函数定义中,不可以包含两个或多个同名的参数。 例如,下面代码中的函数定义会触发 E_COMPILE_ERROR 错误:

<?php
function foo($a$b$unused$unused) {
    
//
}
?>

Switch 语句不可以包含多个 default 块

在 switch 语句中,两个或者多个 default 块的代码已经不再被支持。 例如,下面代码中的 switch 语句会触发 E_COMPILE_ERROR 错误:

<?php
switch (1) {
    default:
    break;
    default:
    break;
}
?>

$HTTP_RAW_POST_DATA 被移除

不再提供 $HTTP_RAW_POST_DATA 变量。 请使用 php://input 作为替代。

INI 文件中 # 注释格式被移除

在 INI 文件中,不再支持以 # 开始的注释行, 请使用 ;(分号)来表示注释。 此变更适用于 php.ini 以及用 parse_ini_file()parse_ini_string() 函数来处理的文件。

JSON 扩展已经被 JSOND 取代

JSON 扩展已经被 JSOND 扩展取代。 对于数值的处理,有以下两点需要注意的: 第一,数值不能以点号(.)结束 (例如,数值 34. 必须写作 34.034)。 第二,如果使用科学计数法表示数值,e 前面必须不是点号(.) (例如,3.e3 必须写作 3.0e33e3)。

在数值溢出的时候,内部函数将会失败

将浮点数转换为整数的时候,如果浮点数值太大,导致无法以整数表达的情况下, 在之前的版本中,内部函数会直接将整数截断,并不会引发错误。 在 PHP 7.0 中,如果发生这种情况,会引发 E_WARNING 错误,并且返回 NULL

自定义会话处理器的返回值修复

在自定义会话处理器中,如果函数的返回值不是 FALSE,也不是 -1, 会引发致命错误。现在,如果这些函数的返回值不是布尔值,也不是 -1 或者 0,函数调用结果将被视为失败,并且引发 E_WARNING 错误。