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