PHP7 不向后兼容的变更

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

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

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

PHP 7 改变了大多数错误的报告方式。不同于传统(PHP 5)的错误报告机制,现在大多数错误被作为 Error 异常抛出。

这种 Error 异常可以像 Exception 异常一样被第一个匹配的 try / catch 块所捕获。如果没有匹配的 catch 块,则调用异常处理函数(事先通过 set_exception_handler() 注册)进行处理。 如果尚未注册异常处理函数,则按照传统方式处理:被报告为一个致命错误(Fatal Error)。

Error 类并非继承自 Exception 类,所以不能用 catch (Exception $e) { ... } 来捕获 Error。你可以用 catch (Error $e) { ... },或者通过注册异常处理函数( set_exception_handler())来捕获 Error。

set_exception_handler() 不再保证收到的一定是 Exception 对象

抛出 Error 对象时,如果 set_exception_handler() 里的异常处理代码声明的类型是 Exception ,将会导致 fatal error。

想要异常处理器同时支持 PHP5 和 PHP7,应该删掉异常处理器里的类型声明。如果代码仅仅是升级到 PHP7,则可以把类型 Exception 替换成 Throwable。

5 时代的代码将会出现问题
function handler(Exception $e) { ... }
set_exception_handler('handler');

// 兼容 PHP 57
function handler($e) { ... }

// 仅支持 PHP 7
function handler(Throwable $e) { ... }

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

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

解析错误会抛出 ParseError 异常

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

E_STRICT 警告级别变更

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

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

关于变量处理的变更

PHP 7 现在使用了抽象语法树来解析源代码。这为语言带来了许多改进,之前的PHP解释器的限制所不可能实现的, 但出于一致性的原因导致了一些特殊例子的变动,而这些变动打破了向后兼容。

关于间接使用变量、属性和方法的变更

对变量、属性和方法的间接调用现在将严格遵循 从左到右 的顺序来解析,而不是之前的混杂着几个特殊案例的情况。 下面这张表说明了这个解析顺序的变化。

表达式 PHP5 的解析方式 PHP7 的解析方式
$$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']()

旧的从右到左的解析顺序的代码必须被重写,明确的使用圆括号来表明顺序(参见上表)。 这样使得代码既保持了与PHP 7.x的前向兼容性,又保持了与PHP 5.x的后向兼容性。

关于list()处理方式的变更

list() 不再以反向的顺序来进行赋值,list() 现在会按照变量定义的顺序来给他们进行赋值。

$a[], $a[], $a[]) = [1, 2, 3];
var_dump($a);

PHP5 中的输出:

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

PHP7 中的输出:

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

总之,我们推荐不要依赖list()的赋值顺序,因为这是一个在未来也许会变更的实现细节。

空的list()赋值支持已经被移除

list() 结构现在不再能是空的。如下的例子不再被允许:

$a;
list(,,) = $a;
list($x, list(), $y) = $a;

list()不再能解开string

list() 不再能解开字符串(string)变量。 你可以使用str_split()来代替它。

当数组中的元素被引用赋值时,数组元素排序的变更

$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);

PHP5 中的输出:

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

PHP7 中的输出:

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

foreach 的变更

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

foreach不再改变内部数组指针

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

$array = [0, 1, 2];
foreach ($array as &$val) {
    var_dump(current($array));
}

PHP5中的输出:

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

PHP7中的输出:

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

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

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

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

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

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

PHP5中的输出:

int(0)

PHP7中的输出:

int(0) int(1)

string 处理上的调整

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

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


var_dump("0x123" == "291");
var_dump(is_numeric("0x123"));
var_dump("0xe" + "0x1");
var_dump(substr("foo", "0x1"));

PHP5中的输出:

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

PHP7中的输出:

bool(false) bool(false) int(0) 
Notice: A non well formed numeric value encountered in D:\phpStudy\WWW\blog\public\test.php on line 5
string(3) "foo"

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

$str = "0xffff";
$int = filter_var($str, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
if (false === $int) {
    throw new Exception("Invalid integer!");
}
var_dump($int); // int(65535)

被移除的函数

call_user_method() 和 call_user_method_array()

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

所有 ereg* 函数

所有 ereg 系列函数被删掉了。 PCRE 作为推荐的替代品。

所有 ext/mysql 函数

所有 ext/mysql 函数已被删掉了。

所有 ext/mssql 函数

所有 ext/mssql 函数已被删掉了。

移除了 ASP 和 script 风格的 PHP 标签

使用类似 ASP 或 script 风格的PHP标签,来区分 PHP 代码的方式已被移除。

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

在 switch 语句中,两个或者多个 default 块的代码已经不再被支持。

func_get_arg() 或 func_get_args() 的变更

当在函数代码中使用 func_get_arg() 或 func_get_args() 函数来检查参数值, 或者使用 debug_backtrace() 函数查看回溯跟踪, 以及在异常回溯中所报告的参数值是指参数当前的值(有可能是已经被函数内的代码改变过的值), 而不再是参数被传入函数时的原始值了。

function foo($x) {
    $x++;
    var_dump(func_get_arg(0));
}
foo(1);

PHP5 中的输出:

int(1)

PHP7 中的输出:

int(2)

$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.0 或 34)。

第二,如果使用科学计数法表示数值,e 前面必须不是点号(.) (例如,3.e3 必须写作 3.0e3 或 3e3)。 另外,空字符串不再被视作有效的 JSON 字符串。

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

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

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

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

相等的元素在排序时的顺序问题

由于内部排序算法进行了提升, 可能会导致对比时被视为相等的多个元素之间的顺序不稳定。

注意: 在对比时被视为相等的多个元素之间的排序顺序是不可信赖的,即使是相等的两个元素, 他们的位置也可能被排序算法所改变。


你可能感兴趣的:(PHP)