在 PHP 7 中,许多致命错误和可恢复致命错误已转换为异常。这些错误异常继承自 Error 类,该类本身实现了 Throwable 接口(所有异常继承的新基础接口)。
这意味着自定义错误处理程序可能不再被触发,因为可能会抛出异常(导致未捕获的 Error 异常的新致命错误)。
有关 PHP 7 中错误如何工作的更完整描述,请参见 PHP 7 错误页面。此迁移指南仅列出影响向后兼容性的更改。
使用 Exception 类型声明为 set_exception_handler() 注册异常处理程序的代码,当抛出 Error 对象时会导致致命错误。
如果处理程序需要在 PHP 5 和 7 上运行,则应从处理程序中删除类型声明,而迁移到仅在 PHP 7 上运行的代码可以简单地将 Exception 类型声明替换为 Throwable。
<?php
// 将在 PHP 5 中出错的代码。
function handler(Exception $e) { ... }
set_exception_handler('handler');
// 与 PHP 5 和 7 兼容。
function handler($e) { ... }
// 仅限 PHP 7。
function handler(Throwable $e) { ... }
?>
解析器错误现在会抛出 ParseError 对象。对 eval() 的错误处理现在应包含一个 catch
块,可以处理此错误。
所有 E_STRICT
通知已重新分类为其他级别。E_STRICT
常量保留,因此像 error_reporting(E_ALL|E_STRICT)
这样的调用不会导致错误。
情况 | 新级别/行为 |
---|---|
通过资源索引 | E_NOTICE |
抽象静态方法 | 通知已删除,不触发任何错误 |
"重新定义"构造函数 | 通知已删除,不触发任何错误 |
继承期间签名不匹配 | E_WARNING |
两个使用的特征中具有相同(兼容)的属性 | 通知已删除,不触发任何错误 |
非静态访问静态属性 | E_NOTICE |
只有变量应该通过引用赋值 | E_NOTICE |
只有变量应该通过引用传递 | E_NOTICE |
静态调用非静态方法 | E_DEPRECATED |
PHP 7 现在在解析源文件时使用抽象语法树。这允许对语言进行许多改进,这些改进以前由于早期 PHP 版本中使用的解析器的限制而无法实现,但由于一致性原因导致删除了一些特例,从而导致向后兼容性中断。本节将详细介绍这些情况。
间接访问变量、属性和方法现在将严格按照从左到右的顺序进行评估,而不是以前混合使用的特例。下表显示了评估顺序是如何改变的。
表达式 | PHP 5 解释 | PHP 7 解释 |
---|---|---|
$$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 向后兼容。
这也影响了 global
关键字。如果需要,可以使用大括号语法来模拟以前的行为
<?php
function f() {
// 仅在 PHP 5 中有效。
global $$foo->bar;
// 在 PHP 5 和 7 中有效。
global ${$foo->bar};
}
?>
list() 现在将按定义的顺序将值分配给变量,而不是相反的顺序。通常,这仅影响在与数组 []
运算符一起使用时使用 list() 的情况,如下所示
<?php
list($a[], $a[], $a[]) = [1, 2, 3];
var_dump($a);
?>
上述示例在 PHP 5 中的输出
array(3) { [0]=> int(3) [1]=> int(2) [2]=> int(1) }
上述示例在 PHP 7 中的输出
array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) }
通常,建议不要依赖 list() 赋值发生的顺序,因为这是可能在将来再次更改的实现细节。
list() 不能再解包 string 变量。应改为使用 str_split()。
数组中元素的顺序已更改,当这些元素通过引用赋值自动创建时。例如
<?php
$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);
?>
上述示例在 PHP 5 中的输出
array(2) { ["b"]=> &int(1) ["a"]=> &int(1) }
上述示例在 PHP 7 中的输出
array(2) { ["a"]=> &int(1) ["b"]=> &int(1) }
在 PHP 5 中,在函数参数周围使用多余的括号可以消除严格标准警告,前提是函数参数是通过引用传递的。现在将始终发出警告。
<?php
function getArray() {
return [1, 2, 3];
}
function squareArray(array &$a) {
foreach ($a as &$v) {
$v **= 2;
}
}
// 在 PHP 7 中会生成警告。
squareArray((getArray()));
?>
上面的例子将输出
Notice: Only variables should be passed by reference in /tmp/test.php on line 13
对 foreach 控制结构的行为进行了微小的更改,主要涉及对内部数组指针的处理以及对正在迭代的数组的修改。
在 PHP 7 之前,当使用 foreach 迭代数组时,会修改内部数组指针。现在不再是这样,如以下示例所示
<?php
$array = [0, 1, 2];
foreach ($array as &$val) {
var_dump(current($array));
}
?>
上述示例在 PHP 5 中的输出
int(1) int(2) bool(false)
上述示例在 PHP 7 中的输出
int(0) int(0) int(0)
当按引用迭代时,foreach 现在可以更好地跟踪在迭代期间对数组所做的更改。例如,在迭代期间追加到数组现在将导致追加的值也被迭代。
<?php
$array = [0];
foreach ($array as &$val) {
var_dump($val);
$array[1] = 1;
}
?>
上述示例在 PHP 5 中的输出
int(0)
上述示例在 PHP 7 中的输出
int(0) int(1)
迭代非 Traversable 对象现在将具有与迭代按引用数组相同的行为。这导致 在迭代期间修改数组时改进的行为 也适用于在对象中添加或删除属性的情况。
以前,包含无效数字的八进制字面量会被静默截断(0128
被视为 012
)。现在,无效的八进制字面量会导致解析错误。
用负数进行位运算移位现在将抛出 ArithmeticError
<?php
var_dump(1 >> -1);
?>
上述示例在 PHP 5 中的输出
int(0)
上述示例在 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
超出 int 位宽的位移(任何方向)将始终导致 0。以前,此类位移的行为取决于体系结构。
以前,当 0 用作除法 (/) 或取模 (%) 运算符的除数时,会发出 E_WARNING 并返回 **false
**。现在,除法运算符返回浮点数,如 +INF、-INF 或 NAN,如 IEEE 754 所规定。取模运算符 E_WARNING 已被删除,并将抛出 DivisionByZeroError 异常。
<?php
var_dump(3/0);
var_dump(0/0);
var_dump(0%0);
?>
上述示例在 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)
上述示例在 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
包含十六进制数字的字符串不再被视为数字。例如
<?php
var_dump("0x123" == "291");
var_dump(is_numeric("0x123"));
var_dump("0xe" + "0x1");
var_dump(substr("foo", "0x1"));
?>
上述示例在 PHP 5 中的输出
bool(true) bool(true) int(15) string(2) "oo"
上述示例在 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 是否包含十六进制数字,以及将这种类型的字符串转换为 int
<?php
$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)
?>
\u{
可能会导致错误由于添加了新的 Unicode 代码点转义语法,包含字面量 \u{
后跟无效序列的字符串将导致致命错误。为了避免这种情况,应转义前导反斜杠。
这些函数在 PHP 4.1.0 中被弃用,取而代之的是 call_user_func() 和 call_user_func_array()。您可能还想考虑使用 可变函数 和/或 ...
运算符。
所有 ereg
函数都被删除。建议使用 PCRE 作为替代。
已弃用的 mcrypt_generic_end() 函数已被删除,取而代之的是 mcrypt_generic_deinit()。
此外,已弃用的 mcrypt_ecb()、mcrypt_cbc()、mcrypt_cfb() 和 mcrypt_ofb() 函数已被删除,取而代之的是使用 mcrypt_decrypt() 以及相应的 MCRYPT_MODE_*
常量。
所有 ext/mysql 函数都被删除。有关选择不同 MySQL API 的详细信息,请参阅 选择 MySQL API。
已弃用的 datefmt_set_timezone_id() 和 IntlDateFormatter::setTimeZoneID() 别名已被删除,取而代之的是 datefmt_set_timezone() 和 IntlDateFormatter::setTimeZone()。
set_magic_quotes_runtime() 及其别名 magic_quotes_runtime() 已被删除。它们在 PHP 5.3.0 中被弃用,并在 PHP 5.4.0 中删除魔术引号后,实际上变得不再起作用。
已弃用的 set_socket_blocking() 别名已移除,建议使用 stream_set_blocking()。
GD 扩展已移除对 PostScript Type1 字体的支持,导致以下函数被移除
建议使用 TrueType 字体及其相关函数。
以下 INI 指令已被移除,因为它们关联的功能也被移除
always_populate_raw_post_data
asp_tags
xsl.security_prefs
xsl.security_prefs
指令已被移除。应改用 XsltProcessor::setSecurityPrefs() 方法来控制每个处理器的安全偏好。
new
语句的结果不再可以通过引用分配给变量
<?php
class C {}
$c =& new C;
?>
上述示例在 PHP 5 中的输出
Deprecated: Assigning the return value of new by reference is deprecated in /tmp/test.php on line 3
上述示例在 PHP 7 中的输出
Parse error: syntax error, unexpected 'new' (T_NEW) in /tmp/test.php on line 3
以下名称不能用于命名类、接口或特征
此外,以下名称也不应使用。虽然它们在 PHP 7.0 中不会产生错误,但它们是为将来使用而保留的,应被视为已弃用。
已移除使用 ASP 和脚本标签来分隔 PHP 代码的支持。受影响的标签是
开始标签 | 结束标签 |
---|---|
<% |
%> |
<%= |
%> |
<script language="php"> |
</script> |
之前在 PHP 5.6 中已弃用,对非静态方法进行静态调用,并且上下文不兼容,现在将导致调用的方法具有未定义的 $this
变量,并发出弃用警告。
<?php
class A {
public function test() { var_dump($this); }
}
// 注意:不继承 A
class B {
public function callNonStaticMethodOfA() { A::test(); }
}
(new B)->callNonStaticMethodOfA();
?>
以上示例在 PHP 5.6 中的输出
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) { }
上述示例在 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 结构不再需要括号,并且已更改为右结合运算符,其优先级介于 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) {
//
}
?>
func_get_arg()、func_get_args()、debug_backtrace() 和异常回溯不再报告传递给参数的原始值,而是提供当前值(可能已被修改)。
<?php
function foo($x) {
$x++;
var_dump(func_get_arg(0));
}
foo(1);?>
上述示例在 PHP 5 中的输出
1
上述示例在 PHP 7 中的输出
2
不再允许在 switch 语句中定义两个或多个 default 块。例如,以下 switch 语句将触发 E_COMPILE_ERROR
<?php
switch (1) {
default:
break;
default:
break;
}
?>
$HTTP_RAW_POST_DATA 不再可用。应改用 php://input
流。
#
注释在 INI 文件中已移除已移除在 INI 文件中使用 #
来前缀注释的支持。应改用 ;
(分号)。此更改适用于 php.ini,以及由 parse_ini_file() 和 parse_ini_string() 处理的文件。
JSON 扩展已替换为 JSOND,导致三个小型的 BC 问题。首先,数字不能以小数点结尾(即 34.
必须更改为 34.0
或 34
)。其次,使用科学记数法时,e
指数不能紧接在小数点之后(即 3.e3
必须更改为 3.0e3
或 3e3
)。最后,空字符串不再被视为有效的 JSON。
以前,内部函数会静默地截断从浮点数到整数强制转换产生的数字,当浮点数太大而无法表示为整数时。现在,将发出 E_WARNING 并返回 null
。
由自定义会话处理程序实现的任何谓词函数,如果返回 false
或 -1
,都将是致命错误。如果这些函数返回除布尔值、-1
或 0
之外的任何值,则它将失败并发出 E_WARNING。
内部排序算法已得到改进,这可能会导致比较为相等的元素的排序顺序与之前不同。
注意:
不要依赖比较为相等的元素的顺序;它随时可能改变。
break
和 continue
语句不在循环或 switch
控制结构中,现在在编译时检测到,而不是像以前一样在运行时检测到,并将触发 E_COMPILE_ERROR
。
Mhash 扩展已完全集成到 Hash 扩展中。因此,不再可以使用 extension_loaded() 来检测 Mhash 支持;应改用 function_exists()。此外,Mhash 不再由 get_loaded_extensions() 及其相关功能报告。
declare(ticks) 指令不再泄漏到不同的编译单元。