# PHP5.6.x版本遷移至7.0.x版本
## 向后兼容說明
### 錯誤和異常處理的變更
許多可以被修正的Fatal錯誤,在PHP7中將以Exceptions異常的形式拋出。這些Error Exceptions繼承于 [Error](http://php.net/manual/en/class.error.php) 類。而 [Error](http://php.net/manual/en/class.error.php) 類則實現了異常基類 [Throwable](http://php.net/manual/en/class.throwable.php) 接口。 <br>
PHP7中詳細的Error信息可以參考\[ [PHP7錯誤](http://php.net/manual/en/language.errors.php7.php) \]。本文中僅僅介紹和向后兼容有關的信息如下。
#### 類構造函數在失敗時拋出異常
之前,類構造函數在失敗時總是返回NULL或者返回一個不可用的Object,但從PHP7開始,在構造函數初始化失敗時會拋出[異常](http://php.net/manual/en/class.exception.php)。
#### 解析錯誤時會拋出 [解析異常](http://php.net/manual/en/class.parseerror.php)
現在,解析[eval\(\)]錯誤會拋出一個 [解析異常](http://php.net/manual/en/class.parseerror.php) 對象。其可以通過 [catch](http://php.net/manual/en/language.exceptions.php#language.exceptions.catch) 捕捉,并做相應處理。
#### E_STRICT 等級的報錯被重新分配
所有**E_STRICT**級別的報錯已重新分配到其他報錯等級中。**E_STRICT**常量依然保留,所以當你設置報錯等級為 **error_reporting\(E_ALL|E_STRICT\)**時,不會引起報錯。<br>
變更情況如下表

### 變量處理環節的變更
由于PHP7采用抽象的語法樹解析代碼文件,并且過去的PHP版本無法滿足該特性,這一變化將引起一些一致性問題。本節詳細介紹這塊的情況。
#### 對于間接變量、屬性、方法的變動
間接的使用變量、屬性、方法,將嚴格按照從左到右的順序執行,而不會因形式問題導致歧義。下表將表明的這一改變引起的差異。

以上使用老得從右到左的方式的代碼,將被重寫。通過花括號來明確順序(見上圖中間列),使代碼向前兼容PHP7.x,并向后兼容PHP5.x。
#### 對于 [list\(\)](http://php.net/manual/en/function.list.php) 函數處理上的修改
##### [list\(\)](http://php.net/manual/en/function.list.php) 不再按照相反順序插入元素
[list\(\)](http://php.net/manual/en/function.list.php)函數從此開始按照原數組中的順序插入到函數參數制定的位置上,不再翻轉數據。這點修改只會作用在[list\(\)](http://php.net/manual/en/function.list.php)函數參數一起用了數組的\[\]符號時。舉例如下:
```PHP
<?php
list($a[], $a[], $a[]) = [1, 2, 3];
var_dump($a);
?>
```
上述例子在PHP5中的輸出為:
```PHP
array(3) {
[0]=>
int(3)
[1]=>
int(2)
[2]=>
int(1)
}
```
而在PHP7中的輸出為:
```php
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
```
在實際開發中,不建議使用依靠 [list\(\)](http://php.net/manual/en/function.list.php) 函數的參數來做排順序這一操作,畢竟這樣的hack用法在未來依然有可能被調整。
##### [list\(\)](http://php.net/manual/en/function.list.php) 函數參數不再允許為空
[list\(\)](http://php.net/manual/en/function.list.php) 構造時不再允許參數為空的情況,下列情況將不再支持!
```PHP
<?php
list() = $a;
list(,,) = $a;
list($x, list(), $y) = $a;
?>
```
##### [list\(\)](http://php.net/manual/en/function.list.php) 函數不再支持拆解字符串
[list\(\)](http://php.net/manual/en/function.list.php) 不再允許拆解[字符串](http://php.net/manual/en/language.types.string.php)變量為字母,[str_split](http://php.net/manual/en/function.str-split.php)函數可以用于做此事。
#### 在數組中的元素通過引用方式創建時,數組順序會被改變
數組中的元素在通過引用方式創建時,其數組順序會被自動的改變。例如:
```PHP
<?php
$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);
?>
```
PHP5中的輸出:
```PHP
array(2) {
["b"]=>
&int(1)
["a"]=>
&int(1)
}
```
PHP7中的輸出
```PHP
array(2) {
["a"]=>
&int(1)
["b"]=>
&int(1)
}
```
#### [global](http://php.net/manual/en/language.variables.scope.php#language.variables.scope.global) 僅支持簡單的變量
[可變變量](http://php.net/manual/en/language.variables.variable.php)將不能再使用[global](http://php.net/manual/en/language.variables.scope.php#language.variables.scope.global)標記。如果真的需要,可以用花括號來間隔開寫,例如下面代碼:
```PHP
<?php
function f() {
// Valid in PHP 5 only.
global $$foo->bar;
// Valid in PHP 5 and 7.
global ${$foo->bar};
}
?>
```
作為一個基本原則,這樣的變量套變量的使用方式,在[global](http://php.net/manual/en/language.variables.scope.php#language.variables.scope.global)這種場景下不被提倡。
#### 函數參數中的括號不再影響(行為?)
在PHP5中,參數若使被引用的并且使用括號,會沒有報錯發生。但在PHP7開始,這種場景都會印發一個報錯。
```PHP
<?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()));
?>
```
上述示例代碼將會產生如下輸出:
```PHP
Notice: Only variables should be passed by reference in /tmp/test.php on line 13
```
### [foreach](http://php.net/manual/en/control-structures.foreach.php) 的改變
關于 [foreach](http://php.net/manual/en/control-structures.foreach.php) 的修改比較少,主要是修改了數組遍歷時的數組指針,以及修改了數組的迭代。
#### [foreach](http://php.net/manual/en/control-structures.foreach.php) 遍歷期間不再修改數組指針
在PHP7之前,當數組通過[foreach](http://php.net/manual/en/control-structures.foreach.php)迭代時,數組指針會移動。現在開始,不再如此,見下面代碼:
```PHP
<?php
$array = [0, 1, 2];
foreach ($array as &$val) {
var_dump(current($array));
}
?>
```
PHP5中的輸出
```PHP
int(1)
int(2)
bool(false)
```
PHP7中的輸出
```PHP
int(0)
int(0)
int(0)
```
#### [foreach](http://php.net/manual/en/control-structures.foreach.php) 通過值遍歷時,操作的值為數組的副本
當默認使用通過值遍歷數組時,[foreach](http://php.net/manual/en/control-structures.foreach.php)實際操作的是數組的迭代副本,而非數組本身。這就意味著,[foreach](http://php.net/manual/en/control-structures.foreach.php)中的操作不會修改原數組的值。
#### [foreach](http://php.net/manual/en/control-structures.foreach.php) 通過引用遍歷時,有更好的迭代特性
當使用引用遍歷數組時,現在[foreach](http://php.net/manual/en/control-structures.foreach.php)在迭代中更好的跟蹤變化。例如,在迭代中添加一個迭代值到數組中,例如下面代碼:
```PHP
<?php
$array = [0];
foreach ($array as &$val) {
var_dump($val);
$array[1] = 1;
}
?>
```
在PHP5的輸出為:
```PHP
int(0)
```
在PHP7的輸出為:
```PHP
int(0)
int(1)
```
#### [non-Traversable](http://php.net/manual/en/class.traversable.php) 對象的遍歷
[non-Traversable](http://php.net/manual/en/class.traversable.php) 對象的遍歷與通過引用遍歷相似,具有相同的行為特性,[在遍歷期間對原數據進行的操作將會被感知](http://php.net/manual/en/migration70.incompatible.php#migration70.incompatible.foreach.by-ref)。
### [整型](http://php.net/manual/en/language.types.integer.php)處理上的調整
#### 無效的八進制常量
此前,八進制中包含無效數據會自動被截斷(0128被當做為012)。現在,一個無效的八進制字面會造成分析錯誤。
#### 負位置
位移的負數將拋出一個 [ArithmeticError](http://php.net/manual/en/class.arithmeticerror.php)
```PHP
<?php
var_dump(1 >> -1);
?>
```
PHP5中的輸出:
```PHP
int(0)
```
PHP7中的輸出:
```PHP
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。以前,這種轉變的行為是依賴于運行環境的機器架構結構。
### [字符串](http://php.net/manual/en/language.types.string.php)處理上的調整
#### 十六進制字符串不再被認為是數字
含十六進制字符串不再被認為是數字。例如:
```PHP
<?php
var_dump("0x123" == "291");
var_dump(is_numeric("0x123"));
var_dump("0xe" + "0x1");
var_dump(substr("foo", "0x1"));
?>
```
在PHP5中得輸出:
```PHP
bool(true)
bool(true)
int(15)
string(2) "oo"
```
在PHP7中得輸出:
```PHP
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\(\)](http://php.net/manual/en/function.filter-var.php) 函數可以用于檢查一個字符串中是否包含十六進制數,同時也可以轉換一個字符串為十六進制數。
```PHP
<?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轉譯語法](http://php.net/manual/en/migration70.new-features.php#migration70.new-features.unicode-codepoint-escape-syntax),字符串中含有 **\\u{ ** 時會觸發Fatal錯誤。為了避免這一報錯,應該避免反斜杠開頭。
### 被移除的函數
#### [call_user_method\(\)](http://php.net/manual/en/function.call-user-method.php) 與 [call_user_method_array\(\)](http://php.net/manual/en/function.call-user-method-array.php)
這些函數被在PHP4.1.0開始被標記為過時的,在PHP7開始被刪除。建議使用 [call_user_func\(\)](http://php.net/manual/en/function.call-user-func.php) 和 [call_user_func_array\(\)](http://php.net/manual/en/function.call-user-func-array.php) 。你可以考慮下 [變量函數](http://php.net/manual/en/functions.variable-functions.php)或者參考其他函數。
#### [mcrypt](http://php.net/manual/en/book.mcrypt.php) 相關
mcrypt_generic_end\(\) 被刪除,建議使用 mcrypt_generic_deinit\(\) 。
此外,廢棄的mcrypt_ecb\(\),mcrypt_cbc\(\),mcrypt_cfb\(\)和mcrypt_ofb\(\)功能,建議使用目前還支持的mcrypt_decrypt()與適當的MCRYPT_MODE_\*常量。
#### [intl](http://php.net/manual/en/book.intl.php) 相關
datefmt_set_timezone_id\(\)與IntlDateFormatter::setTimeZoneID\(\)被刪除,建議使用datefmt_set_timezone\(\)與IntlDateFormatter::setTimeZone\(\)。
#### [set_magic_quotes_runtime\(\)](http://php.net/manual/en/function.set-magic-quotes-runtime.php)
set_magic_quotes_runtime\(\)與它的別名函數magic_quotes_runtime\(\)被刪除。他們在PHP5.3.0中就被標記被過時的。
#### [set_socket_blocking\(\)](http://php.net/manual/en/function.set-socket-blocking.php)
set_socket_blocking\(\)已被移除,建議使用stream_set_blocking\(\)。
#### [dl\(\)](http://php.net/manual/en/function.dl.php) 在PHP-FPM中
dl\(\)函數不能在PHP-FPM中使用了,它的功能做在了CLI、嵌入到SAPIs中了。
#### [GD](http://php.net/manual/en/book.image.php) 類型的函數
PostScript Type1字體的支持已經從GD擴展刪除,涉及的函數有:
* imagepsbbox()
* imagepsencodefont()
* imagepsextendfont()
* imagepsfreefont()
* imagepsloadfont()
* imagepsslantfont()
* imagepstext()
建議使用TrueType字體和其相關的功能代替。
### 刪除INI配置
#### 刪除的功能
下面的INI指令以及相關的功能被刪除:
* [always_populate_raw_post_data](http://php.net/manual/en/ini.core.php#ini.always-populate-raw-post-data)
* [asp_tags](http://php.net/manual/en/ini.core.php#ini.asp-tags)
#### xsl.security_prefs
xsl.security_prefs指令已被刪除。相反,該[xsltprocessor::setsecurityprefs\(\)](http://php.net/manual/en/xsltprocessor.setsecurityprefs.php)方法用于控制在每個處理器上的安全選項。
### 其他不向后兼容的變更
#### [New](http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.new) 對象不能被引用分配
[New](http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.new)語句的結果不再能通過引用賦值給一個變量,如下代碼:
```PHP
<?php
class C {}
$c =& new C;
?>
```
PHP5中的輸出:
```PHP
Deprecated: Assigning the return value of new by reference is deprecated in /tmp/test.php on line 3
```
PHP7中的輸出:
```PHP
Parse error: syntax error, unexpected 'new' (T_NEW) in /tmp/test.php on line 3
```
#### 無效的類、接口和特性的名字
下面的名稱不能被用來類、接口、特性的名稱:
* bool
* int
* float
* string
* NULL
* TRUE
* FALSE
此外,不推薦下列名稱,他們已被標記過時
* resource
* object
* mixed
* numeric
#### ASP語法標記、Script PHP語法標記被移除
使用ASP腳本標簽,或者Script的PHP代碼,已被刪除。受影響的標簽是:

#### 禁止調用不確定的情況
[之前PHP5.6的過時說明中](http://php.net/manual/en/migration56.deprecated.php#migration56.deprecated.incompatible-context),靜態調用一個非靜態方法,會在靜態調用中被提示未定義 $this ,并會報錯。
```PHP
<?php
class A {
public function test() { var_dump($this); }
}
// Note: Does NOT extend A
class B {
public function callNonStaticMethodOfA() { A::test(); }
}
(new B)->callNonStaticMethodOfA();
?>
```
在PHP5中會輸出:
```PHP
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) {
}
```
在PHP7中會輸出:
```PHP
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
<?php
echo yield -1;
// Was previously interpreted as
echo (yield) - 1;
// And is now interpreted as
echo yield (-1);
yield $foo or die;
// Was previously interpreted as
yield ($foo or die);
// And is now interpreted as
(yield $foo) or die;
?>
```
括號可以用來消除歧義的情況。
#### 函數不能有多個相同的名稱的參數
不允許函數在參數中出現相同名稱的參數。例如下列代碼,將會產生 **E_COMPILE_ERROR** 的報錯。
```PHP
<?php
function foo($a, $b, $unused, $unused) {
//
}
?>
```
#### [$HTTP_RAW_POST_DATA](http://php.net/manual/en/reserved.variables.httprawpostdata.php) 被移除
$HTTP_RAW_POST_DATA 不再被支持。 可以使用 php://input 流數據來代替實現。
#### \# 注釋已被移除
INI文件中以\#符號作為注釋的內容已被移除,**;**符號將代替**\#**,這個改變同樣適用于PHP.ini文件,以及parse_ini_file()和parse_ini_string()處理文件期間。
## 用戶貢獻說明
暫無