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