在 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。
抛出 Error 对象时,如果 set_exception_handler() 里的异常处理代码声明的类型是 Exception ,将会导致 fatal error。
想要异常处理器同时支持 PHP5 和 PHP7,应该删掉异常处理器里的类型声明。如果代码仅仅是升级到 PHP7,则可以把类型 Exception 替换成 Throwable。
5 时代的代码将会出现问题
function handler(Exception $e) { ... }
set_exception_handler('handler');
// 兼容 PHP 5 和 7
function handler($e) { ... }
// 仅支持 PHP 7
function handler(Throwable $e) { ... }
在之前版本中,如果内部类的构造器出错,会返回 NULL 或者一个不可用的对象。 从 PHP 7 开始,如果内部类构造器发生错误, 那么会抛出异常。
解析错误会抛出 ParseError 异常。 对于 eval() 函数,需要将其包含到一个 catch 代码块中来处理解析错误。
原有的 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() 现在会按照变量定义的顺序来给他们进行赋值。
$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() 结构现在不再能是空的。如下的例子不再被允许:
$a;
list(,,) = $a;
list($x, list(), $y) = $a;
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发生了细微的变化,主要是阵列的内部数组指针和迭代处理的修改。
在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 在迭代中能更好的跟踪变化。例如,在迭代中添加一个迭代值到数组中,参考下面的代码:
$array = [0];
foreach ($array as &$val) {
var_dump($val);
$array[1] = 1;
}
PHP5中的输出:
int(0)
PHP7中的输出:
int(0) int(1)
含十六进制字符串不再被认为是数字。例如:
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"
$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)
这两个函数从PHP 4.1.0开始被废弃,现已被移除,应该使用call_user_func() 和 call_user_func_array()。 你也可以考虑使用 变量函数 或者 ... 操作符。
所有 ereg 系列函数被删掉了。 PCRE 作为推荐的替代品。
所有 ext/mysql 函数已被删掉了。
所有 ext/mssql 函数已被删掉了。
使用类似 ASP 或 script 风格的PHP标签,来区分 PHP 代码的方式已被移除。
在 switch 语句中,两个或者多个 default 块的代码已经不再被支持。
当在函数代码中使用 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 变量。 请使用 php://input 作为替代。
在 INI 文件中,不再支持以 # 开始的注释行, 请使用 ;(分号)来表示注释。 此变更适用于 php.ini 以及用 parse_ini_file() 和 parse_ini_string() 函数来处理的文件。
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 错误。
由于内部排序算法进行了提升, 可能会导致对比时被视为相等的多个元素之间的顺序不稳定。
注意: 在对比时被视为相等的多个元素之间的排序顺序是不可信赖的,即使是相等的两个元素, 他们的位置也可能被排序算法所改变。