我接觸 PHP 蠻久了,大概從 5.1 還是 5.2 開始用到現在 8.4,避開了聽說很雷的 PHP 4,看著 PHP 引進新的功能,到現在社群開發出各種神奇的工具。
回顧過去一些事情,我覺得很重要的事情,不只可以了解 PHP 是什麼樣子程式語言,或許也可以協助判斷其他程式語言是否適合在你手上的專案使用。
就我所知,PHP 4 到 5 主要是支援物件導向,開始可以使用 class 來設計自己所需的功能,不用擔心 function name 和其他人的衝突。在這個時候,大家可以各自在網路上下載別人寫好的類別來使用,例如那個時候「藍色小舖」就有很多工程師分享自己實做的功能,例如 MySQL driver 之類的。
一些大型的專案仍保有這類的程式,像是 dolibarr ERP 的 /htdocs/core/class
目錄下,就有多個作者實做的 class。
在這個時候,功能比較複雜的專案可能會遇到類似的問題,我想要 require
新的 class 時,發現該 class 也有 require
其他的 class,如 dolibarr 的 fileupload.class.php
:
<?php /* Copyright (C) 2011-2022 Regis Houssin * .... */ require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; /** * This class is used to manage file upload using ajax */ class FileUpload {
在 FileUpload
中需要使用到 files
和 images
這二個類別。
若現有的專案,剛好有這二個類別時,會遇到類別名稱衝突,導致 require
失敗,導致 FileUpload
沒有辦法使用。這個問題其實和更早期 PHP 的狀況相同,大家各自實做自己的 function、和別人分享 function,卻又怕遇到相同的 function 名稱導致衝突。
為了解決 dependency (相依性、依賴關係) 問題,PHP 5.3 開始支援 namespace。
透過 namespace 功能,大家為 class 和 function 設定各自的 namespace,如:
<?php namesace Zeroplex; class FileUpload { }
雖然 class 名稱相同,但只要在不同的 namespace 就不會導致衝突。
再故事往下之前提一下 autoload 功能,當 require 寫到手軟時,就開始有人開始想各種偷懶的方法了。
PHP 的 autoload 功能,允許使用者在建議物件時,針對自訂的邏輯,決定要做什麼事。所以這讓帶來幾個好處:
- 只要先定好規則,就可以透過類別名稱,找到需要 require 的檔案
- 透過 autoload,在需要使用時再 require 檔案
當這個功能合併使用時,就獲得合併技能「要使用時在 require」,不僅可以讓程式碼可讀性增加,也可以減少 disk I/O、記憶體使用量。
例如我透過 spl_autoload_register() 先定義好 require 規則:
<?php spl_autoload_register(function ($className) { $classInfo = explode('/', $className); $namespace = $classInfo[0]; $className = $classInfo[1]; echo "namespace: " . $namespace . "\n"; echo "class name: " . $className . "\n"; require __DIR__ . "/library/" . $namespace . "/" . $className . ".php"; }
上述設定會自動將 Zeroplex/FileUploader
拆解成 namespace 和檔案名稱並 require,執行程式時會在 new 階段透過 autoloader 來決定要去哪裡 require 檔案:
<?php $uploader = new Zeroplex\FileUploader(); namespace: Zeroplex class name: FileUploader
在百家爭鳴寫 library 時,還會遇到一個問題:A 和 B library 都需要用到 C library,但 A library 中任何版本的 C library 都可,而 B library 則需要 C library 的最新版。這樣開發程式時,就還需要考慮到底要使用 C library 的哪一個版本。
於是 composer 就出現了。composer 是 dependency management,會自動尋找專案中各 library 可正確運作的版本。
時間往後走一段時間, 來到 PHP 7。
PHP 7 做的最大改變,是大幅改寫直譯器,讓效能大幅提升,而且不只是快一點點而已,是快了 100%。以前為了讓 PHP 程式跑得更快,Facebook 開發了 hhvm,現在用不到了。
若想知道為什麼效能可以快這麼多,可以參考 Nikita Popov (PHP 核心工程師之一) 的簡報:PHP 7 – What changed internally?。或是聽他的演講:
除了效能以外,PHP 在此時也慢慢的提供一些資料型態的定義、檢查功能,像是在撰寫 function 時可以定義 argument 的資料型態 (type hint),除了 scalar type 也開始支援使用者自訂 class 的支援。
這些支援,讓原本是 dynamic typing 的 PHP 可以多一些檢查,程式的可讀性和穩定性上都有改善。
PHP 7 到 8 我打算很不負責任得什麼都不說直接跳過 XD
這邊列出一些有趣的專案:
- 在 awesome-php 上有很多有趣的專案
- 如果覺得 PHP 效能不佳,可以試試看 Swoole 以及 Phalcon,這二個 framework 使用 C/C++ 實作,在 phpize 與 PHP 串接。
- 其實有了 interface,不只有 C/C++,連 Rust 也可以開發 PHP extension
- PHP 沒有 non-blocking I/O?其實也有,實作方法換一下即可 ReactPHP