PHP 8預計將於2020年12月發布,它將為我們帶來很多強大的功能和出色的語言改進。
#js-mykinsta-video {
背景圖片:url(https://kinsta.com/wp-content/themes/kinsta/images/mykinsta-dashboard-v8@2x.jpg);
}
免費試用
儘管仍有一些提案正在開發中,但許多RFC已經得到批准和實施,因此現在是時候讓我們開始研究一些最令人興奮的補充,這些補充應使PHP更快,更可靠。
請注意,PHP 8仍在開發中,在最終版本發布之前我們可以看到一些變化。無論如何,我們會一直跟蹤這些更改並定期更新此帖子,因此請確保您不會錯過任何有關PHP 8的信息,並不時檢查一下此帖子。
那麼,我們期望PHP 8有哪些功能和改進? PHP 8(該語言的下一個主要版本)最大的特點是什麼?
讓我們潛入吧!
PHP JIT(及時編譯器)
PHP 8附帶的最受讚譽的功能是即時(JIT)編譯器。 JIT的全部意義是什麼?
RFC提案將JIT描述如下:
「 PHP JIT被實現為OPcache的幾乎獨立的部分。它可以在PHP編譯時和運行時啟用/禁用。啟用後,PHP文件的本機代碼存儲在OPcache共享內存的另一個區域中,並且op_array→opcodes[].handler保留指向JIT版本代碼的入口點的指針。」
那麼,我們如何實現JIT?JIT與OPcache有何區別?
為了更好地了解什麼是JIT for PHP,讓我們快速看一下PHP如何從源代碼執行到最終結果。
PHP執行是一個4個階段的過程:
- Lexing /令牌化:首先,解釋器讀取PHP代碼並構建一組令牌。
- 解析:解釋器檢查腳本是否與語法規則匹配,並使用標記來構建抽象語法樹(AST),該語法是源代碼結構的分層表示。
- 編譯:解釋器遍歷樹並將AST節點轉換為低級Zend操作碼,這些操作碼是確定Zend VM執行的指令類型的數字標識符。
- 解釋:操作碼在Zend VM上解釋並運行。
下圖顯示了基本PHP執行過程的直觀表示。
那麼,OPcache如何使PHP更快? JIT執行過程中有哪些變化?
OPcache擴展
PHP是一種解釋型語言。這意味著,當運行PHP腳本時,解釋器將在每次請求時一遍又一遍地解析,編譯和執行代碼。這可能會浪費CPU資源和其他時間。
這是OPcache擴展發揮作用的地方:
「 OPcache通過將預編譯的腳本位元組碼存儲在共享內存中來提高PHP性能,從而消除了PHP在每個請求上載入和解析腳本的需要。」
啟用OPcache後,PHP解釋程序僅在腳本首次運行時才經過上述4個階段。由於PHP位元組碼存儲在共享內存中,因此它們可以立即作為低級中間表示形式使用,並且可以立即在Zend VM上執行。
從PHP 5.5開始,Zend OPcache擴展默認情況下可用,您可以通過從伺服器上的腳本中調用phpinfo()或檢出php.ini文件(請參閱OPcache配置設置)來檢查是否正確配置了它。
預裝
最近通過預載入的實現對OPcache進行了改進,預載入是PHP 7.4中新增的新OPcache功能。預載入提供了一種在「運行任何應用程序代碼之前」將一組指定的腳本存儲到OPcache內存中的方法,但是它不會為基於Web的典型應用程序帶來明顯的性能提升。
您可以在我們的PHP 7.4簡介中閱讀有關預載入的更多信息。
通過JIT,PHP向前邁了一步。
JIT —及時編譯器
即使操作碼採用低級中間表示形式,也仍然必須將其編譯為機器代碼。 JIT「沒有引入任何其他IR(中間表示)形式」,而是使用DynASM(用於代碼生成引擎的動態彙編器)直接從PHP位元組碼生成本機代碼。
簡而言之,JIT將中間代碼的熱門部分轉換為機器代碼。繞過編譯,它將能夠顯著提高性能和內存使用率。
PHP JIT提案的合著者Zeev Surasky顯示了使用JIT可以更快地進行多少計算:
但是,JIT是否可以有效地提高WordPress性能?
實時Web應用的JIT
根據JIT RFC,即時編譯器實現應提高PHP性能。但是,我們真的會在WordPress等現實應用中體驗到這種改進嗎?
早期測試表明,JIT可以使CPU密集型工作負載的運行速度大大提高,但是RFC警告:
「…像以前的嘗試一樣,它目前似乎並不能顯著改善WordPress等現實應用(opcache.jit = 1235 326 req / sec與315 req / sec)。
計劃通過性能分析和推測性優化來做出更大的努力,以改善實際應用的JIT。」
啟用JIT後,代碼將不會由Zend VM運行,而是由CPU本身運行,這將提高計算速度。諸如WordPress之類的Web應用程序還依賴於TTFB,資料庫優化,HTTP請求等其他因素。
因此,對於WordPress和類似的應用程序,我們不應該期望PHP的執行速度會大大提高。但是,JIT可以為開發人員帶來一些好處。
根據尼基塔·波波夫(Nikita Popov)的說法:
「 JIT編譯器的好處大致(如RFC中所述):
- 數字代碼的性能明顯更好。
- 「典型」 PHP Web應用程序代碼的性能略好。
- 將更多代碼從C轉移到PHP的潛力,因為PHP現在已經足夠快了。」
因此,儘管JIT幾乎不會給WordPress性能帶來巨大的改善,但它將將PHP升級到一個新的水平,從而使它現在可以直接編寫許多功能的語言。
不過,不利的一面是更大的複雜性,它可能導致維護,穩定性和調試成本增加。根據Dmitry Stogov的說法:
「 JIT非常簡單,但無論如何,它會增加整個PHP複雜性的水平,增加新錯誤的風險以及開發和維護成本。」
將JIT納入PHP 8的提案以50票對2票獲得通過。
PHP 8改進和新功能
除了JIT之外,我們還可以期望PHP 8帶來許多功能和改進。以下是我們精選的即將到來的新增內容和更改,這些內容應使PHP更加可靠和高效。
驗證抽象特徵方法
特性被定義為「一種在單一繼承語言(例如PHP)中代碼重用的機制」。通常,它們用於聲明可在多個類中使用的方法。
特徵也可以包含抽象方法。這些方法只是聲明方法的簽名,但是該方法的實現必須在使用trait的類中完成。
根據PHP手冊,
「特質支持對抽象方法的使用,以便對展出的類強加要求。」
這也意味著方法的簽名必須匹配。換句話說,所需參數的類型和數量必須相同。
無論如何,根據RFC的作者Nikita Popov的說法,簽名驗證目前僅是強制實施的:
- 在最常見的情況下,如果使用using類提供方法實現,則不強制實施:https://3v4l.org/SeVK3
- 如果實現來自父類,則將強制實施:https://3v4l.org/4VCIp
- 如果實現來自子類,則將強制實施:https://3v4l.org/q7Bq2
Nikita的以下示例涉及第一種情況(非強制籤名):
性狀T {
抽象公共功能測試(int $ x);
}
C級{
用T;
//允許,但不應由於類型無效而導致。
公共功能測試(字元串$ x){}
}
話雖如此,如果實現方法與抽象特徵方法不兼容,則無論其來源如何,該RFC都建議始終引發致命錯誤:
致命錯誤:第10行的/path/to/your/test.php中的C :: test(string $ x)聲明必須與T :: test(int $ x)兼容
該RFC已獲得一致批准。
使用WordPress,我們的流量增長了1,187%。
我們將向您展示如何。
加入20,000多個其他人,他們每周都會收到有關WordPress內部技巧的新聞!
現在訂閱
成功!感謝您的訂閱
您將在一周內收到下一期的Kinsta新聞通訊。
訂閱Kinsta新聞通訊
訂閱
我同意條款和條件以及隱私政策
不兼容的方法簽名
在PHP中,由於方法簽名不兼容而導致的繼承錯誤會引發致命錯誤或警告,具體取決於導致錯誤的原因。
如果類正在實現介面,則不兼容的方法簽名將引發致命錯誤。根據對象介面文檔:
「實現介面的類必須使用與LSP(Liskov替換原理)兼容的方法簽名。不這樣做將導致致命錯誤。」
這是一個帶有介面的繼承錯誤的示例:
介面我{
公共函數方法(數組$ a);
}
C類實現了我{
公共函數方法(int $ a){}
}
在PHP 7.4中,上面的代碼將引發以下錯誤:
致命錯誤:C :: method(int $ a)的聲明必須與第7行/path/to/your/test.php中的I :: method(array $ a)兼容
子類中具有不兼容簽名的函數將引發警告。請參閱RFC中的以下代碼:
C1級{
公共函數方法(數組$ a){}
}
C2類擴展了C1 {
公共函數方法(int $ a){}
}
在PHP 7.4中,以上代碼將簡單地發出警告:
警告:C2 :: method(int $ a)的聲明應與第7行/path/to/your/test.php中的C1 :: method(array $ a)兼容
現在,該RFC建議始終為不兼容的方法簽名引發致命錯誤。使用PHP 8,我們在上文中看到的代碼將提示以下內容:
致命錯誤:C2 :: method(int $ a)的聲明必須與第7行/path/to/your/test.php中的C1 :: method(array $ a)兼容
以負索引開頭的數組
在PHP中,如果數組以負索引開頭(start_index <0),則以下索引將從0開始(有關array_fill文檔的更多信息)。看下面的例子:
$ a = array_fill(-5,4,真);
var_dump($ a);
在PHP 7.4中,結果如下:
數組(4){
[-5]=>
布爾值(true)
[0]=>
布爾值(true)
[1]=>
布爾值(true)
[2]=>
布爾值(true)
}
現在,該RFC建議進行更改,以使第二個索引為start_index + 1,無論start_index的值如何。
在PHP 8中,上面的代碼將導致以下數組:
數組(4){
[-5]=>
布爾值(true)
[-4]=>
布爾值(true)
[-3]=>
布爾值(true)
[-2]=>
布爾值(true)
}
使用PHP 8,以負索引開頭的數組會更改其行為。在RFC中閱讀有關向後不兼容的更多信息。
聯合類型2.0
聯合類型接受可以是不同類型的值。目前,除?Type語法和特殊的可迭代類型外,PHP不支持聯合類型。
在PHP 8之前,聯合類型只能在phpdoc批註中指定,如RFC中的以下示例所示:
班級編號{
/ **
* @var int | float $ number
* /
私人$ number;
/ **
* @param int | float $ number
* /
公共功能setNumber($ number){
$ this-> number = $ number;
}
/ **
* @return int | float
* /
公共函數getNumber(){
返回$ this-> number;
}
}
現在,聯合體類型2.0 RFC提議在函數簽名中增加對聯合體類型的支持,這樣我們就不再依賴內聯文檔,而是改為使用T1 | T2 | …語法定義聯合體類型:
班級編號{
private int | float $ number;
公共函數setNumber(int | float $ number):void {
$ this-> number = $ number;
}
公共函數getNumber():int | float {
返回$ this-> number;
}
}
正如Nikita Popov在RFC中所述,
「通過該語言支持聯合類型,我們可以將更多類型信息從phpdoc移到函數簽名中,這具有通常的優點:
- 類型實際上是強制執行的,因此可以及早發現錯誤。
- 因為它們是強制性的,所以類型信息不太可能變得過時或遺漏邊緣情況。
- 在繼承過程中檢查類型,以執行Liskov替換原則。
- 可通過反射獲得類型。
- 語法比phpdoc少了很多。」
聯合類型支持所有可用類型,但有一些限制:
- void類型不能成為並集的一部分,因為void意味著函數不返回任何值。
- null類型僅在聯合類型中受支持,但不允許將其用作獨立類型。
- 也可以使用可為空的類型表示法(?T),表示T | null,但不允許在聯合類型中包含?T表示法(不允許使用?T1 | T2,而應使用T1 | T2 | null) 。
- 由於許多函數(例如strpos(),strstr(),substr()等)在可能的返回類型中包括false,因此也支持false偽類型。
您可以在RFC中閱讀有關聯合類型V2的更多信息。
內部函數的一致類型錯誤
傳遞非法類型的參數時,內部函數和用戶定義函數的行為會有所不同。
用戶定義的函數會引發TypeError,但內部函數會根據多種條件以多種方式運行。無論如何,典型的行為是發出警告並返回null。請參見PHP 7.4中的以下示例:
var_dump(strlen(new stdClass));
這將導致以下警告:
警告:strlen()期望參數1為字元串,第4行的/path/to/your/test.php中給出的對象
空值
如果啟用strict_types或參數信息指定類型,則行為將有所不同。在這種情況下,將檢測到類型錯誤並導致TypeError。
這種情況將導致許多問題,這些問題在RFC的問題部分中得到了很好的解釋。
為了消除這些不一致,此RFC建議使內部參數解析API在參數類型不匹配的情況下始終生成ThrowError。
在PHP 8中,上面的代碼引發以下錯誤:
致命錯誤:未被捕獲的TypeError:strlen():參數#1($ str)必須為字元串類型,對象在/path/to/your/test.php:4中給出
堆棧跟蹤:
#0 {main}
在第4行的/path/to/your/test.php中拋出
拋出表情
在PHP中,throw是一條語句,因此無法在只允許使用表達式的地方使用它。
該RFC建議將throw語句轉換為表達式,以便可以在允許使用表達式的任何上下文中使用。例如,箭頭函數,空合併運算符,三元和貓王運算符等。
請參閱RFC中的以下示例:
$ callable = fn()=>拋出新的Exception();
// $ value是不可為空的。
$ value = $ nullableValue?拋出新的InvalidArgumentException();
// $ value是真實的。
$ value = $ falsableValue?:拋出新的InvalidArgumentException();
弱地圖
弱映射是弱引用鍵的數據(對象)的集合,這意味著不會阻止對它們的垃圾回收。
PHP 7.4增加了對弱引用的支持,這是一種保留對對象的引用的方式,這種引用不會阻止對象本身被銷毀。正如Nikita Popov所指出的,
「原始的弱引用本身僅具有有限的用途,而弱映射在實踐中更為常用。由於未提供註冊銷毀回調的功能,因此不可能在PHP弱引用之上實現有效的弱映射。」
因此,該RFC引入了WeakMap類來創建用作弱映射鍵的對象,如果沒有其他對鍵對象的引用,則可以將其從弱映射中刪除。
在長時間運行的進程中,這將防止內存泄漏並提高性能。請參閱RFC中的以下示例:
$ map =新的WeakMap;
$ obj =新的stdClass;
$地圖[$obj] = 42;
var_dump($ map);
使用PHP 8,以上代碼將產生以下結果(請參見此處的代碼):
object(WeakMap)#1(1){
[0]=>
array(2){
[“key”]=>
object(stdClass)#2(0){
}
[“value”]=>
整數(42)
}
}
如果您取消設置對象,則鍵會自動從弱貼圖中刪除:
unset($ obj);
var_dump($ map);
現在的結果如下:
object(WeakMap)#1(0){
}
要仔細查看弱映射,請參閱RFC。該提案獲得一致通過。
參數列表中的尾部逗號
尾隨逗號是附加到不同上下文中項目列表的逗號。 PHP 7.2在列表語法中引入了結尾逗號,PHP 7.3在函數調用中引入了結尾逗號。
是否需要為您的網站提供快速,安全且對開發人員友好的託管服務? Kinsta在構建時就考慮了WordPress開發人員,並提供了許多工具和功能強大的儀錶板。查看我們的計劃
PHP 8現在在參數列表中以函數,方法和閉包形式引入尾隨逗號,如以下示例所示:
Foo類{
公共功能__construct(
字元串$ x,
int $ y,
float $ z,//逗號結尾
){
// 做一點事
}
}
該提案以58票對1票獲得通過。
在對象上允許:: class語法
為了獲取一個類的名字,我們可以使用Foo Bar :: class語法。該RFC建議將相同的語法擴展到對象,以便現在可以獲取給定對象的類的名稱,如下例所示:
$ object =新的stdClass;
var_dump($ object :: class); //「 stdClass」
$ object = null;
var_dump($ object :: class); // TypeError
在PHP 8中,$ object :: class提供的結果與get_class($ object)相同。如果$ object不是對象,則拋出TypeError異常。
這項建議獲得一致通過。
屬性v2
屬性,也稱為注釋,是結構化元數據的一種形式,可用於指定對象,元素或文件的屬性。
在PHP 7.4之前,文檔注釋是將元數據添加到類,函數等的聲明的唯一方法。現在,Attributes v2 RFC引入了PHP的屬性,這些屬性將其定義為結構化的語法元數據的形式,可以將其添加到類,屬性,函數,方法,參數和常量。
將屬性添加到它們所引用的聲明之前。請參閱RFC中的以下示例:
<
Foo類
{
<
public const FOO =’foo’;
<
公開$ x;
<
公共功能foo(<
}
$ object =新<
<
函數f1(){}
$ f2 = <
$ f3 = <
可以在文檔塊注釋之前或之後添加屬性:
<
/ ** docblock * /
<
函數foo(){}
每個聲明可以具有一個或多個屬性,並且每個屬性可以具有一個或多個關聯值:
<
<
<
函數foo(){}
請參閱RFC,以更深入地了解PHP屬性,用例和替代語法。請注意,屬性v2當前正在等待實施。
新的PHP函數
PHP 8為該語言帶來了幾個新功能:
str_contains
在PHP 8之前,strstr和strpos是開發人員在給定字元串中搜索針的典型選擇。問題是,這兩個功能並不是很直觀,新PHP開發人員對它們的使用可能會感到困惑。請參見以下示例:
$ mystring =’託管的WordPress託管’;
$ findme =「 WordPress」;
$ pos = strpos($ mystring,$ findme);
如果($ pos!== false){
回顯「已找到字元串」;
}其他{
回顯「未找到字元串」;
}
在上面的示例中,我們使用了!==比較運算符,該運算符還檢查兩個值是否屬於同一類型。如果針的位置為0,這可以防止我們出錯:
「此函數可能返回布爾FALSE,但也可能返回非布爾值,其值為FALSE。 […] 使用===運算符測試此函數的返回值。」
此外,一些框架提供了幫助程序功能來搜索給定字元串內的值(請參閱Laravel幫助程序文檔作為示例)。
現在,該RFC建議引入一個新功能,該功能允許在字元串內部進行搜索:str_contains。
str_contains(字元串$ haystack,字元串$ needle):bool
它的用法非常簡單。 str_contains檢查在$ haystack中是否找到$ needle,並相應返回true或false。
因此,感謝str_contains,我們可以編寫以下代碼:
$ mystring =’託管的WordPress託管’;
$ findme =「 WordPress」;
如果(str_contains($ mystring,$ findme)){
回顯「已找到字元串」;
}其他{
回顯「未找到字元串」;
}
這更易讀,更不容易出錯(請參見此處的代碼)。
在撰寫本文時,str_contains區分大小寫,但是將來可能會改變。
str_contains提案以43票對9票獲得通過。
str_starts_with()和str_ends_with()
除了str_contains函數之外,還有兩個新函數允許在給定字元串中搜索指針:str_starts_with和str_ends_with。
這些新函數檢查給定字元串是否以另一個字元串開頭或結尾:
str_starts_with(字元串$ haystack,字元串$ needle):bool
str_ends_with(字元串$ haystack,字元串$ needle):bool
如果$ needle比$ haystack長,則兩個函數均返回false。
根據該RFC的作者Will Hudgins所說,
「 str_starts_with和str_ends_with功能非常普遍,以至許多主要的PHP框架都支持它,包括Symfony,Laravel,Yii,FuelPHP和Phalcon。」
多虧了他們,我們現在可以避免使用次優且不太直觀的功能,例如substr,strpos。這兩個函數都區分大小寫:
$ str =「 WordPress」;
如果(str_starts_with($ str,「 Word」))回顯「 Found!」;
如果(str_starts_with($ str,「 word」))回顯「未找到!」;
您可以在此處查看此代碼的運行情況。
該RFC已以51到4票獲得批准。
get_debug_type
get_debug_type是一個新的PHP函數,它返回變數的類型。新函數的工作方式與gettype函數非常相似,但是get_debug_type返回本機類型名稱並解析類名稱。
這對語言是一個很好的改進,因為gettype()對類型檢查沒有用。
RFC提供了兩個有用的示例,以更好地理解新的get_debug_type()函數和gettype()之間的區別。第一個示例顯示了工作中的gettype:
$ bar = [1,2,3];
如果(!($ bar instanceof Foo)){
拋出新的TypeError(’Expected’。Foo :: class。’,got’。(is_object($ bar)?get_class($ bar):gettype($ bar)));
}
在PHP 8中,我們可以使用get_debug_type來代替:
如果(!($ bar instanceof Foo)){
拋出新的TypeError(’Expected’。Foo :: class。’got’。get_debug_type($ bar));
}
下表顯示了get_debug_type和gettype的返回值:
值 | gettype() | get_debug_type() |
---|---|---|
1個 | 整數 | 整型 |
0.1 | 雙 | 浮動 |
真正 | 布爾值 | 布爾 |
假 | 布爾值 | 布爾 |
空值 | 空值 | 空值 |
「 WordPress」 | 串 | 串 |
[1,2,3] | 數組 | 數組 |
名稱為「 Foo Bar」的類 | 賓語 | Foo Bar |
匿名班 | 賓語 | class @ anonymous |
其他RFC
在撰寫本文時,一些針對PHP 8的RFC仍在草擬和/或有待實施中。一旦它們的狀態更改為「已實施」,我們將立即添加它們。
這是PHP 8包含的其他已批准改進的快速列表:
- Stringable介面:此RFC引入了Stringable介面,該介面會自動添加到實現__to String()方法的類中。這裡的主要目標是使用string | Stringable聯合類型。
- ext / dom中的新DOM Living Standard API:此RFC建議通過引入新的介面和公共屬性,將當前的DOM Living Standard實現為PHP DOM擴展。
- 靜態返回類型:PHP 8在self和parent類型旁邊引入了static作為返回類型的用法。
- 變數語法調整:此RFC解決了PHP變數語法中的一些殘留不一致之處。
摘要
太好了!在本文中,我們介紹了PHP 8發行版中預期的所有關鍵更改和改進。其中最值得期待的肯定是Just in Time編譯器,但是PHP 8附帶了更多功能。
請確保將此博客帖子添加為書籤,因為我們將在收藏夾被批准後立即將其添加到列表中。 ?
現在輪到您了:您準備好測試即將推出的PHP功能嗎?哪一個是你的最愛?在下面的評論部分添加一行。
如果您喜歡這篇文章,那麼您會喜歡Kinsta的WordPress託管平台。加速您的網站並獲得我們經驗豐富的WordPress團隊的24/7支持。我們基於Google Cloud的基礎架構專註於自動擴展,性能和安全性。讓我們向您展示Kinsta的與眾不同!查看我們的計劃