為什么說PHP是很糟糕的,也是很好的編程語言
PHP 又是一門相當(dāng)奇怪的編程語言。當(dāng)人們抱怨這門語言“很槽糕”時(shí),他們并沒有說錯(cuò)。這門語言確實(shí)有很多不好的地方。
擱在以前,這門語言還有更多糟糕的問題。嘲笑 PHP 的博文《全面解析 PHP 的槽糕設(shè)計(jì)》(PHP: a fractal of bad design)確實(shí)有幾個(gè)正確的觀點(diǎn),即使這些觀點(diǎn)在九年前發(fā)表時(shí)就已經(jīng)過時(shí)了。
然而,與此同時(shí),開發(fā)人員卻可以利用 PHP 創(chuàng)建結(jié)構(gòu)上“正確”的軟件,并從其他語言中引入被視為良好實(shí)踐的哲學(xué)。
像 Laminas 和 Symfony 這樣的框架就使用了面向?qū)ο缶幊痰淖罴褜?shí)踐,使開發(fā)者可以用這些框架編寫結(jié)構(gòu)正確的代碼。
PHP 是怎么做到這些的?這是因?yàn)?PHP 是最糟糕的編程語言。
設(shè)計(jì)軟件
1991 年,Richard P. Gabriel 發(fā)表了一篇文章《Lisp:好消息,壞消息,如何贏得大》(Lisp: Good News, Bad News, How to Win Big)。這篇文章的論點(diǎn)是,在軟件設(shè)計(jì)和壽命方面,“更糟就是更好”的哲學(xué)將是更好的選擇。
他之所以得出這一結(jié)論,是因?yàn)樗庾R(shí)到出現(xiàn)了兩種不同的程序設(shè)計(jì)流派,他分別將之命名為“麻省理工學(xué)院 / 斯坦福風(fēng)格”(MIT/Standford Style),或者“正確的方式”,以及“新澤西風(fēng)格”(New Jersey Style)或者“更糟就是更好”。
這兩種哲學(xué)的目標(biāo)相似,但在關(guān)鍵領(lǐng)域卻有所不同。兩種風(fēng)格都側(cè)重于哲學(xué)理念的四個(gè)關(guān)鍵領(lǐng)域:簡單性(Simplicity)、正確性(Correctness)、一致性(Consistency)和完整性(Completeness)。
麻省理工學(xué)院風(fēng)格是這樣描述的:
- 簡單性:設(shè)計(jì)一定要簡單,不論它的實(shí)現(xiàn)還是接口,都一定要簡單。相較而言,讓接口保持簡單更重要。
- 正確性:在所有可以觀察到的方方面面,設(shè)計(jì)一定要正確。不要妄想做一個(gè)不正確的設(shè)計(jì)。
- 一致性:設(shè)計(jì)一定不能是不一致的。為了確保一致性,你可以略微犧牲簡單性和完整性。一致性和正確性同等重要。
- 完整性:設(shè)計(jì)一定要盡可能多地涵蓋重要的情況。所有符合預(yù)期的情況一定要被覆蓋到。完整性優(yōu)先級(jí)應(yīng)該高于簡單性。
至于新澤西風(fēng)格,Gabriel 說,它將其目標(biāo)定義為:
- 簡單性:設(shè)計(jì)一定要簡單,不論它的實(shí)現(xiàn)還是接口,都一定要簡單。而相較而言,讓實(shí)現(xiàn)保持簡單更重要。簡單是最重要的,其他的特性都不如保持簡單更重要。
- 正確性:在所有可以觀察到的方面,設(shè)計(jì)一定要正確。但是可以為了簡單而輕微犧牲正確性。
- 一致性:設(shè)計(jì)一定不能太過不一致。某些情況下,為了保證簡單可以犧牲一致性。如果將某個(gè)不常見的情況引入設(shè)計(jì),會(huì)導(dǎo)致實(shí)現(xiàn)變復(fù)雜或者不一致,那么就不要考慮這種情況。
- 完整性:設(shè)計(jì)一定要盡可能多地涵蓋重要的情況。所有符合預(yù)期的情況一定要被覆蓋到。完整性可以為任何其他特性讓步。實(shí)際上,一旦威脅到實(shí)現(xiàn)的簡單性,完整性必須要被犧牲。如果為了保持簡單,可以犧牲一致性來實(shí)現(xiàn)完整性;尤其是接口的一致性。
這場(chǎng)爭(zhēng)論的關(guān)鍵是用 LISP 和 C 作為例子來說明為什么“更糟就是更好”。對(duì)于 LISP 程序員 Gabriel 來說,LISP 是一種比 C 更好的語言,速度和 C 一樣快,而且 Common LISP 的設(shè)計(jì)、開發(fā)和標(biāo)準(zhǔn)化已經(jīng)花了很多年。
定義該語言的規(guī)范吸取了所有不同的 LISP 的精華,而現(xiàn)代開發(fā)環(huán)境對(duì)于 LISP 開發(fā)者來說是最好的。
LISP 是正確的方式
LISP 代表了軟件開發(fā)的“正確的方式”。LISP 易于交互,你可以通過各種方式與它交互。希望從 Fortran 中調(diào)用 LISP?
你可以從 Fortran 中調(diào)用 LISP 并將數(shù)據(jù)傳入,反之亦然。在使用遺留代碼時(shí),你可以愉快地使用 LISP 的所有現(xiàn)代“豪華”特性。
LISP 擁有一致的設(shè)計(jì),這得益于它的規(guī)范。假如你研究一下 Python 這樣的現(xiàn)代語言,規(guī)范在提供多個(gè)后端和編譯器方面有很大的作用,而且它們都以同樣的方式解釋或編譯代碼。
這些工具是一流的,1991 年的 LISP 擁有我們今天仍然享受的所有舒適,比如步驟調(diào)試、數(shù)據(jù)檢查和花哨的編輯器。
作為一種語言,LISP 是完備的。它具有先進(jìn)的面向?qū)ο缶幊虒?、多重繼承、一流的對(duì)象以及函數(shù)和類型。LISP 似乎是開發(fā)人員心中想要的編程語言。
1991 年,LISP 這么編程語言可能處于有史以來的最佳狀態(tài)。這種技術(shù)上的正確性并沒有被實(shí)際使用所證實(shí)。
LISP 的開發(fā)商正在衰退。多年來負(fù)面新聞和錯(cuò)誤定位阻礙了 LISP 的外部聲譽(yù)。人們不再將其視為向最終用戶交付軟件的方式。
就開發(fā)而言,LISP 往往代表著許多與“大規(guī)模預(yù)先設(shè)計(jì)”(Big Design Up Front,BDUF)一樣的理想。
假如你曾經(jīng)使用過瀑布模型(Waterfall Model)這樣的設(shè)計(jì)方法,你就會(huì)發(fā)現(xiàn)一些問題。“正確的方式”非常強(qiáng)調(diào)一致性、正確性,并確保考慮到所有能想到的問題。
LISP 本身并非一種單一的語言,而是一個(gè)語言家族。盡管 Common LISP 被設(shè)計(jì)成一種標(biāo)準(zhǔn),但是 LISP 本身的實(shí)現(xiàn)方式是根據(jù)需要完成的各種工作而存在的。
Lockless Inc 網(wǎng)站上的一篇文章指出,這種“碎片化”是 LISP 最終失敗的決定因素之一。盡管 LISP 堅(jiān)持軟件設(shè)計(jì)的“正確的方式”,但是這種碎片化導(dǎo)致代碼維護(hù)和可移植性都受到了影響。
PHP 是最槽糕的
因此,“更糟就是更好”的軟件首先會(huì)被接受,其次它會(huì)使用戶期望更少,第三,這些軟件將被不斷改進(jìn),直到接近“正確的方法”的程度。——Richard Gabrie
在這一啟示的幾年后,Rasmus Lerdorf 開始研究個(gè)人主頁 / 表單解釋器,也就是我們現(xiàn)在所知的 PHP。
PHP/FI 的誕生是因?yàn)?Lerdorf 需要維護(hù)他的主頁,并與表單和數(shù)據(jù)庫進(jìn)行交互。PHP/FI 甚至不是作為一種實(shí)際的編程語言設(shè)計(jì)的,而是作為 C 語言之上的一層腳本和函數(shù)設(shè)計(jì)的。
PHP 很簡單
設(shè)計(jì)一定要簡單,不論是它的實(shí)現(xiàn)還是接口。
PHP 底層使用了 C 語言,我們之前已經(jīng)說過,這部分是“最糟糕的”。然而,這也帶來了一些優(yōu)勢(shì),最重要的是,更簡單的底層語言可以讓它更容易擴(kuò)展。雖然 Hack/HHVM 采用了更多的 C++ 方法,但 PHP 本身仍然是 C 語言。
只需短短幾個(gè)小時(shí)就能學(xué)完這門語言的內(nèi)部結(jié)構(gòu)。Elizabeth Smith 發(fā)表過一篇關(guān)于 PHP 擴(kuò)展的精彩演講,其中介紹了大量關(guān)于 PHP 的內(nèi)部工作原理。這門語言本身借鑒了其他 C 風(fēng)格的語言,不僅易于閱讀,并且能夠跟 C 風(fēng)格的其他語言互相轉(zhuǎn)換。
PHP 的大多數(shù)接口,或者說標(biāo)準(zhǔn)庫,都非常簡單,因?yàn)榇蠖鄶?shù)核心功能都只不過是包裝了各種 C 語言庫,然后幾乎原封不動(dòng)地公開出來。盡管這樣做會(huì)導(dǎo)致接口上的一些不一致,但是它為來自 C 或 C++ 的開發(fā)者提供了一個(gè)熟悉的環(huán)境。
PHP 語言非常注重于 Web 開發(fā)。將 HTTP 中的概念提取出來并在語言中找到相似的概念通常非常簡單。希望了解一個(gè)請(qǐng)求的頭信息嗎?get_headers() 就能滿足你。獲取請(qǐng)求信息就像讀取 _POST 全局變量一樣簡單。
PHP 保持了簡單的開發(fā)者接口,并且盡可能地保持內(nèi)部結(jié)構(gòu)的簡單。
PHP(幾乎)是正確的
在所有可以觀察到的方面,設(shè)計(jì)一定要正確。但是可以為了簡單性而輕微犧牲正確性。
在這里,PHP 傾向于選擇“簡單”而不是正確。在 HHVM 出現(xiàn)之前,語言的外觀和特性一直沒有得到規(guī)范。
Zend 解釋器本身就是規(guī)范,并且這門語言的行為方式總是 “正確”的(不包括實(shí)際的錯(cuò)誤)。要想用別的東西代替 PHP 引擎,就必須實(shí)現(xiàn)現(xiàn)有引擎的所有特性。
許多核心函數(shù)的 LAX 函數(shù)參數(shù)和返回類型都使得系統(tǒng)的工作更容易。像 strpos() 這樣的函數(shù)返回值可以是整型數(shù)或布爾值,相對(duì)于嚴(yán)格設(shè)計(jì)成返回整型數(shù)或拋出異常的方法,處理要稍微容易一些。
看 PHP 語言的發(fā)展,幾乎所有新特性都是建立在開發(fā)人員需要的基礎(chǔ)上,而不是“因?yàn)樗e(cuò)了所以必須修復(fù)”的嚴(yán)肅想法。
更多地關(guān)注那些嚴(yán)格類型和異常錯(cuò)誤是一種更正確的做事方法。然而,還有一些東西,比如簡短的箭頭函數(shù)(arrow function)、屬性和枚舉,才是開發(fā)者想要用來簡化代碼的東西。
PHP 不需要一致性
設(shè)計(jì)一定不能太過不一致。某些情況下,為了保持簡單可以犧牲一致性。
我甚至不打算假裝 PHP 是一致的,但是它的一致性已經(jīng)足夠了。當(dāng)涉及到數(shù)組與字符串函數(shù)時(shí),人們可能會(huì)抱怨 needle/haystack 參數(shù)順序。
不過,一般而言,數(shù)組函數(shù)是一致的,而字符串函數(shù)也是一致的。與底層 C 庫保持一致比在語言中保持一致要簡單得多。
PHP 在其他方面也足夠一致。正如我在 strpos() 中提到的,PHP 對(duì)于遇到錯(cuò)誤的函數(shù)往往會(huì)相當(dāng)一致地返回 FALSE。這未必是正確的,但它卻是一致的。帶下劃線和不帶下劃線的函數(shù)名通常都會(huì)匹配其基礎(chǔ)庫。
為了簡單起見, PHP 語言犧牲了一致性,但是即使沒有這個(gè)規(guī)范,它仍然努力在有意義的地方保持一致。
PHP 的完整性符合所需
設(shè)計(jì)一定要盡可能多地涵蓋重要的情況。
無論何時(shí),在針對(duì) PHP 需求最大的設(shè)計(jì)任務(wù):編寫 Web 應(yīng)用程序時(shí),PHP 都是完備的。PHP 從未被設(shè)計(jì)成一種可以適用于編程世界所有問題的語言。
盡管如此,它的簡單性還是使它可以用于 Web 以外的場(chǎng)合。PHP 最初的目的就是為 Web 編程提供最基本的功能,這一趨勢(shì)一直持續(xù)至今。
修改核心語言通常是由開發(fā)人員的需求驅(qū)動(dòng)。整個(gè)社區(qū)提出修改意見,然后經(jīng)由社區(qū)投票,決定新特性被拒絕、改變或者接受。該語言的許多創(chuàng)新都源于快速完成工作的需要。
即便我們吸收了其它語言的功能,也是因?yàn)樗刮覀兊拈_發(fā)變得簡單,而很少是因?yàn)槠渌Z言做得“更正確”。
今天,你可以用 PHP 開發(fā) Web 應(yīng)用程序。五年后,你仍然可以用 PHP 開發(fā) Web 應(yīng)用程序,只不過會(huì)增加一些新特性。
但是,語言本身的完整性已經(jīng)符合今天所需。如果未來有需要,我們可以隨時(shí)修改語言或?yàn)樗砑有鹿δ堋?/p>
更糟就是更好嗎?
Gabriel 承認(rèn),“更糟就是更好”的哲學(xué)指的是設(shè)計(jì)看起來很糟糕,也許不應(yīng)該作為更好的選擇。唯一的問題是,當(dāng)他審視這兩種哲學(xué)時(shí),與麻省理工學(xué)院 /“正確的方式”的設(shè)計(jì)哲學(xué)相比,“更糟就是更好”最終仍然是更靈活的選擇,“具有更好的生存特性”。如果我們看一下 PHP,就可以證實(shí)“更糟就是更好”這一觀點(diǎn)。
這些年來,Gabriel 承認(rèn)他在哪種方式更好之間搖擺不定。PHP 社區(qū)一直在爭(zhēng)論我們是應(yīng)該正確地做事還是繼續(xù)簡單地做事。
我們有像 Laminas 這樣的框架,以經(jīng)典的計(jì)算機(jī)科學(xué)方式構(gòu)建庫,然后我們有像 Laravel 這樣的框架,關(guān)注開發(fā)者的體驗(yàn)和速度。PHP 本身二者兼具。
下次再聽到有人罵 PHP 的時(shí)候,就隨他噴去吧。這門語言確實(shí)很糟糕。但從許多方面來看,PHP 的長壽和廣泛使用證明了這樣一個(gè)事實(shí):用“正確的方式”做事并不總是比用“最糟糕”的方式做事好。
當(dāng)有人吐槽你正在使用的框架時(shí),你要明白從長遠(yuǎn)來看這并不重要。選擇一種你認(rèn)為適合自己的設(shè)計(jì)哲學(xué),并欣然接受這一點(diǎn):更糟的可能實(shí)際上是更好。