在 PHP 7 中,很多致命錯(cuò)誤以及可恢復(fù)的致命錯(cuò)誤,都被轉(zhuǎn)換為異常來(lái)處理了。 這些異常繼承自 Error 類(lèi),此類(lèi)實(shí)現(xiàn)了 Throwable 接口 (所有異常都實(shí)現(xiàn)了這個(gè)基礎(chǔ)接口)。
這也意味著,當(dāng)發(fā)生錯(cuò)誤的時(shí)候,以前代碼中的一些錯(cuò)誤處理的代碼將無(wú)法被觸發(fā)。 因?yàn)樵?PHP 7 版本中,已經(jīng)使用拋出異常的錯(cuò)誤處理機(jī)制了。 (如果代碼中沒(méi)有捕獲 Error 異常,那么會(huì)引發(fā)致命錯(cuò)誤)。
PHP 7 中的錯(cuò)誤處理的更完整的描述,請(qǐng)參見(jiàn) PHP 7 錯(cuò)誤處理。 本遷移指導(dǎo)主要是列出對(duì)兼容性有影響的變更。
拋出 Error 對(duì)象時(shí),如果 set_exception_handler() 里的異常處理代碼聲明了類(lèi)型 Exception ,將會(huì)導(dǎo)致 fatal error。
想要異常處理器同時(shí)支持 PHP5 和 PHP7,應(yīng)該刪掉異常處理器里的類(lèi)型聲明。如果代碼僅僅是升級(jí)到 PHP7,則可以把類(lèi)型 Exception 替換成 Throwable。
<?php
// PHP 5 時(shí)代的代碼將會(huì)出現(xiàn)問(wèn)題
function handler(Exception $e) { ... }
set_exception_handler('handler');
// 兼容 PHP 5 和 7
function handler($e) { ... }
// 僅支持 PHP 7
function handler(Throwable $e) { ... }
?>
在之前版本中,如果內(nèi)部類(lèi)的構(gòu)造器出錯(cuò),會(huì)返回 null
或者一個(gè)不可用的對(duì)象。
從 PHP 7 開(kāi)始,如果內(nèi)部類(lèi)構(gòu)造器發(fā)生錯(cuò)誤,
那么會(huì)拋出異常。
解析錯(cuò)誤會(huì)拋出 ParseError 異常。
對(duì)于 eval() 函數(shù),需要將其包含到一個(gè)
catch
代碼塊中來(lái)處理解析錯(cuò)誤。
原有的 E_STRICT
警告都被遷移到其他級(jí)別。
E_STRICT
常量會(huì)被保留,所以調(diào)用
error_reporting(E_ALL|E_STRICT)
不會(huì)引發(fā)錯(cuò)誤。
場(chǎng)景 | 新的級(jí)別/行為 |
---|---|
將資源類(lèi)型的變量用作鍵來(lái)進(jìn)行索引 | E_NOTICE |
抽象靜態(tài)方法 | 不再警告,會(huì)引發(fā)錯(cuò)誤 |
重復(fù)定義構(gòu)造器函數(shù) | 不再警告,會(huì)引發(fā)錯(cuò)誤 |
在繼承的時(shí)候,方法簽名不匹配 | E_WARNING |
在兩個(gè) trait 中包含相同的(兼容的)屬性 | 不再警告,會(huì)引發(fā)錯(cuò)誤 |
以非靜態(tài)調(diào)用的方式訪問(wèn)靜態(tài)屬性 | E_NOTICE |
變量應(yīng)該以引用的方式賦值 | E_NOTICE |
變量應(yīng)該以引用的方式傳遞(到函數(shù)參數(shù)中) | E_NOTICE |
以靜態(tài)方式調(diào)用實(shí)例方法 | E_DEPRECATED |
PHP 7 現(xiàn)在使用了抽象語(yǔ)法樹(shù)來(lái)解析源代碼。這使得許多由于之前的PHP的解釋器的限制所不可能實(shí)現(xiàn)的改進(jìn)得以實(shí)現(xiàn)。 但出于一致性的原因?qū)е铝艘恍┨厥饫拥淖儎?dòng),而這些變動(dòng)打破了向后兼容。 在這一章中將詳細(xì)介紹這些例子。
對(duì)變量、屬性和方法的間接調(diào)用現(xiàn)在將嚴(yán)格遵循從左到右的順序來(lái)解析,而不是之前的混雜著幾個(gè)特殊案例的情況。 下面這張表說(shuō)明了這個(gè)解析順序的變化。
表達(dá)式 | 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']()
|
使用了舊的從右到左的解析順序的代碼必須被重寫(xiě),明確的使用大括號(hào)來(lái)表明順序(參見(jiàn)上表中間一列)。 這樣使得代碼既保持了與PHP 7.x的前向兼容性,又保持了與PHP 5.x的后向兼容性。
這同樣影響了 global
關(guān)鍵字。如果需要的話可以使用大括號(hào)來(lái)模擬之前的行為。
<?php
function f() {
// Valid in PHP 5 only.
global $$foo->bar;
// Valid in PHP 5 and 7.
global ${$foo->bar};
}
?>
list() 現(xiàn)在會(huì)按照變量定義的順序來(lái)給他們進(jìn)行賦值,而非反過(guò)來(lái)的順序。
通常來(lái)說(shuō),這只會(huì)影響list() 與數(shù)組的[]
操作符一起使用的案例,如下所示:
<?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) }
總之,我們推薦不要依賴(lài)list()的賦值順序,因?yàn)檫@是一個(gè)在未來(lái)也許會(huì)變更的實(shí)現(xiàn)細(xì)節(jié)。
list() 結(jié)構(gòu)現(xiàn)在不再能是空的。如下的例子不再被允許:
<?php
list() = $a;
list(,,) = $a;
list($x, list(), $y) = $a;
?>
list() 不再能解開(kāi)字符串(string)變量。 你可以使用str_split()來(lái)代替它。
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中,在以引用方式傳遞函數(shù)參數(shù)時(shí),使用冗余的括號(hào)對(duì)可以隱匿嚴(yán)格標(biāo)準(zhǔn) 的警告。現(xiàn)在,這個(gè)警告總會(huì)觸發(fā)。
<?php
function getArray() {
return [1, 2, 3];
}
function squareArray(array &$a) {
foreach ($a as &$v) {
$v **= 2;
}
}
// Generates a warning in PHP 7.
squareArray((getArray()));
?>
以上例程會(huì)輸出:
Notice: Only variables should be passed by reference in /tmp/test.php on line 13
foreach發(fā)生了細(xì)微的變化,控制結(jié)構(gòu), 主要圍繞陣列的內(nèi)部數(shù)組指針和迭代處理的修改。
在PHP7之前,當(dāng)數(shù)組通過(guò) foreach 迭代時(shí),數(shù)組指針會(huì)移動(dòng)?,F(xiàn)在開(kāi)始,不再如此,見(jiàn)下面代碼
<?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)
當(dāng)默認(rèn)使用通過(guò)值遍歷數(shù)組時(shí),foreach 實(shí)際操作的是數(shù)組的迭代副本,而非數(shù)組本身。這就意味著,foreach 中的操作不會(huì)修改原數(shù)組的值。
當(dāng)使用引用遍歷數(shù)組時(shí),現(xiàn)在 foreach 在迭代中能更好的跟蹤變化。例如,在迭代中添加一個(gè)迭代值到數(shù)組中,參考下面的代碼:
<?php
$array = [0];
foreach ($array as &$val) {
var_dump($val);
$array[1] = 1;
}
?>
以上例程在 PHP 5 中的輸出:
int(0)
以上例程在 PHP 7 中的輸出:
int(0) int(1)
迭代一個(gè)非Traversable對(duì)象將會(huì)與迭代一個(gè)引用數(shù)組的行為相同。 這將導(dǎo)致在對(duì)象添加或刪除屬性時(shí),foreach通過(guò)引用遍歷時(shí),有更好的迭代特性也能被應(yīng)用
在之前,一個(gè)八進(jìn)制字符如果含有無(wú)效數(shù)字,該無(wú)效數(shù)字將被靜默刪節(jié)(0128
將被解析為 012
).
現(xiàn)在這樣的八進(jìn)制字符將產(chǎn)生解析錯(cuò)誤。
以負(fù)數(shù)形式進(jìn)行的位移運(yùn)算將會(huì)拋出一個(gè) 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 位寬的位移操作(無(wú)論哪個(gè)方向)將始終得到 0 作為結(jié)果。在從前,這一操作是結(jié)構(gòu)依賴(lài)的。
之前的版本中,當(dāng) 0 被做為除數(shù)時(shí),無(wú)論是除數(shù) (/) 或取模 (%) 操作,都會(huì)拋出一個(gè) E_WARNING 錯(cuò)誤并返回
false
。現(xiàn)在,除法運(yùn)算符 (/) 會(huì)返回一個(gè)由 IEEE 754 指定的浮點(diǎn)數(shù):+INF, -INF 或 NAN。取模操作符
(%) 則會(huì)拋出一個(gè) DivisionByZeroError 異常,并且不再產(chǎn)生 E_WARNING 錯(cuò)誤。
<?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
含十六進(jìn)制字符串不再被認(rèn)為是數(shù)字。例如:
<?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() 函數(shù)可以用于檢查一個(gè) string 是否含有十六進(jìn)制數(shù)字,并將其轉(zhuǎn)換為 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{
可能引起錯(cuò)誤
由于新的
Unicode codepoint 轉(zhuǎn)譯語(yǔ)法語(yǔ)法,
緊連著無(wú)效序列并包含\u{
的字串可能引起致命錯(cuò)誤。 為了避免這一報(bào)錯(cuò),應(yīng)該避免反斜杠開(kāi)頭。
這兩個(gè)函數(shù)從PHP 4.1.0 開(kāi)始被廢棄,應(yīng)該使用
call_user_func() 和
call_user_func_array()。 你也可以考慮使用
變量函數(shù)
或者
...
操作符。
所有 ereg
系列函數(shù)被刪掉了。
PCRE 作為推薦的替代品。
已廢棄的 mcrypt_generic_end() 函數(shù)已被移除,請(qǐng)使用mcrypt_generic_deinit()代替。
此外,已廢棄的 mcrypt_ecb(),
mcrypt_cbc(), mcrypt_cfb() 和
mcrypt_ofb() 函數(shù)已被移除,請(qǐng)配合恰當(dāng)?shù)?strong>MCRYPT_MODE_*
常量來(lái)使用 mcrypt_decrypt()進(jìn)行代替。
所有 ext/mysql 函數(shù)已被刪掉了。 如何選擇不同的 MySQL API,詳情請(qǐng)見(jiàn) 選擇 MySQL API。
已廢棄的 datefmt_set_timezone_id() 和 IntlDateFormatter::setTimeZoneID() 函數(shù)已被移除,請(qǐng)使用 datefmt_set_timezone() 與 IntlDateFormatter::setTimeZone()代替。
移除了 set_magic_quotes_runtime() 和它的別名 magic_quotes_runtime()。 它們?cè)?PHP 5.3.0 中已經(jīng)被廢棄, 并由于 PHP 5.4.0 移除魔術(shù)引號(hào)(Magic Quotes)而沒(méi)有用處。
已廢棄的 set_socket_blocking() 函數(shù)已被移除,請(qǐng)使用stream_set_blocking()代替。
Support for PostScript Type1 fonts has been removed from the GD extension, resulting in the removal of the following functions:
推薦使用 TrueType 字體和相關(guān)的函數(shù)作為替代。
以下 INI 配置指令已經(jīng)被移除,同時(shí)移除的還有其對(duì)應(yīng)的功能
always_populate_raw_post_data
asp_tags
xsl.security_prefs
xsl.security_prefs
指令被移除
在預(yù)處理的時(shí)候,取而代之的方法 XsltProcessor::setSecurityPrefs()
應(yīng)該被調(diào)用到
new
語(yǔ)句創(chuàng)建的對(duì)象不能
以引用的方式賦值給變量。
<?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
不能以下列名字來(lái)命名類(lèi)、接口以及 trait:
null
true
false
此外,也不要使用下列的名字來(lái)命名類(lèi)、接口以及 trait。雖然在 PHP 7.0 中, 這并不會(huì)引發(fā)錯(cuò)誤, 但是這些名字是保留給將來(lái)使用的。
使用類(lèi)似 ASP 的標(biāo)簽,以及 script 標(biāo)簽來(lái)區(qū)分 PHP 代碼的方式被移除。 受到影響的標(biāo)簽有:
開(kāi)標(biāo)簽 | 閉標(biāo)簽 |
---|---|
<% |
%> |
<%= |
%> |
<script language="php"> |
</script> |
在不匹配的上下文中以靜態(tài)方式調(diào)用非靜態(tài)方法,
在 PHP 5.6 中已經(jīng)廢棄,
但是在 PHP 7.0 中,
會(huì)導(dǎo)致被調(diào)用方法中未定義 $this
變量,以及此行為已經(jīng)廢棄的警告。
<?php
class A {
public function test() { var_dump($this); }
}
// 注意:并沒(méi)有從類(lèi) 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 關(guān)鍵字的時(shí)候,不再需要括號(hào),
并且它變更為右聯(lián)接操作符,其運(yùn)算符優(yōu)先級(jí)介于
print
和 =>
之間。
這可能導(dǎo)致現(xiàn)有代碼的行為發(fā)生改變:
<?php
echo yield -1;
// 在之前版本中會(huì)被解釋為:
echo (yield) - 1;
// 現(xiàn)在,它將被解釋為:
echo yield (-1);
yield $foo or die;
// 在之前版本中會(huì)被解釋為:
yield ($foo or die);
// 現(xiàn)在,它將被解釋為:
(yield $foo) or die;
?>
可以通過(guò)使用括號(hào)來(lái)消除歧義。
在函數(shù)定義中,不可以包含兩個(gè)或多個(gè)同名的參數(shù)。
例如,下面代碼中的函數(shù)定義會(huì)觸發(fā)
E_COMPILE_ERROR
錯(cuò)誤:
<?php
function foo($a, $b, $unused, $unused) {
//
}
?>
在 switch 語(yǔ)句中,兩個(gè)或者多個(gè) default 塊的代碼已經(jīng)不再被支持。
例如,下面代碼中的 switch 語(yǔ)句會(huì)觸發(fā)
E_COMPILE_ERROR
錯(cuò)誤:
<?php
switch (1) {
default:
break;
default:
break;
}
?>
當(dāng)在函數(shù)代碼中使用 func_get_arg() 或 func_get_args() 函數(shù)來(lái)檢視參數(shù)值, 或者使用 debug_backtrace() 函數(shù)查看回溯跟蹤, 以及在異?;厮葜兴鶊?bào)告的參數(shù)值是指參數(shù)當(dāng)前的值(有可能是已經(jīng)被函數(shù)內(nèi)的代碼改變過(guò)的值), 而不再是參數(shù)被傳入函數(shù)時(shí)候的原始值了。
<?php
function foo($x) {
$x++;
var_dump(func_get_arg(0));
}
foo(1);?>
以上例程在 PHP 5 中的輸出:
1
以上例程在 PHP 7 中的輸出:
2
不再提供 $HTTP_RAW_POST_DATA 變量。 請(qǐng)使用
php://input
作為替代。
#
注釋格式被移除
在 INI 文件中,不再支持以 #
開(kāi)始的注釋行,
請(qǐng)使用 ;
(分號(hào))來(lái)表示注釋。
此變更適用于 php.ini 以及用 parse_ini_file() 和 parse_ini_string()
函數(shù)來(lái)處理的文件。
JSON 擴(kuò)展已經(jīng)被 JSOND 擴(kuò)展取代。
對(duì)于數(shù)值的處理,有以下兩點(diǎn)需要注意的:
第一,數(shù)值不能以點(diǎn)號(hào)(.)結(jié)束
(例如,數(shù)值 34.
必須寫(xiě)作
34.0
或 34
)。
第二,如果使用科學(xué)計(jì)數(shù)法表示數(shù)值,e
前面必須不是點(diǎn)號(hào)(.)
(例如,3.e3
必須寫(xiě)作
3.0e3
或 3e3
)。
另外,空字符串不再被視作有效的 JSON 字符串。
將浮點(diǎn)數(shù)轉(zhuǎn)換為整數(shù)的時(shí)候,如果浮點(diǎn)數(shù)值太大,導(dǎo)致無(wú)法以整數(shù)表達(dá)的情況下,
在之前的版本中,內(nèi)部函數(shù)會(huì)直接將整數(shù)截?cái)?,并不?huì)引發(fā)錯(cuò)誤。
在 PHP 7.0 中,如果發(fā)生這種情況,會(huì)引發(fā) E_WARNING 錯(cuò)誤,并且返回 null
。
在自定義會(huì)話處理器中,如果函數(shù)的返回值不是 false
,也不是 -1
,
會(huì)引發(fā)致命錯(cuò)誤。現(xiàn)在,如果這些函數(shù)的返回值不是布爾值,也不是 -1
或者 0
,函數(shù)調(diào)用結(jié)果將被視為失敗,并且引發(fā) E_WARNING 錯(cuò)誤。
由于內(nèi)部排序算法進(jìn)行了提升, 可能會(huì)導(dǎo)致對(duì)比時(shí)被視為相等的多個(gè)元素之間的順序不穩(wěn)定。
注意:
在對(duì)比時(shí)被視為相等的多個(gè)元素之間的排序順序是不可信賴(lài)的,即使是相等的兩個(gè)元素, 他們的位置也可能被排序算法所改變。
在循環(huán)或者 switch
語(yǔ)句之外使用
break
和 continue
被視為編譯型錯(cuò)誤(之前視為運(yùn)行時(shí)錯(cuò)誤),會(huì)引發(fā)
E_COMPILE_ERROR
錯(cuò)誤。
Mhash 擴(kuò)展已經(jīng)被完全整合進(jìn) Hash 擴(kuò)展了。 因此,不要再使用 extension_loaded() 函數(shù)來(lái)檢測(cè)是否支持 MHash 擴(kuò)展了, 建議使用 function_exists() 函數(shù)來(lái)進(jìn)行檢測(cè)。 另外,函數(shù) get_loaded_extensions() 以及相關(guān)的特性中,也不再報(bào)告 和 MHash 擴(kuò)展相關(guān)的信息了。
declare(ticks) 指示符不再泄漏到不同的編譯單元中。