2024 年 PHP 日本研討會

向下不相容的變更

錯誤和例外處理的變更

在 PHP 7 中,許多致命錯誤和可復原的致命錯誤已轉換為例外。這些錯誤例外繼承自 Error 類別,該類別本身實作了 Throwable 介面(所有例外繼承的新基底介面)。

這意味著自訂錯誤處理器可能不再被觸發,因為可能會改為拋出例外(導致未捕獲的 Error 例外產生新的致命錯誤)。

關於 PHP 7 中錯誤如何運作的更完整描述,可以在PHP 7 錯誤頁面找到。本遷移指南僅列舉影響向下相容性的變更。

set_exception_handler() 不再保證會收到 Exception 物件

使用 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) { ... }
?>

內部建構函式在失敗時一律拋出例外

先前,某些內部類別在建構函式失敗時會回傳 null 或無法使用的物件。現在所有內部類別都會在這種情況下拋出 Exception,與使用者類別必須執行的操作相同。

剖析錯誤會拋出 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 現在在解析原始碼檔案時使用抽象語法樹 (AST)。這允許對語言進行許多先前由於早期 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() 不再以相反的順序賦值給變數

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() 賦值

list() 建構式不能再為空。以下用法不再允許

<?php
list() = $a;
list(,,) =
$a;
list(
$x, list(), $y) = $a;
?>
list() 無法解開字串

list() 不再能解開字串變數。應使用 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 的變更

foreach 控制結構的行為有一些小幅度的變更,主要圍繞在內部陣列指標的處理以及對迭代中陣列的修改。

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 現在操作的是陣列的副本

當使用預設的以值傳遞模式時,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 物件現在將與迭代以參考傳遞的陣列具有相同的行為。這導致 在迭代期間修改陣列時的改進行為 也會在物件新增或移除屬性時套用。

整數 處理方式的變更

無效的八進位字面值

以前,包含無效數字的八進位字面值會被靜默截斷(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

超出範圍的位元位移

超出 整數 位元寬度的位元位移(無論哪個方向)將永遠導致 0。以前,這種位移的行為取決於架構。

除以零的變更

先前,當 0 作為除法 (/) 或取餘 (%) 運算子的除數時,會發出 E_WARNING 並返回 **false**。現在,除法運算子會根據 IEEE 754 規範,返回一個浮點數,其值為 +INF、-INF 或 NAN。取餘運算子的 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

字串 (string) 處理方式的變更

十六進位字串不再被視為數值

包含十六進位數字的字串將不再被視為數值。例如:

<?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() 函式來檢查字串是否包含十六進位數字,也可以將這種類型的字串轉換為整數 (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{ 後接無效序列的字串將會造成致命錯誤。為了避免這種情況,開頭的反斜線應該要跳脫。

移除的函式

call_user_method()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_decrypt() 搭配適當的 MCRYPT_MODE_* 常數。

所有 ext/mysql 函式

所有 ext/mysql 函式皆已移除。關於選擇其他 MySQL API 的詳細資訊,請參閱 選擇 MySQL API

所有 ext/mssql 函式

所有 ext/mssql 函式皆已移除。

intl 別名

已移除棄用的 datefmt_set_timezone_id()IntlDateFormatter::setTimeZoneID() 別名,建議分別改用 datefmt_set_timezone()IntlDateFormatter::setTimeZone()

set_magic_quotes_runtime()

set_magic_quotes_runtime() 以及它的別名 magic_quotes_runtime() 已移除。它們在 PHP 5.3.0 中被標記為棄用,並在 PHP 5.4.0 中移除魔術引號後實際上失效。

set_socket_blocking()

已移除棄用的 set_socket_blocking() 別名,建議改用 stream_set_blocking()

PHP-FPM 中的 dl()

在 PHP-FPM 中已無法使用 dl()。它在 CLI 和 embed SAPI 中仍然有效。

GD Type1 函式

GD 擴充功能已移除對 PostScript Type1 字體的支援,導致以下函式也被移除:

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

建議改用 TrueType 字體及其相關函式。

已移除的 INI 指令

已移除的功能

由於相關功能已被移除,以下 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

無效的類別、介面和 Trait 名稱

以下名稱不能用於命名類別、介面或 Trait:

此外,也不應該使用以下名稱。雖然它們在 PHP 7.0 中不會產生錯誤,但它們是保留給未來使用的,應視為已棄用。

已移除 ASP 和 script PHP 標籤

不再支援使用 ASP 和 script 標籤來界定 PHP 程式碼。受影響的標籤如下:

已移除的 ASP 和 script 標籤
起始標籤 結束標籤
<% %>
<%= %>
<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 現在是右結合運算子

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 陳述式不能有多個預設區塊

在 switch 陳述式中定義兩個或多個預設區塊將不再被允許。例如,以下 switch 陳述式將觸發 E_COMPILE_ERROR 錯誤

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

已移除 $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。

溢位時內建函式失敗

先前,當浮點數太大而無法表示為整數時,內建函式會在浮點數轉換為整數時靜默地截斷數字。現在,將發出 E_WARNING 警告,並回傳 null

修正自訂 Session 處理器的回傳值

由自訂 Session 處理器實作的任何斷言函式,如果回傳 false-1 都將造成致命錯誤。如果這些函式回傳的值不是布林值、-10,則會失敗並發出 E_WARNING 警告。

相同元素的排序順序

內部排序演算法已改良,這可能導致比較結果相等的元素排序順序與之前不同。

注意:

不要依賴比較結果相等元素的排序順序;它隨時可能改變。

誤置的 break 和 continue 陳述式

在迴圈或 switch 控制結構之外的 breakcontinue 陳述式現在會在編譯時而不是像以前一樣在執行時被偵測到,並觸發 E_COMPILE_ERROR 編譯錯誤。

不允許常數作為 break 和 continue 的參數

breakcontinue 陳述式不再允許其參數為常數,並會觸發 E_COMPILE_ERROR 編譯錯誤。

Mhash 不再是一個擴充套件

Mhash 擴充套件已完全整合到 雜湊 (Hash) 擴充套件中。因此,不再能夠使用 extension_loaded() 函式來偵測 Mhash 支援;請改用 function_exists() 函式。此外,get_loaded_extensions() 和相關功能也不再回報 Mhash。

declare(ticks)

declare(ticks) 指令不再洩漏到不同的編譯單元。

新增註釋

使用者提供的註釋

此頁面沒有使用者提供的註釋。
To Top