Php/docs/migration70.incompatible

来自菜鸟教程
跳转至:导航、​搜索

不向后兼容的变更

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

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

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

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

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

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

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

<?php// PHP 5 时代的代码将会出现问题function handler(Exception $e) { ... }set_exception_handler('handler');// 兼容 PHP 5 和 7function handler($e) { ... }// 仅支持 PHP 7function handler(Throwable $e) { ... }?>

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

在之前版本中,如果内部类的构造器出错,会返回 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


关于变量处理的变化

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 关键字。如果需要的话可以使用大括号来模拟之前的行为。

<?phpfunction f() {    // Valid in PHP 5 only.    global $$foo->bar;    // Valid in PHP 5 and 7.    global ${$foo->bar};}?>

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

list() 不再以反向的顺序来进行赋值

list() 现在会按照变量定义的顺序来给他们进行赋值,而非反过来的顺序。 通常来说,这只会影响list() 与数组的[]操作符一起使用的案例,如下所示:

<?phplist($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()赋值支持已经被移除

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

<?phplist() = $a;list(,,) = $a;list($x, list(), $y) = $a;?>

list()不再能解开string

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


==== 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);?>

以上例程在 PHP 5 中的输出:

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

以上例程在 PHP 7 中的输出:

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

函数参数附近的括号不再影响行为

在 PHP 5中,在以引用方式传递函数参数时,使用冗余的括号对可以隐匿严格标准 的警告。现在,这个警告总会触发。

<?phpfunction getArray() {    return [1, 2, 3];}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 = [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 通过值遍历时,操作的值为数组的副本

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


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

当使用引用遍历数组时,现在 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 对象的遍历

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


Changes to integer handling

无效的八进制字符(Invalid octal literals)

在之前,一个八进制字符如果含有无效数字,该无效数字将被静默删节(0128 将被解析为 012). 现在这样的八进制字符将产生解析错误。


负位移运算(Negative bitshifts)

以负数形式进行的位移运算将会抛出一个 ArithmeticError

<?phpvar_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

超范围后产生位移

超出 integer 位宽的位移操作(无论哪个方向)将始终得到 0 作为结果。在从前,这一操作是结构依赖的。


除以零的变化

之前的版本中,当 0 被做为除数时,无论是除数 (/) 或取模 (%) 操作,都会抛出一个 E_WARNING 错误并返回 false。现在,除法运算符 (/) 会返回一个由 IEEE 754 指定的浮点数:+INF, -INF 或 NAN。取模操作符 (%) 则会抛出一个 DivisionByZeroError 异常,并且不再产生 E_WARNING 错误。

<?phpvar_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

string处理上的调整

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

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

<?phpvar_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 是否含有十六进制数字,并将其转换为integer:

<?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 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()。 你也可以考虑使用 变量函数 或者 ... 操作符。


所有的 ereg* 函数

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


mcrypt 别名

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

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


所有 ext/mysql 函数

所有 ext/mysql 函数已被删掉了。 如何选择不同的 MySQL API,详情请见 选择 MySQL API


所有 ext/mssql 函数

所有 ext/mssql 函数已被移除。


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:

  • imagepsbbox()
  • imagepsencodefont()
  • imagepsextendfont()
  • imagepsfreefont()
  • imagepsloadfont()
  • imagepsslantfont()
  • imagepstext()

推荐使用 TrueType 字体和相关的函数作为替代。


被移除掉的 INI 配置指令

被移除的功能

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


xsl.security_prefs

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


其他向后兼容相关的变更

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

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

<?phpclass 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

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

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

  • bool
  • int
  • float
  • string
  • null
  • true
  • false

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

  • resource
  • object
  • mixed
  • numeric


移除了 ASP 和 script PHP 标签

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

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


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

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

<?phpclass 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 变更为右联接运算符

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

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

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


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

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

<?phpfunction foo($a, $b, $unused, $unused) {    //}?>

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

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

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

在函数中检视参数值会返回 当前 的值

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

<?phpfunction foo($x) {    $x++;    var_dump(func_get_arg(0));}foo(1);?>

以上例程在 PHP 5 中的输出:

1

以上例程在 PHP 7 中的输出:

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


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

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


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

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


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

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

Note:

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

错误的使用 break 和 switch 语句

在循环或者 switch 语句之外使用 breakcontinue 被视为编译型错误(之前视为运行时错误),会引发 E_COMPILE_ERROR 错误。


Mhash 不再是一个单独的扩展了

Mhash 扩展已经被完全整合进 Hash 扩展了。 因此,不要再使用 extension_loaded() 函数来检测是否支持 MHash 扩展了, 建议使用 function_exists() 函数来进行检测。 另外,函数 get_loaded_extensions() 以及相关的特性中,也不再报告 和 MHash 扩展相关的信息了。


declare(ticks)

declare(ticks) 指示符不再泄漏到不同的编译单元中。