Meet the PHP
最後編輯於 2020-03-27
前言
2020-02-16 寫的內容因為受到當時公司的影響充滿了偏頗(完全是抱怨文),所以這次會盡可能以 PHP 這個語言給我的感覺來談談。
第一次接觸到 PHP 是在資策會,當時的結業專題因為種種原因我需要可以回應資料的 API ,但那時根本還沒教如何弄後端 API ,所以為了有資料可以在 Android 上呈現就找上了 PHP 。
直接說結論吧, PHP 跟當時所學的 Java 語法結構很不一樣而我那時也是超級菜雞,做出來的東西幾乎都是照抄網路上的範例,所以 PHP 之於我就像是被老師要求抄文言文的學生一樣,只想著『文言文他媽到底是三小,最好人討論是這樣子說話啦。』,但為了成績( API )還是不得不做。
我那時只覺得 PHP 真的是醜死了。
時間回到一月,我應徵上了自稱合法的博弈技術外包公司,職稱為 Java 開發工程師。
然後就開始學 PHP 了。
對,你沒看錯,不要懷疑。
當時面試有提到公司內有用 PHP 與 Java ,而且可能會寫一些 PHP 啦⋯⋯ 但我直到最近離職前寫的全都是 PHP 耶?
抱歉離題了。
我是一個不太喜歡動態語言的人,或者說我不太喜歡規範不明確。
我認為動態語言於單人快速開發很合適,多數情況下自己的思考邏輯不會有衝突,若是已知函式輸入與輸出型態,卻還得補上型別提示、強制轉型確實令人煩躁;於多人開發則有可能導致災難,畢竟每個人的思考邏輯很難不產生衝突,所以會變成需要在程式內進行某些檢查,但這樣反而囉唆。
1 | # Python 的情況,為了確保其他人不誤用所以進行型態檢查 |
還不如靜態語言直接註明輸入的型態:
1 | // Go 的情況,直接標示輸入值須為 string |
不過我也想過這可能是因為我沒參與過動態語言開發的大型專案,或許在那些專案中其實根本沒有這種問題存在。
總之,下面提到的點都是我不太喜歡或難以接受的部分。
語法
PHP 與我所熟悉的大多數語言寫法不同,當初看程式碼的時候感覺觀念被當面撕碎,加水攪拌後被潑了滿臉。
操作符號
PHP 字串串接使用 .
而屬性、函式存取用 ->
。
1 | # Python 的情況 |
1 | // PHP 的情況 |
一直以來碰過的語言 Java 、 Go 、 JavaScript 、 Dart 等都是用 +
來進行串接,而 .
則是用來存取屬性與函式,這讓我看程式碼的時候很痛苦,再加上公司的前輩們字串串接時都不喜歡加上空白,簡直跟天書一樣:
1 | $url = $some_url.'?xxx_id='.$this->xxx_id; |
雖然這種情況 +
也很不好看,但因為我不會誤解 +
的語義所以情況或許會好點。
我想對於原本學習 PHP 或有相近語法語言的人大概不會有這個問題。
變數
動態語言的特性之一就是變數不受型態約束:
1 | # Python 的情況 |
PHP 只多了個規定,要求所有的變數需以 $
開頭:
1 | // PHP 的情況 |
這對我來說感覺與操作符號一樣,都跟我原本的觀念衝突而導致觀看時的消耗。
$
其實有專門的稱呼,稱為 Variable variables (可變的變數),這是什麼意思呢?
直接看個例子:
1 | $a = "hello"; |
稍微解釋一下演變過程:
$a
取出指向的值 “hello”- $ + “hello” 轉變為
$hello
$hello
指向值 “world”
是個類似於 Pointer ,但又比它莫名其妙的玩意
我懷疑可能太容易激起同事的殺人慾望,尚未在專案中看人用過。
但你我都知道,總是有人喜歡自作聰明的使用這些功能。
萬能 Array
印象中的 Array (陣列)是種連續記憶體空間結構,也就是那種可以隨機存取的資料結構。
而在 PHP 內的 Array 實際上是 Sorted Hash Table (可排序雜湊表),也就是我印象中的 Map 或 Dictionary 結構。
1 | // 寫法有多種,大概是因為歷史包袱的關係 |
PHP 中 Array 很常使用,因為無論是多回傳值,多個參數都可以輕易地使用 Array 包裹起來傳遞。
要傳一堆變數?用 Array
回傳多個值?用 Array
臨時想弄個結構?用 Array
看起來很萬用對吧?因為你也沒其它選擇了。
PHP Array 還有個詭異的語法糖:
1 | $arr = ['a' => "Apple", 'b' => "Banana"]; |
也就是當 Array 的 Key 不寫的時候會進行 Append (添加元素),這種語法糖我是第一次見到,老實說 PHP 有很多的工具函式,透過函式不是更具表達力嗎?
1 | // 回傳新的陣列 |
PHP 的 Array 要是命名為 Map 或 Dict 之類的我都覺得還好,但是 Array 很容易誤會實際的結構
常數
常數早期只能透過 define(Name, Value, CaseSenstive?)
這個內建函式定義,直到 5.3 之後才可以使用 const 關鍵字。
PHP 內的常數是沒有 $
開頭的,使用起來就跟其它語言的變數一樣,而這對我當然又是一種額外消耗,因為看到常數時會突然忘記 PHP 的變數要 $
;看到變數時會突然看不懂常數是啥鬼。
1 | define("SUCCESS", 1); |
專案內與某些套件的程式碼都是看到 define()
比較多,可能原因大概是 define()
可以透過判斷式來追加定義:
1 | // 無法這樣使用 |
另一個原因或許是 const
只能接受靜態數值,換句話說你沒辦法用函式之類的回傳值來設定常數。
老實說這點我覺得很正常啦,常數就應該是一種固定數值才對,如果要透過函式回傳值,那不就表示可能會變動嗎,這樣還能算是常數嗎?
需要修改 php.ini 來開啟函式庫
這個我不確定有沒有其它解決方法,是處理專案時碰到的一個問題。
當時碰到要串接的三方 API 是使用 SOAP 協議,而 PHP 可以透過 SoapClient 類別來處理。
1 | // WSDL 是 XML 格式的服務描述檔案 |
但測試時出現錯誤說找不到 SoapClient 類別。
拜請了 Google 大神後發現要使用這個類別竟然得修改 php.ini 來啟用,我實在難以相信一個內建的函式庫竟然必須修改 PHP 設定檔才能開啟。
大小寫敏感不敏感?
PHP 中只有變數、常數是大小寫敏感的,而像是:
- 類別
- 函式
- 關鍵字
換句話說,你可以像這樣寫:
1 | FuncTion justdoit($flag = tRuE) { |
當然真的這麼做的人很少,可這真的是個匪夷所思的設計,完全不能理解。
有篇文章比較詳細的說了此設計的原因:PHP黑系列之一:PHP 为什么大小写规则是如此不规则?
多樣化的關鍵字
各種各樣的關鍵字出現在 PHP 中,讓我感到困惑。
好比說 include
與 require
都可以將指定的文件給引入,類似一種複製貼上。
那麼為什麼需要兩個?因為如果是 include
即使對象不存在也不會出現錯誤, require
則會報錯,所以多數情況下都建議使用 require
,這就很讓人懷疑 include
的存在價值。
還有特殊的實體關鍵字,比如可以透過:
1 | // 不論如何都會實例化當前這個類別 |
其它像是為了不寫花括號而增加的 end 系列:
1 | // 原本的寫法 |
不太清楚這是為了照顧多種風格的設計師還是怎樣,老樣子太多選擇容易導致困惑。
官方無規範
官方理應具有一致性規範,這才不容易造成開發者的負擔。
但是 PHP 的內建函式命名你可以發現多種風格:
- strptime : C 語言風格
- nl2br :縮寫風格
- htmlspecialchars :全小寫風格
- json_encode :底線區隔
這來自一篇詳細解釋的文章:PHP黑系列之二:PHP 为什么函数命名是如此不一致?
PHP 內建的函式多的誇張,所以 PHPer 最常做的事情就是去查看官方手冊,聽起來雖然很正常,但如前述 PHP 根本沒有命名脈絡可循,所以你根本不知道字串操作、加密解密都提供了哪些函式給你,還曾發生過版更替時將舊有的函式給移除的情況,再加上參數順序延續此傳統各種混亂,導致難以想像在沒手冊的狀況下寫 PHP 。
就連型態都有好幾種寫法,以下幾種都是一樣型態:
- int/integer
- bool/boolean
- float/double/real
結論
最早我是因為不喜歡動態語言的關係不太喜歡 PHP ,但是這次學習部分知識與實際撰寫後我想我可以認真的說:「我不喜歡 PHP 。」
這個語言散發著濃厚 “ 走一步算一步 “ 的氣息,也很明顯有許多致敬其它語言的部分,但重點是太多設計理念被包含在 PHP 中卻沒有被整理,變成東拼西湊弄出一個看上去還可以的成品,不過當你仔細觀察後就會發現其糟糕的內在。
當然我也不否認 PHP 在快速開發上真的很強,前公司之所以使用也是看上了 PHP 的某些特性,而我不喜歡的只是它的設計風格,如果能自己選的話 PHP 目前不會在選項內,但假使未來 PHP 狠下心決定處理掉某些糟糕的設計,那我可能就會考慮使用了。
畢竟你看 PHP 可是唯一一個寫完會變得更開心的、不容你質疑的、世界上最好的程式語言啊!