我已經有將近二十年的程式設計經驗,並使用過各種程式語言進行開發。在我以前做過的許多工作和現在正在做的工作中,我非常高興能夠將 PHP 作為核心程式語言。從第一次使用 PHP 工作開始,我就聽到了關於 PHP 的各種抱怨,但同時我也看到了 PHP 的力量。
PHP 至少是一門有趣的程式語言。這門語言和用它所建構的程式通常屬於兩種設計哲學。在這裡,我所說的並非軟體開發生命週期,如瀑布或敏捷,而是關於軟體應該是什麼樣的基本想法。這些想法被稱為「正確的方式」(The Right Way)和 「更糟就是更好」(Worse is better)。
PHP 又是一門相當奇怪的程式語言。當人們抱怨這門語言「很槽糕」時,並沒有說錯。這門語言確實有很多不好的地方。擱在以前,這門語言還有更多糟糕的問題。嘲笑 PHP 的部落格文章《全面解析 PHP 的槽糕設計》(PHP: a fractal of bad design)確實有幾個正確的觀點,即使這些觀點在九年前發表時就已經過時了。
然而,同時,開發人員卻可以利用 PHP 創建結構上「正確」的軟體,並從其他語言引入被視為良好實踐的哲學。像 Laminas 和 Symfony 這樣的框架就使用了物件導向程式設計的最佳實踐,使開發者可以用這些框架編寫結構正確的程式碼。
PHP 是怎麼做到這些的?這是因為 PHP 是最糟糕的程式語言。
設計軟體
1991 年,Richard P. Gabriel 發表了一篇文章《Lisp:好消息,壞消息,如何贏得大》(Lisp: Good News, Bad News, How to Win Big)。這篇文章的論點是,在軟體設計和壽命方面,「更糟就是更好」的哲學將是更好的選擇。他之所以得出這一結論,是因為他意識到出現了兩種不同的程序設計流派,他分別將之命名為“麻省理工學院/ 斯坦福風格”(MIT/Standford Style),或者“正確的方式”,以及“新澤西風格”(New Jersey Style)或“更糟就是更好”。
這兩種哲學的目標相似,但在關鍵領域卻有所不同。兩種風格都著重於哲學理念的四個關鍵領域:簡單性(Simplicity)、正確性(Correctness)、一致性(Consistency)和完整性(Completeness)。
麻省理工學院風格是這樣描述的:
簡單性:設計一定要簡單,不論它的實現還是接口,都一定要簡單。相較而言,讓介面保持簡單更重要。
正確性:在所有可以觀察到的各個層面,設計一定要正確。不要妄想做一個不正確的設計。
一致性:設計一定不能是不一致的。為了確保一致性,你可以稍微犧牲簡單性和完整性。一致性和正確性同等重要。
完整性:設計一定要盡可能涵蓋重要的情況。所有符合預期的情況一定要被覆蓋到。完整性優先權應該高於簡單性。
至於新澤西風格,Gabriel 說,它將其目標定義為:
簡單:設計一定要簡單,無論它的實現還是接口,都一定要簡單。而相較而言,讓實作保持簡單更重要。簡單是最重要的,其他的特性不如保持簡單更重要。
正確性:在所有可以觀察到的方面,設計一定要正確。但可以為了簡單而輕微犧牲正確性。
一致性:設計一定不能太不一致。某些情況下,為了確保簡單可以犧牲一致性。如果將某個不常見的情況引入設計,會導致實作變得複雜或不一致,那麼就不要考慮這種情況。
完整性:設計一定要盡可能涵蓋重要的情況。所有符合預期的情況一定要被覆蓋到。完整性可以為任何其他特性讓步。實際上,一旦威脅到實現的簡單性,完整性必須被犧牲。如果為了保持簡單,可以犧牲一致性來實現完整性;尤其是介面的一致性。
這場爭論的關鍵是用 LISP 和 C 作為例子來說明為什麼「更糟就是更好」。對於 LISP 程式設計師 Gabriel 來說,LISP 是一種比 C 更好的語言,速度和 C 一樣快,而且 Common LISP 的設計、開發和標準化已經花了很多年。定義語言的規範吸收了所有不同的 LISP 的精華,而現代開發環境對 LISP 開發者來說是最好的。
LISP 是正確的方式
LISP 代表了軟體開發的「正確的方式」。 LISP 易於交互,你可以透過各種方式與它互動。希望從 Fortran 調用 LISP?你可以從 Fortran 中呼叫 LISP 並將資料傳入,反之亦然。在使用遺留程式碼時,你可以愉快地使用 LISP 的所有現代「豪華」特性。
LISP 擁有一致的設計,這得益於它的規範。假如你研究 Python 這樣的現代語言,規範在提供多個後端和編譯器方面有很大的作用,而且它們都以同樣的方式解釋或編譯程式碼。這些工具是一流的,1991 年的 LISP 擁有我們今天仍然享受的所有舒適,例如步驟調試、數據檢查和花哨的編輯器。
作為一種語言,LISP 是完備的。它具有先進的物件導向程式設計層、多重繼承、一流的物件以及函數和類型。 LISP 似乎是開發人員心中想要的程式語言。
1991 年,LISP 這麼程式語言可能處於有史以來的最佳狀態。這種技術上的正確性並沒有被實際使用所證實。 LISP 的開發商正在衰退。多年來負面新聞和錯誤定位阻礙了 LISP 的外部聲譽。人們不再將其視為向最終用戶交付軟體的方式。
就開發而言,LISP 往往代表著許多與「大規模預先設計」(Big Design Up Front,BDUF)一樣的理想。如果你曾經使用過瀑布模型(Waterfall Model)這樣的設計方法,你會發現一些問題。 「正確的方式」非常強調一致性、正確性,並確保考慮到所有能想到的問題。
LISP 本身並非單一的語言,而是語言家族。儘管 Common LISP 被設計成一種標準,但是 LISP 本身的實現方式是根據需要完成的各種工作而存在的。 Lockless Inc 網站上的一篇文章指出,這種「分散化」是 LISP 最終失敗的決定因素之一。儘管 LISP 堅持軟體設計的“正確的方式”,但這種碎片化導致程式碼維護和可移植性都受到了影響。
C 和 Unix 是錯誤的方式
#同時,由於 Unix 的出現,C 語言逐漸成為軟體開發的首選方法。 C 語言是為 Unix 設計的,而 Unix 是用 C 語言設計的。它的開發人員與麻省理工學院的 LISP 及其作者有著不同的設計立場。
在 1972 年,C 語言被設計成一種簡單的語言。到 1991 年,它已經發生了一些變化,但是 C 語言的基本原則並沒有改變。一些特性是為了滿足開發者和 Unix 的需求而添加的。因為語言很簡單,所以寫編譯器和程式很容易。儘管這種語言並不會妨礙你進行複雜的編程,但是與 LISP 相比,C 語言估計只有程式設計師所需的 50-80% 特性。
但是, C 語言卻有很強的可移植性。相對於常用於 LISP 軟體和環境的硬件,它也可以運行在低功率硬體上。這一因素使得它可以在更廣泛的機器上編譯和運行軟體。 C 語言和 Unix 很容易使用,Gabriel 認為 Unix 和 C 語言會像病毒一樣流行起來。
在 Dennis Ritchie 設計和建構 Unix 的過程中,C 語言得到了發展。因為貝爾實驗室(Bell Labs)不被允許正式進入電腦領域,所以 Unix 也可以輕鬆地分發給各種不同的使用者。這些用戶協助修補 Unix 以滿足他們自己的需求。 Dennis Ritchie 能夠根據需求將這些修補程式整合在一起,而不必事先考慮這些需求。
與 LISP 不同,C 至今仍被大量使用。儘管高階的解釋性語言,如 PHP、JavaScript 和 Python 是許多開發者的首選,但這些高階語言很多都是用 C 語言開發的。即使像 Rust 這樣的競爭對手開始嶄露頭角,但能夠在小型低功率設備上運行仍然是 C 語言的優勢。
PHP 是最槽糕的
#因此,「更糟就是更好」的軟體首先會被接受,其次它會使用戶期望更少,第三,這些軟體將被不斷改進,直到接近「正確的方法」的程度。
——Richard Gabrie
在這啟示的幾年後,Rasmus Lerdorf 開始研究個人首頁 / 表單解釋器,也就是我們現在所知的 PHP。 PHP/FI 的誕生是因為 Lerdorf 需要維護他的主頁,並與表單和資料庫互動。 PHP/FI 甚至不是作為實際的程式語言設計的,而是作為 C 語言之上的一層腳本和函數設計的。
PHP 很簡單
設計一定簡單,不論是它的實作還是介面。
PHP 底層使用了 C 語言,我們之前已經說過,這部分是「最糟糕的」。然而,這也帶來了一些優勢,最重要的是,更簡單的底層語言可以讓它更容易擴展。雖然 Hack/HHVM 採用了更多的 C 方法,但 PHP 本身仍然是 C 語言。
只需短短幾個小時就能學完這門語言的內部結構。 Elizabeth Smith 發表過一篇關於 PHP 擴充功能的精彩演講,其中介紹了大量關於 PHP 的內部運作原理。這門語言本身借鑒了其他 C 風格的語言,不僅易於閱讀,並且能夠跟隨 C 風格的其他語言。
PHP 的大多數接口,或者說標準庫,都非常簡單,因為大多數核心功能都只不過是包裝了各種 C 語言庫,然後幾乎原封不動地公開出來。儘管這樣做會導致介面上的一些不一致,但是它為來自 C 或 C 的開發者提供了一個熟悉的環境。
PHP 語言非常注重於 Web 開發。將 HTTP 中的概念提取出來並在語言中找到相似的概念通常非常簡單。希望了解一個要求的頭資訊嗎? get_headers() 就能滿足你。取得請求資訊就像讀取 $_GET 和 $_POST 全域變數一樣簡單。
PHP 保持了簡單的開發者接口,並且盡可能地保持內部結構的簡單。
PHP(幾乎)是正確的
在所有可以觀察到的方面,設計一定要正確。但可以為了簡單性而輕微犧牲正確性。
在這裡,PHP 傾向於選擇「簡單」而不是正確。在 HHVM 出現之前,語言的外觀和特性一直沒有被規範。 Zend 解釋器本身就是規範,而這門語言的行為方式總是 「正確」的(不包括實際的錯誤)。要用別的東西來取代 PHP 引擎,就必須實現現有引擎的所有特性。
許多核心函數的 LAX 函數參數和傳回型別都使得系統的工作更容易。像 strpos() 這樣的函數傳回值可以是整數數或布林值,相對於嚴格設計成傳回整數數或拋出例外的方法,處理要稍微容易一些。
看 PHP 語言的發展,幾乎所有新特性都是建立在開發人員需要的基礎上,而不是「因為它錯了所以必須修復」的嚴肅想法。更專注於那些嚴格類型和異常錯誤是一種更正確的做事方法。然而,還有一些東西,像是簡短的箭頭函數(arrow function)、屬性和枚舉,才是開發者想要用來簡化程式碼的東西。
PHP 不需要一致性
#設計一定不能太不一致。某些情況下,為了保持簡單可以犧牲一致性。
我甚至不打算假裝 PHP 是一致的,但它的一致性已經足夠了。當涉及到陣列與字串函數時,人們可能會抱怨 needle/haystack 參數順序。不過,一般而言,陣列函數是一致的,而字串函數也是一致的。與底層 C 函式庫保持一致比在語言中保持一致要簡單得多。
PHP 在其他方面也足夠一致。正如我在 strpos() 中提到的,PHP 對於遇到錯誤的函數往往會相當一致地傳回 FALSE。這未必是正確的,但它卻是一致的。帶下劃線和不帶下劃線的函數名通常都會匹配其基礎庫。
為了簡單起見, PHP 語言犧牲了一致性,但即使沒有這個規範,它仍然努力在有意義的地方保持一致。
PHP 的完整性符合所需
設計一定要盡可能涵蓋重要的情況。
無論何時,在針對 PHP 需求最大的設計任務:撰寫 Web 應用程式時,PHP 都是完整的。 PHP 從未被設計成一種可以適用於程式設計世界所有問題的語言。儘管如此,它的簡單性還是使它可以用於 Web 以外的場合。 PHP 最初的目的就是為 Web 程式設計提供最基本的功能,而這趨勢一直持續至今。
修改核心語言通常是由開發人員的需求所驅動。整個社區提出修改意見,然後經由社區投票,決定新特性被拒絕、改變或接受。該語言的許多創新都源自於快速完成工作的需要。即便我們吸收了其它語言的功能,也是因為它使我們的開發變得簡單,而很少是因為其他語言做得「更正確」。
今天,你可以用 PHP 開發 Web 應用程式。五年後,你仍然可以用 PHP 開發 Web 應用程序,只不過會增加一些新功能。但是,語言本身的完整性已經符合今天所需。如果未來有需要,我們可以隨時修改語言或為它添加新功能。
更糟就是更好嗎?
#Gabriel 承認,「更糟就是更好」的哲學指的是設計看起來很糟糕,也許不應該作為更好的選擇。唯一的問題是,當他審視這兩種哲學時,與麻省理工學院/“正確的方式”的設計哲學相比,“更糟就是更好”最終仍然是更靈活的選擇,“具有更好的生存特性」。如果我們看一下 PHP,就可以證實「更糟就是更好」這個觀點。
多年來,Gabriel 承認他在哪種方式更好之間搖擺不定。 PHP 社群一直在爭論我們是應該正確地做事還是繼續簡單地做事。我們有像 Laminas 這樣的框架,以經典的電腦科學方式建立函式庫,然後我們有像 Laravel 這樣的框架,專注於開發者的體驗和速度。 PHP 本身二者兼具。
下次再聽到有人罵 PHP 的時候,就隨他噴去吧。這門語言確實很糟糕。但從許多方面來看,PHP 的長壽和廣泛使用證明了這樣一個事實:用「正確的方式」做事並不總是比用「最糟糕」的方式做事好。當有人吐槽你正在使用的框架時,你要明白從長遠來看這並不重要。選擇一種你認為適合自己的設計哲學,並欣然接受這一點:更糟的可能實際上更好。
原文連結:https://www.phparch.com/2021/09/education-station-php-is-the-worst/
原作者:Chris Tankersley
作者介紹:
Chris Tankersley 有許多角色:丈夫、父親、作家、演講家、播客主持人和PHP 開發者。 Chris 在 12 年的程式設計生涯中使用 了很多種不同的框架和語言,但是他一天的大部分時間都在使用 PHP 和 Python。他是《Docker for Developers(尚未有中文版)的作者,並與公司和開發者合作,將容器整合到他們的工作流程中。
推薦學習:《PHP影片教學》