如何全面分析PHP的糟糕設(shè)計(jì)

如何全面分析PHP的糟糕設(shè)計(jì),很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

站在用戶的角度思考問題,與客戶深入溝通,找到豐林網(wǎng)站設(shè)計(jì)與豐林網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都做網(wǎng)站、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、國際域名空間、網(wǎng)絡(luò)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋豐林地區(qū)。

PHP不僅使用起來尷尬, 還有要嘛我想要的不適合, 要嘛不是最令人滿意, 要嘛違背我的信仰. 我可以告訴你關(guān)于一門語言, 所有我想避免的好方式, 所有我喜歡的壞方式. 來吧, 問吧! 談話會(huì)很有趣!

php是唯一的例外. 幾乎php抽象的所有東西都是支離破碎的. 包括語言, 框架, 整個(gè)生態(tài)系統(tǒng)都一塌糊涂. 我?guī)缀醪荒軉为?dú)列出咒罵的事情, 因?yàn)樗矶級(jí)牧? 每次我打算編輯一堆雜亂如麻的php抱怨清單的時(shí)候, 我都被一些瑣事打亂, 越深入就越會(huì)發(fā)現(xiàn)其它令人震驚的事情.

php讓人難堪. 它是如此的破碎, 但那些被培訓(xùn)的業(yè)余愛好者, 卻對(duì)它稱贊不已. php在做一些徽不足道的挽回措施, 但我選擇忘記它.

不過我得讓我的系統(tǒng)擺脫這些東西, 也就這樣了, 這是最后一次嘗試.

打個(gè)比喻

我只是隨口和 Mel 抱怨下, 而她卻堅(jiān)決讓我發(fā)表出來.

我甚至說不出來PHP到底怎么了, 因?yàn)?-- 還好. 想想你有一個(gè), 嗯, 工具箱吧. 一堆工具. 看起來還好, 有標(biāo)準(zhǔn)的東西.

你拔除螺絲釘, 它怪異的有三個(gè)頭. OK, 好吧, 這對(duì)你不太有用, 但你猜遲早有天會(huì)有用.

你拿出榔頭, 被震住了, 兩邊都有是尖爪. 但它仍然能用, 我的意思是, 你可以用兩頭的中部斜著敲.

你拿出老虎鉗, 但它們沒有鋸齒面. 表面平而光滑. 這沒多大用, 但依然能用, 沒什么.

你可以繼續(xù). 工具箱的東西都是怪異和琢磨不定的, 但又不能說毫無價(jià)值. 整體看沒什么大問題; 它的工具都齊全.

現(xiàn)在, 想象有很多使用這些工具的木匠, 它們和你說:"這些工具有什么問題呢? 我們都用過, 它們工作都很好啊!". 工匠們給你展示他們建的房子,每個(gè)門都是五邊形的而屋頂是癲倒的. 你敲前門, 它向內(nèi)倒榻了, 而他們卻抱怨你打破了他們的門.

這就是PHP的問題.

立場(chǎng)

我認(rèn)為下面的特質(zhì)對(duì)于一門語言的生產(chǎn)力和可用性是重要的, 而PHP在大范圍破壞它們. 如果你不同意這些, 好吧, 我無法想像, 我們永遠(yuǎn)不會(huì)達(dá)成一致.

>> 一門語言必須是可預(yù)見的. 它是將人類的思想反映給計(jì)算機(jī)執(zhí)行的媒介, 因此它的關(guān)鍵是, 人類對(duì)程序的理解實(shí)際要正確.

>> 語言必須一致. 相似的東西就要看起來相似, 不同的就是不同. 學(xué)習(xí)了語言的部分知識(shí), 就應(yīng)能很容易理解剩下的部分.

>> 語言必須簡(jiǎn)潔. 新語言應(yīng)該減少繼承舊語言的不好的形式. (我們也可以寫機(jī)器碼.) 新語言當(dāng)然應(yīng)努力避免織入新的特有的形式.

>> 語言必須是可靠的. 語言是解決問題的工具; 應(yīng)盡量避免引入新問題. 任何"陷阱"都會(huì)大量的分散注意力.

>> 語言必須是可調(diào)試的. 當(dāng)出錯(cuò)的時(shí)候, 程序員必須修正它, 我們需要獲得我們想要的幫助.

我的立場(chǎng)是:

>> PHP到處處充滿驚奇: MySQL_real_escape_string, E_ACTUALLY_ALL

>> PHP不一致: strpos, str_rot13

>> PHP需要特別形式: error-checking around C API calls, ===

>> PHP古怪: ==. for($foo as &$bar)

>> PHP晦澀: 默認(rèn)無棧跟蹤或fatals, 復(fù)雜的錯(cuò)誤報(bào)告

我不能就單個(gè)問題解釋為什么它歸為這些類, 否則將會(huì)沒完沒了. 我相信讀者自己會(huì)思考.

不要再和我扯這些東西了

我知道很多有利的論點(diǎn). 我也聽到很多反駁的論點(diǎn). 這些都只能讓談話立即停止. 不要再跟我扯這些東西了, 求你了. :(

>> 不要和我說"好的開發(fā)者能用任何語言寫出好的代碼", 或者壞開發(fā)者.. 吧啦吧啦. 這毫無意義. 好的工匠可以用石頭或錘子駕馭釘子, 但你見過有多少工匠用石頭的? 成為一個(gè)好開發(fā)者的標(biāo)準(zhǔn)之一就是善于選擇工具.

>> 不要和我說熟記上千個(gè)例外和古怪行為是開發(fā)者的職責(zé). 是的, 這在任何系統(tǒng)中都是必要的, 因?yàn)殡娔X是傻的. 這不意味著, 系統(tǒng)能瘋狂的接受而沒有上限. PHP有的只是異常, 這是不行的, 一旦和語言摔角決斗, 你實(shí)際編寫程序就要花費(fèi)更多的努力. 我的工具不能為我創(chuàng)建應(yīng)用產(chǎn)生積極作用.

>> 不要和我說 "那就是C API 的工作方式". 這星球上高級(jí)語言存在的目的是什么, 它們能提供的一切僅僅是一些字符串助手函數(shù)和一堆C的包裝器? 如果是這樣, 那就用C! 這里, 甚至還有為它準(zhǔn)備的CGI庫.

>> 不要和我扯 "搞出奇怪的事, 是你活該". 如果存在兩個(gè)特性, 總有一天, 某些人會(huì)找到一起使用它們的理由. 再次強(qiáng)調(diào), 這不是C; 這里沒有規(guī)范, 這里不需要 "未定義行為".

>> 不要再和我扯 Facebook 和 Wikipedia 就用的PHP. 我早知道了! 它們也能用 Brainfuck 寫, 但只要他們足夠陪明, 不斷折騰這些事情, 他們總能克服平臺(tái)的問題. 眾所周知, 如果使用其它語言編寫, 開發(fā)時(shí)間可能會(huì)減少一半或加倍; 單獨(dú)拿出這些數(shù)據(jù)毫無意義.

上帝保佑, 不要再和我扯任何東西了! 如果列出的沒有傷害你的PHP的觀點(diǎn), 無所謂, 因此請(qǐng)停止在網(wǎng)上做無意義的爭(zhēng)論, 繼續(xù)開發(fā)高帥富酷的站點(diǎn)來證明我是錯(cuò)的 :).

偷偷告訴你: 我非常喜歡Python. 我也很樂意對(duì)它說些你不愛聽的話, 如果你真想的話. 我并不要求它完美; 我只是想揚(yáng)長(zhǎng)避短, 總結(jié)我想要的最佳東西.

PHP

語言核心

CPAN被稱為 "Perl的標(biāo)準(zhǔn)庫". 這并沒有對(duì)Perl的標(biāo)準(zhǔn)庫做過多說明, 但它蘊(yùn)含了健壯的核心可以構(gòu)建強(qiáng)大的東西的思想.

基本原則

PHP最初很明確的是為非程序員設(shè)計(jì)的(言外之意, 非專業(yè)程序); 根源已經(jīng)很難脫離. 從PHP 2.0 文檔中挑選出來的對(duì)話:

一旦你開始為每個(gè)類型區(qū)分不同的操作符, 你就開始使用語言變得復(fù)雜了. 例如, 你不能為strings使用 '==', 你現(xiàn)在必須用 'eq'. 我沒看出這點(diǎn)來, 特別是那些類似PHP的腳本語言, 它們大多數(shù)相當(dāng)簡(jiǎn)單而多數(shù)情況下, 作為非程序員, 只想要一門包含少量基本邏輯語法的語言, 而不想付出過多學(xué)習(xí)曲線.

>> PHP 為保持前進(jìn)不惜代價(jià). 什么都有比沒有好.

>> 這不是個(gè)正確的設(shè)計(jì)原則. 早期的PHP受Perl影響; 大量的標(biāo)準(zhǔn)庫參考C使用 "out" 參數(shù); OO部分的設(shè)計(jì)像C++和Java.

>> PHP從其它語言中引入大量的靈感, 但對(duì)那些熟知其它語言的人, 仍然難以理解. (int)看起來像 C, 但是 int 并不存在. 命名空間使用 \. 新的數(shù)組語法使用 [key => value], 不同于任何其它語言定義hash字面量的形式.

>> 弱類型(例如, 默默的自動(dòng)在 strings/mumbers/等間轉(zhuǎn)換)是如此的復(fù)雜.

>> 少量的新特性以新語法實(shí)現(xiàn); 大多數(shù)工作通過函數(shù)或者看起來像函數(shù)的東西完成. 除了類的支持, 這理所當(dāng)然的需要新的操作符和關(guān)鍵字.

>> 本頁列出的問題都有官方解決方案 -- 如果你想資助 Zend 修復(fù)它們的開源編程語言的話.

>> 路漫漫, 其修遠(yuǎn). 思考下面的代碼, 從PHP文檔的某地方挑出來的.

@fopen('http://example.com/not-existing-file', 'r');

它將做什么?

>> 如果PHP使用 --disable-url-fopen-wrapper編譯, 它將不工作. (文檔沒有說, "不工作"是什么意思; 返回 null, 拋出異常?)

>> 注意這點(diǎn)已在 PHP 5.2.5 中移除.

>> 如果 allow_url_fopen 在 php.ini 中禁用, 也將不工作. (為什么? 無從得知.)

>> 由于 @ , non-existent file 的警告將不打印.

>> 但如果在php.ini中設(shè)置了scream.enabled, 它又將打印.

>> 或者如果用 ini_set 手動(dòng)設(shè)置 scream.enabled.

>> 但, 如果 error_reporting 級(jí)別沒設(shè)置, 又不同.

>> 如果打印出來了, 精確去向依賴于 display_errors , 再一次還是在 php.ini. 或者 ini_set中.

我無法告訴你這個(gè)函數(shù)調(diào)用的行為, 如果沒有查看編譯時(shí)標(biāo)志 , 服務(wù)器端配置, 和我的程序中的配置的話. 這些都是內(nèi)建行為.

>> 該語言充滿了全局和隱似狀態(tài). mbstring 使用全局字符編碼. func_get_arg 之類的看起來像正常的函數(shù), 但是只對(duì)當(dāng)前正在執(zhí)行的函數(shù)操作. Error/exception 處理默認(rèn)是全局的. register_tick_function 設(shè)置了一個(gè)全局函數(shù)去運(yùn)行每個(gè) tick(鉤子?) ---- 什么?!

>> 沒有任何線程支持. (不奇怪, 因?yàn)樯厦嬉呀o出.) 加之缺乏內(nèi)建的 fork (下面提到), 使得并行編程極其困難.

>> PHP的某些部分在實(shí)踐中會(huì)產(chǎn)生錯(cuò)誤代碼.

>> json_decode 對(duì)不正確的輸入返回 null, 盡管 null 也是一個(gè) JSON 解碼的合法對(duì)象 -- 該函數(shù)極不可靠, 除非你每次使用后都調(diào)用 json_last_error.

>> 如果在位置0處找到, array_search , strpos, 和其它類似的函數(shù)返回0, 但如果都沒有找到的話. 會(huì)返回 false

讓我們稍稍展開最后一部分.

在C中, 函數(shù)如 strpos 返回 -1, 如果未找到. 如果你沒檢查這種情況, 卻試著以下標(biāo)使用它, 那將可能命中垃圾內(nèi)存, 程序會(huì)崩潰. (也許吧, 這是C. 誰泥馬知道. 我確定至少有工具處理它)

話說, Python中, 等效的 .index 方法將拋出一個(gè)異常, 如果元素沒找到的話. 如果你不檢查該情形, 程序?qū)⒈罎?

在PHP中, 該函數(shù)返回 false. 如果你把 FALSE 作為下標(biāo)使用, 或者用它做其他事情, PHP會(huì)默默的將它轉(zhuǎn)成0, 但除了用于 === 比較. 程序是不會(huì)崩潰的; 它將執(zhí)行錯(cuò)誤的邏輯, 且無任何警告, 除非你記得在每個(gè)使用 strpos 和其它類似函數(shù)的地方包含正確的樣版處理代碼.

這真是糟透了! 編程語言只是工具; 它們是為我服務(wù)的. 這里, PHP給我布下了陷阱, 等著我跳進(jìn)去, 而我不得不時(shí)刻警惕這些無聊的字符串操作和相等比較. PHP是個(gè)雷區(qū).

我已經(jīng)聽過很多關(guān)于PHP解析器的故事, 它的開發(fā)者來自世界各地. 有從事PHP核心開發(fā)工作的人, 有調(diào)試PHP核心的人, 也有和核心開發(fā)者交流過的人. 沒有一個(gè)故事是贊賞的.

因此不得不在這里插入一句, 因?yàn)樗档弥貜?fù): PHP是個(gè)業(yè)余愛好者的社區(qū). 極少數(shù)人設(shè)計(jì), 為它工作, 或極少有人知道他們?cè)谧鍪裁? (哦, 親愛的讀者, 你當(dāng)然是個(gè)極品例外!) 那些成長(zhǎng)了, 想轉(zhuǎn)投其它平臺(tái)的人, 使整個(gè)社區(qū)的平均水平下降. 這個(gè), 就是這里, 是PHP的最大問題: 絕對(duì)的盲目領(lǐng)導(dǎo)盲目.

好了, 回來面對(duì)現(xiàn)實(shí)吧.

操作符

== 不中用.

>> "foo" == TRUE , 和 "foo" == 0... 但, 當(dāng)然 TRUE != 0.

>> == 會(huì)將兩邊轉(zhuǎn)成數(shù)字, 如果可能的話, 這意味著它將轉(zhuǎn)成 floats 如果可能. 所以大的16進(jìn)制字符串(如, password hashes) 可能偶然會(huì)比較成 true , 盡管它們不一樣. 就連 JavaScript 都不會(huì)這樣做.

>> 由于某些原因, "6" == "6", "4.2" == "4.20", 和 "133" == "0133". 但注意 133 != 0133, 因?yàn)?0133 是八進(jìn)制的.

>> === 比較值和類型... 除了對(duì)象, 只有兩邊實(shí)際上是同一對(duì)象才為 true ! 對(duì)于對(duì)象, == 比較值(或每個(gè)屬性)和類型, 這又是 === 比較任何非對(duì)象類型的行為. 好玩嗎?

比較大小也好不到哪去.

>> 甚至行為都不一致: NULL < -1, 而 NULL == 0. 排序也因此不確定; 它依賴于在排序中比較元素的算法的順序.

>> 比較操作符嘗試排序數(shù)組, 以兩種不同的方式: 首先按長(zhǎng)度, 然后按元素. 如果它們有相同數(shù)量的元素但不同的keys, 它們是不可比的.

>> 對(duì)象比較比其它比較做得更多... 除了那些即不小于也不大于的對(duì)象.

>> 為了類型更安全的 == 比較, 我們有 ===. 為了類型更安全的 < 比較, 我們有... 什么也沒有. "123" < "0124", 通常, 不管你怎么做. 類型轉(zhuǎn)換也無濟(jì)于事.

>> 盡管上面的舉動(dòng)很瘋狂, 但卻明確拒絕Perl's的字符串 paris 和算術(shù)運(yùn)行符, PHP沒有重載 +. + 就是通常的 +, 而 . 是通常的連接符.

>> [] 下標(biāo)操作符也可以拼寫成 {}.

>> [] 可以用于任何變量, 不光是字符串和數(shù)組. 它返回 null , 無錯(cuò)誤警告.

>> [] 僅能獲取單個(gè)元素.

>> foo()[0] 是個(gè)語法錯(cuò)誤. (已在 PHP 5.4 中修復(fù))

>> 不像(從字面上看)任何其它語言都有的類似的操作符, ?: 是左結(jié)合的. 因此:

$arg = 'T';   $vehicle = ( ( $arg == 'B' ) ? 'bus' :               ( $arg == 'A' ) ? 'airplane' :               ( $arg == 'T' ) ? 'train' :               ( $arg == 'C' ) ? 'car' :               ( $arg == 'H' ) ? 'horse' :               'feet' );  echo $vehicle;

打印 horse.

變量

>> 無法聲明變量. 當(dāng)?shù)谝淮问褂脮r(shí), 不存在的變量會(huì)被創(chuàng)建為 null 值.

>> 全局變量在使用前, 需要 global 聲明. 這是根據(jù)上面得出的自然結(jié)果, 因此這是個(gè)完美的理由, 但, 如果沒有顯示的聲明, 全局變量甚至無法讀取 -- PHP 將悄悄的創(chuàng)建一個(gè)局部同名變量取代它. 我還沒見過其它語言使用類似的方法處理范圍問題.

>> 沒有引用. PHP所謂的引用是個(gè)真正的別名; 這無疑是一種倒退, 不像 Perl 的引用, 也沒有像 Python 那樣的對(duì)象標(biāo)識(shí)傳遞.

>> 沒有明顯的方式檢測(cè)和取消引用.

>> "引用" 使變量在語言中與眾不同. PHP 是動(dòng)態(tài)類型的, 因此變量通常無類型... 除了引用, 它修飾函數(shù)定義, 變量語法, 和賦值. 一旦變量被引用(可在任何地方發(fā)生), 它就一直是個(gè)引用. 沒有明顯的方法探測(cè)和解引用需要的變量值.

>> 好吧, 我說謊了. 有些"SPL types" 也作用于變量: $x = new SplBool(true); $x = "foo"; 將失敗. 這有點(diǎn)像靜態(tài)類型, 自己看看.

>> A reference can be taken to a key that doesn&rsquo;t exist within an undefined variable (which becomes an array). Using a non-existent array normally issues a notice, but this does not.

>> 通過函數(shù)定義的常量稱為 taking a string; 這之前, 它們不存在. (這可能實(shí)際上是復(fù)制 Perl 使用常量的行為.)

>> 變量名是大小寫敏感的. 函數(shù)和類名不是. 使得方法使用駝峰式命名會(huì)很奇怪.

結(jié)構(gòu)

>> array() 和幾個(gè)類似的結(jié)構(gòu)不是函數(shù). $func = "array"; $func(); 不工作.

>> 數(shù)組拆包可以使用 list($a,$b) = .... 操作完成. list() 是類函數(shù)語法, 就像數(shù)組那樣. 我不知道為什么不給一個(gè)真正的專用語法, 也不知道為什么名字如些的讓人迷惑.

>> (int) 很顯然的被設(shè)計(jì)成類似C, 但它不是單獨(dú)的標(biāo)記; 在語言中, 沒有東西被稱為 int. 試試看: var_dump(int)不工作, 它會(huì)拋出一個(gè)解析錯(cuò)誤, 因?yàn)閰?shù)看起來像是強(qiáng)制轉(zhuǎn)操作符.

>> (integer) 是 (int) 的別名. 也有 (bool)/(boolean)和(float)/(double)/(real).

>> 有個(gè)(array)操作符用來轉(zhuǎn)成數(shù)組和 (object) 用來轉(zhuǎn)成對(duì)象. 這聽起來很貼心, 但常常有個(gè)用例: 你可以用 (array) 使得某個(gè)函數(shù)參數(shù), 既可以是單個(gè)元素,也可以是列表, 相同對(duì)待. 但這樣做不可靠, 因?yàn)槿绻橙藗鬟f了單個(gè)對(duì)象,把它轉(zhuǎn)換成數(shù)組將實(shí)際上生成了一個(gè)包含對(duì)象屬性的數(shù)組. (轉(zhuǎn)換成對(duì)象執(zhí)行了反轉(zhuǎn)操作.)

>> include()這類的函數(shù)基本上就是C的#include: 他們將其它的文件源碼轉(zhuǎn)存到你的文件中. 沒有模塊系統(tǒng), 甚至對(duì) PHP 代碼也一樣.

>> 沒有類似嵌套或者局部范圍的函數(shù)或類. 它們都是全局的. include 某文件, 它的變量導(dǎo)入到當(dāng)前函數(shù)范圍中(給了文件訪問你的變量的能力), 但是函數(shù)和類存入全局范圍中.

>> 追加數(shù)組使用 $foo[] = $bar.

>> echo 不是函數(shù).

>> empty($var) 是如此極端, 對(duì)于任何其它東西不表現(xiàn)為函數(shù), 除了變量, e.g. empty($var || $var2), 是個(gè)解析錯(cuò)誤. 為什么地球上有這種東西, 解析器為什么需要了解 empty ?

>> 還有些冗余的語法塊: if (...): ... endif;, 等等.

錯(cuò)誤處理

>> PHP 的一個(gè)獨(dú)特操作符是 @ (實(shí)際上從DOS借用過來的), 它隱藏錯(cuò)誤.

>> PHP 錯(cuò)誤不提供棧軌跡. 你不得不安裝一個(gè)處理器生成它們. (但 fatal errors不行 -- 見下文.)

>> PHP 的解析錯(cuò)誤通常只拋出解析的狀態(tài), 沒其它東西了, 使得調(diào)試很糟糕.

>> PHP 的解析器所指的例如. :: 內(nèi)部作為 T_PAAMAYIM_NEKUDOTAYIM, 而 << 操作符作為 T_SL. 我說 "內(nèi)部的", 但像上面說的, 給程序員顯示的 :: 或 << 出現(xiàn)在了錯(cuò)誤的位置.

>> 大多數(shù)錯(cuò)誤處理打印給服務(wù)器日志打印一行錯(cuò)誤日志, 沒人看到而一直進(jìn)行.

>> E_STRICT看起來像那么回事, 但它實(shí)際上沒多少保護(hù), 沒有文檔顯示它實(shí)際上是做什么的.

>> E_ALL包含了所有的錯(cuò)誤類別 -- 除了 E_STRICT.

>> 關(guān)于什么允許而什么不允許是古怪而不一致的. 我不知道 E_STRICT 是怎樣適用于這里的, 但這些卻是正確的:

>> 試圖訪問不存在的對(duì)象屬性, 如, $foo->x. (warning)

>> 使用變量做為函數(shù)名, 或者變量名, 或者類名. (silent)

>> 試圖使用未定義常量. (notice)

>> 試圖訪問非對(duì)象類型的屬性.(notice)

>> 試圖使用不存在的變量名.(notice)

>> 2 < "foo" (隱藏)

>> foreach (2 as $foo); (warning)

而下面這些不行:

>> 試圖訪問不存在的類常量, 如 $foo::x. (fatal error)

>> 使用字符串常量作為函數(shù)名, 或變量名, 或類名. (parse error)

>> 試圖調(diào)用一個(gè)示定義函數(shù). (fatal error)

>> Leaving off a semicolon on the last statement in a block or file. (parse error)

>> 使用 list 和其它準(zhǔn)內(nèi)建宏作為方法名. (parse error)

>> 用下標(biāo)訪問函數(shù)的返回值, 如: foo()[0]. (parse error; 已在 5.4 中修復(fù))

在列表的其他地方也有幾個(gè)關(guān)于其它怪異解析錯(cuò)誤的好例子

>> __toString 方法不能拋出異常. 如果你嘗試, PHP 將 ... 呃, 拋出一個(gè)異常. (實(shí)際上是個(gè) fatal error, 可以被通過的, 除了...)

>> PHP 錯(cuò)誤和 PHP 異常是完全不同的物種. 它們不能相互作用.

>> PHP 錯(cuò)誤 (內(nèi)部, 稱為 trigger_error)不能被 try/catch 捕獲.

>> 同樣, 異常不能通過 set_error_handler 安裝的錯(cuò)誤處理器觸發(fā)錯(cuò)誤.

>> 作為替代, 有一個(gè)單獨(dú)的 set_exception_handler 可以處理未捕獲的異常, 因?yàn)橛?try 塊包裝你程序入口在 mod_pho 模塊中是不可能的.

>> Fatal 錯(cuò)誤 (例如, new ClassDoesntExist()) 不能被任何東西捕獲. 大量的完全無害的操作會(huì)拋出 fatal 錯(cuò)誤, 由 于一些有爭(zhēng)議的原因被迫終結(jié)你的程序. 關(guān)閉函數(shù)仍然運(yùn)行, 但它們無法獲取棧軌跡(它們運(yùn)行在上層), 它們很難告知該程序是由一個(gè)錯(cuò)誤還是程序的正常運(yùn)行結(jié)束.

>> 沒有 finally 結(jié)構(gòu), 使得包裝代碼 (注冊(cè)處理器, 運(yùn)行代碼, 注銷處理器; monkeypatch, 運(yùn)行測(cè)試, unmonkeypatch) 很難看, 很難寫. 盡管 OO 和異常大量的復(fù)制了Java的模式, 這是故意的, 因?yàn)?finally "在PHP上下文中, 只得其形不得其神".Huh ?

函數(shù)

>> 函數(shù)調(diào)用似乎相當(dāng)昂貴.

>> 一些內(nèi)建函數(shù)與 reference-returning 函數(shù)交互, 呃, 一種奇怪的方式.

>> 正如在別處提到的, 很多看起來像函數(shù)或者看起來它們應(yīng)該是函數(shù)的東西實(shí)際上是語言的構(gòu)成部分, 因此無法像正常函數(shù)一樣的工作.

>> 函數(shù)參數(shù)可以具有 "類型提示", 基本上只是靜態(tài)類型. 你不能要求某個(gè)參數(shù)是 int 或是 string 或是 對(duì)象 或其它 "核心" 類型, 即使每個(gè)內(nèi)建函數(shù)使用這種類型, 可能因?yàn)?int 在PHP中不是個(gè)東西吧. (查看上面關(guān)于 (int) 的討論). 你也不能使用特殊的被大量?jī)?nèi)建函數(shù)使用的偽類型裝飾: mixed, number, or callback.

>> 因此, 下面:

function foo(string $s) {}  foo("hello world");

產(chǎn)生錯(cuò)誤 the error:

PHP Catchable fatal error: Argument 1 passed to foo() must be an instance of string, string given, called in...

>> 你可能會(huì)注意到 "類型提示" 實(shí)際上并不存在; 在程序中沒有 string 類. 如果你試圖使用 ReflectionParameter::getClass() 動(dòng)態(tài)測(cè)試類型提示, 將會(huì)得到類型不存在, 使得實(shí)際上不可能取得該類型名.

>> 函數(shù)的返回值不能被推斷

>> 將當(dāng)前函數(shù)的參數(shù)傳給另一個(gè)函數(shù) (分派, 不罕見) 通過 call_user_func_array('other_function', func_get_args())完成. 但 func_get_args 在運(yùn)行時(shí)拋出一個(gè) fatal 錯(cuò)誤, 抱怨它不能作為函數(shù)參數(shù). 為什么為什么這是個(gè)類型錯(cuò)誤? ( 已在 PHP 5.3 中修復(fù))

>> 閉包需要顯示的命名每個(gè)變量為 closed-over. 為什么解析器不想辦法解決? (Okay, it&rsquo;s because using a variable ever, at all, creates it unless explicitly told otherwise.)

>> Closed-over 變量, 通過和其它函數(shù)參數(shù)相同的語義"傳遞". 這樣的話, 數(shù)組和字符串等等, 將以傳值方式傳給閉包. 除非使用 &.

>> 因?yàn)殚]包變量會(huì)自動(dòng)傳遞參數(shù), 沒有嵌套范圍, 閉包不能指向私有方法, 不管是否定義在類中. ( 可能在 5.4 中修復(fù)? 不清楚.)

>> 函數(shù)沒有命名參數(shù). 實(shí)際上被 devs 顯示拒絕, 因?yàn)樗?"會(huì)導(dǎo)致代碼臭味".

>> Function arguments with defaults can appear before function arguments without, even though the documentation points out that this is both weird and useless. (So why allow it?)

>> 向函數(shù)傳遞額外的參數(shù)會(huì)被忽略 (除了內(nèi)建函數(shù), 會(huì)拋出異常). 丟失的參數(shù)被假定為 null.

>> "可變" 函數(shù)需要 func_num_args, func_get_arg, 和 func_get_args. 這類事情沒有語法.

OO

>> PHP的函數(shù)部分被設(shè)計(jì)成類似C, 但面向?qū)ο?(ho ho) 被設(shè)計(jì)成類似 Java. 我不想過分強(qiáng)調(diào)這有多不合諧. 我還沒有發(fā)現(xiàn)一個(gè)有大寫字母的全局函數(shù), 重要的內(nèi)建類使用駝峰式方法命名, 并有g(shù)etFoo的Java風(fēng)格的屬性訪問器. 這是門動(dòng)態(tài)語言, 對(duì)嗎? Perl, Python, 和 Ruby 都有一些 通過代碼訪問"屬性"的概念; PHP 僅僅有笨重的 __get 之類的東西. 類型系統(tǒng)圍繞著低層的 Java語言設(shè)計(jì), Java 和PHP's處一時(shí)代, Java 有意的做了更多限制, 照搬Java, 我百思不得其解.

>> 類不是對(duì)象. 元編程不得不通過字符串名指向它們, 就像函數(shù)一樣.

>> 內(nèi)建的類型不是對(duì)象, (不像Perl) 也無法使得看起來像對(duì)象.

>> instanceof 是個(gè)操作符, 盡管很晚才增加進(jìn)來, 而大多數(shù)語言都建有專門的函數(shù)和語法. 受Java影響嗎? 類不是第一類? (我不知道它們是不是.)

>> 但有一個(gè) is_a 函數(shù). 它有個(gè)可選參數(shù)指定是否允許對(duì)象實(shí)際是一個(gè)字符串命名的類.

>> get_class 是函數(shù); 沒有 typeof 操作符. 同樣有 is_subclass_of.

>> 然而, 這對(duì)于內(nèi)建類型無法工作, (再一次, int 不是個(gè)東西). 這樣, 你需要 is_int 等等.

>> 右值必須是變量或字面量; 不能是表達(dá)式. 不然會(huì)導(dǎo)致... 一個(gè)解析錯(cuò)誤.

>> clone 是一個(gè)操作符?!

>> OO 的設(shè)計(jì)是一只混合 Perl 和 Java 的怪物.

>> 對(duì)象屬性通過 $obj->foo, 但類屬性是 $obj::foo. 我沒見過任何其它語言這樣做, 或者這樣做有什么用.

>> 而, 實(shí)例方法仍然能通過靜態(tài)的(Class::method)調(diào)用. 如果從其它方法中這么調(diào)用, 會(huì)在當(dāng)前 $this 上被看成常規(guī)的方法調(diào)用. 我認(rèn)為吧.

>> new, private, public, protected, static ,等等. 試圖虜獲 Java 開發(fā)者的芳心? 我知道這更多是個(gè)人的品位, 但我不知道為什么這些東西在一門動(dòng)態(tài)語言中是必要的 -- 在 C++ 中, 它們中的大多數(shù)是有關(guān)匯編和編譯時(shí)的命名決議.

>> 子類不能覆蓋 private 方法. 子類覆蓋的公共方法也不可見, 單獨(dú)調(diào)用, 超類的私有方法. 會(huì)有問題, 如在測(cè)試mocks對(duì)象時(shí).

>> 方法無法命名為, 例如 "list" , 因?yàn)?list() 是特殊的語法 (不是個(gè)函數(shù)) , 而解析器會(huì)被搞暈. 如此曖昧的原因無從得知, 而類工作得就很好. ($foo->list() 不是語法錯(cuò)誤.)

>> 如果當(dāng)解析構(gòu)造函數(shù)參數(shù)時(shí)拋出異常(如, new Foo(bar()) 而 bar() 拋出), 構(gòu)造函數(shù)不會(huì)被調(diào)用, 但析構(gòu)函數(shù)會(huì). (已在PHP 5.3 中修復(fù))

>> 在 __autoload 和解析函數(shù)中的異常會(huì)導(dǎo)致 fatal 錯(cuò)誤.

>> 沒有構(gòu)造器或析構(gòu)器. __construct 是個(gè)初始化函數(shù), 像 Python 的 __init__. 無法通過調(diào)用類申請(qǐng)內(nèi)存和創(chuàng)建對(duì)象.

>> 沒有默認(rèn)的初始化函數(shù). 調(diào)用 parent::__construct()的時(shí)候, 如果父類沒定義它自己的 __construct 方法會(huì)導(dǎo)致 fatal 錯(cuò)誤.

>> OO 帶來了個(gè)迭代器接口, 是語言規(guī)范的部分(如 ... as ...), 但該接口實(shí)際上沒有內(nèi)建實(shí)現(xiàn)(如數(shù)組) . 如果你想要個(gè)數(shù)組迭代器,你必須用 ArrayIterator 包裝它. 沒有內(nèi)建方式能夠讓迭代器將其作為第一類對(duì)像工作.

>> 類可以重載它們轉(zhuǎn)化成字符串的方式, 但不能重載怎樣轉(zhuǎn)換成數(shù)字或任何其它內(nèi)建類型的方式.

>> 字符串, 數(shù)字, 和數(shù)組都有字符串轉(zhuǎn)換方式; 語言很依賴于此. 函數(shù)和類都是字符串. 然而,如果沒定義 __toString , 試圖將換內(nèi)建或自定義對(duì)像(甚至于一個(gè)閉包) 轉(zhuǎn)換成字符串會(huì)導(dǎo)致錯(cuò)誤, 甚至連 echo 都可能出錯(cuò).

>> 無法重載相等或比較操作.

>> 實(shí)例方法中的靜態(tài)變量是全局的; 它們的值跨越該類的多個(gè)實(shí)例共享.

標(biāo)準(zhǔn)庫

Perl "某些需要匯編". Python 是 "batteries included". PHP 是 "廚房水槽, 它來自加拿大, 但所有的水龍頭用C貼牌".

概括

>> 沒有類型系統(tǒng). 你可以編譯PHP, 但必須通過 php.ini 指定要加載什么, 選項(xiàng)因擴(kuò)展部分存在(將它們的內(nèi)容注入到全局名稱空間中)或不存在.

>> 因?yàn)槊Q空間是最近才有的特性, 標(biāo)準(zhǔn)庫一點(diǎn)沒被打亂. 在全局名稱空間中有上千個(gè)函數(shù).

>> 庫的某些部分很不一致.

>> 下劃線 對(duì) 無下劃線: strpos/str_rot13, php_uname/phpversion, base64_encode/urlencode, gettype/get_class

>> “to” 對(duì) 2: ascii2ebcdic, bin2hex, deg2rad, strtolower, strtotime

>> Object+verb 對(duì) verb+object: base64_decode, str_shuffle, var_dump versus create_function, recode_string

>> 參數(shù)順序: array_filter($input, $callback) versus array_map($callback, $input), strpos($haystack, $needle) versus array_search($needle, $haystack)

>> 前綴混亂: usleep vs microtime

>> Case insensitive functions vary on where the i goes in the name.

>> 大概一半的數(shù)組函數(shù)以 array_ 開頭. 剩下的不是.

>> 廚房水槽. 庫包括:

>> 綁定 ImageMagick, 綁定 GraphicsMagick (ImageMagick的派生), 少量的幾個(gè)函數(shù)能檢測(cè) EXIF 數(shù)據(jù) (其中ImageMagick已經(jīng)可以做到)

>> 解析 bbcode 的函數(shù), 一些非常特殊的標(biāo)記, 被幾個(gè)少量的論壇包使用.

>> 太多 XML 包. DOM (OO), DOM XML (not), libxml, SimpleXML, “XML Parser”, XMLReader/XMLWriter, 和一大砣我不能認(rèn)出的東西就省略了. 當(dāng)然會(huì)有些不同, 你可以自由的弄清晰它們的區(qū)別.

>> 綁定了兩個(gè)特別的信用卡處理器, SPPLUS 和 MCVE. 什么?

>> 三種訪問 MySQL 數(shù)據(jù)庫的方式: mysql, mysqli, 和 PDO 抽象的一些東西.

C 影響

它需要擁有的自己的符號(hào). PHP 是個(gè)高層的, 動(dòng)態(tài)類型的語言. 然后大量的標(biāo)準(zhǔn)庫的部分仍然只是圍繞 C APIS 的薄層封裝, 伴隨著下面的東西:

>> "Out" 參數(shù), 盡管 PHP 可以返回 ad-hoc 哈?;蚝敛毁M(fèi)力的返回多參數(shù).

>> 至少一打的函數(shù)是為了獲取某子系統(tǒng)的最近一次錯(cuò)誤(見下文), 盡管 PHP 已存存異常處理功能8年了.

>> 有個(gè) mysql_real_escape_string, 盡管已有個(gè)具有相同參數(shù)的 mysql_escape_string, 僅僅因?yàn)樗?MySQL C API 的一部分.

>> 全局行為卻是非全局功能的(如 MySQL). 使用多個(gè) MySQL 連接需要顯示的對(duì)每個(gè)函數(shù)調(diào)用傳遞連接句柄.

>> 包裝器真的, 真的, 真的很薄. 例如, 調(diào)用了 dba_nextkey 而沒調(diào)用 dba_firstkey 將出現(xiàn)段錯(cuò)誤.

>> 有一堆的 ctype_* 函數(shù) (如 ctype_alnum) 映射類似名稱的 C 字符函數(shù), 而不是如, isupper.

Genericism

如果函數(shù)相做兩件略有不同的事, PHP 就搞出兩個(gè)函數(shù).

你怎樣反向排序? 在 Perl 中, 你可以用 { $b <=> $a}. 在 Python 中, 你可能用 .sort(reverse = True). 在 PHP 中, 有個(gè)特別的函數(shù)叫 rsort().

>> 那些看起來像 C error 的函數(shù): curl_error, json_last_error, openssl_error_string, imap_errors, mysql_error, xml_get_error_code, bzerror, date_get_last_errors, 還有其它的嗎?

>> 排序函數(shù): array_multisort, arsort, asort, ksort, krsort, natsort, natcasesort, sort, rsort, uasort, uksort, usort

>> 文本檢索函數(shù): ereg, eregi, mb_ereg, mb_eregi, preg_match, strstr, strchr, stristr, strrchr, strpos, stripos, strrpos, strripos, mb_strpos, mb_strrpos, plus the variations that do replacements

>> 有大量的別名: strstr/strchr, is_int/is_integer/is_long, is_float/is_double, pos/current, sizeof/count, chop/rtrim, implode/join, die/exit, trigger_error/user_error&hellip;

>> scandir 返回一個(gè)當(dāng)前給出目錄的文件列表. 而不是(可能有益)按返回目錄順序返回, 函數(shù)返回一個(gè)已排序的文件列表. 有個(gè)可選的參數(shù)可以按字母逆順返回. 這些用于排序很顯然很不夠.

>> str_split 將字符串拆成等長(zhǎng)的塊. chunk_split 將字符串拆成等長(zhǎng)的塊, 然后用個(gè)分隔符連接.

>> 讀取壓縮文件需要一套單獨(dú)的函數(shù), 取決于格式. 有六套函數(shù), 它們的 API 都不同, 如 bzip2, LZF, phar, rar, zip, 和gzip/zlib

>> 因?yàn)槭褂脜?shù)數(shù)組調(diào)用函數(shù)是如此的別扭(call_user_func_array), 所以有些配套的像 printf/vprintf 和 sprintf/vsprintf. 它們做相同的事, 但一個(gè)帶多個(gè)參數(shù), 另一個(gè)帶參數(shù)數(shù)組.

文本

>> preg_replace 帶 /e (eval) 標(biāo)志的將用待替換的字符串替換匹配的部分, 然后 eval 它.

>> strtok 的設(shè)計(jì)顯然是和 C 函數(shù)等效的, 由于很多原因, 已被認(rèn)為是個(gè)壞注意. PHP 可以輕易的返回一個(gè)數(shù)組(而這在C中別扭), 很多的hack strtok(3) 用法 (修改字符串某處), 在這里不能使用.

>> parse_str 解析查詢字符串, 從函數(shù)名看不出任何跡象. 而它會(huì) register_globals 并轉(zhuǎn)存查詢字符串到本地范圍變量中, 除非你傳遞一個(gè)數(shù)組來填充. (當(dāng)然, 什么也不返回)

>> 碰到空分隔符, explode 會(huì)拒絕分割. 每個(gè)其它的字符串拆分實(shí)現(xiàn)采取這種作法的意思應(yīng)該是把字符串應(yīng)拆分成字符; PHP有一個(gè)拆分函數(shù), 令人迷惑的稱為 str_split 而卻描述為 "將字符串轉(zhuǎn)成數(shù)組".

>> 格式化日期, 有 strftime, 像 C API 處理本地語言環(huán)境一樣. 當(dāng)然也有 date, 完全不同的語法而僅用于 English.

>> "gzgetss -- 獲取 gz 文件的行指針并去除 HTML 標(biāo)記." 知道了這一系列函數(shù)的概念, 讓我去死吧.

>> mbstring

>> 都是關(guān)于 "multi-byte", 解決字符集的問題.

>> 仍然處理的是普通字符串. 有個(gè)單一的全局"默認(rèn)"的字符集. 一些函數(shù)允許指定字符集, 但它依賴于所有的參數(shù)和返回值.

>> 提供了 ereg_* 函數(shù), 但這些都被廢棄了. preg_* 很幸運(yùn), 用一些 PCRE-specific 標(biāo)記, 它們能理解 UTF-8.

系統(tǒng)和反射

>> 有一大堆的函數(shù), 聚焦于文本和變量. 壓縮和提取僅是冰山一角.

>> 有幾種方式讓PHP動(dòng)態(tài), 咋一看沒有什么明顯的不同或相對(duì)好處. 類工具不能修改自定義類; 運(yùn)行時(shí)工具取代了它并能修改自定義的任何東西; Reflection* 類能反射語言的大部分東西; 有很多獨(dú)特的函數(shù)是為了報(bào)告函數(shù)和類的屬性的. 這些子系統(tǒng)是獨(dú)立, 相關(guān), 多余的嗎?

>> get_class($obj) 返回對(duì)象的類名稱. get_class()返回被調(diào)用函數(shù)中的類的名稱. 撇開這些不說, 同一個(gè)函數(shù)會(huì)做完全不同的事情: get_class(null)... 行為象后者. 因此面對(duì)一個(gè)隨機(jī)的變量, 你不能信任它. 驚訝吧!

>> stream_* 類允許實(shí)現(xiàn)自定義的流對(duì)象給fopen和其它的內(nèi)建的類似文件處理的東西使用. 由于幾個(gè)內(nèi)部原因, "通知" 不能被實(shí)現(xiàn).

>> register_tick_function 能接受閉包對(duì)象. unregister_tick_function 不行; 相反, 它會(huì)拋出錯(cuò)誤, 抱怨閉包不能轉(zhuǎn)換成字符串.

>> php_uname 告知你當(dāng)前操作系統(tǒng)相關(guān)東西.

>> fork 和 exec 不是內(nèi)建的. 它們來自 pcntl 擴(kuò)展, 但默認(rèn)不包含. popen 不提供 pid 文件.

>> session_decode 用于讀取任意的 PHP session 字符串, 但僅當(dāng)有個(gè)活躍的 session 時(shí)才工作. 它轉(zhuǎn)存結(jié)果到 $_SESSION 中, 而不是返回它的值.

雜項(xiàng)

>> curl_multi_exec 不改變 curl_error 當(dāng)出錯(cuò)的時(shí)候, 但它改變 curl_error.

>> mktime 的參數(shù)是有順序的: hour, minute, second, month, day, year

數(shù)據(jù)操縱

程序什么都不是, 除了咀嚼和吐出數(shù)據(jù)以外. 大量的語言圍繞著數(shù)據(jù)操縱設(shè)計(jì), 從 awk 到 Prolog 到 C. 如果語言無法操縱數(shù)據(jù), 它就無法做任何事.

數(shù)字

>> Integers 在32位平臺(tái)是是有符號(hào)32位數(shù). 不像PHP的同時(shí)代者, 沒有自動(dòng) bigint 提升. 因此你的數(shù)學(xué)運(yùn)算可能會(huì)由于CPU體系結(jié)構(gòu)結(jié)果不一樣. 你唯一選擇大整數(shù)的方式是使用 GMP 或 BC 包裝函數(shù). (開發(fā)者可能已經(jīng)建義加入新的, 單獨(dú)的,64位類型. 這真是瘋了.)

>> PHP支持八進(jìn)制數(shù)語法, 以0開頭, 因此如 012 是10. 然而, 08變成了0. 8(或9)和任何接下來的數(shù)字消失了. 01c是個(gè)語法錯(cuò)誤.

>> pi 是個(gè)函數(shù). 或者有個(gè)常量, M_PI.

>> 沒有冪操作符, 只有 pow 函數(shù).

文本

>> 無Unicode支持. 只有ASCII工作是可靠的, 真的. 有個(gè) mbstring 擴(kuò)展, 上面提過的, 但會(huì)稍被打擊.

>> 這意味著使用內(nèi)建的string函數(shù)處理UTF-8文本會(huì)有風(fēng)險(xiǎn).

>> 相似的, 在ASCII外, 也沒有什么大小寫比較概念. 盡管有擴(kuò)展版本的大小寫敏感的函數(shù), 但它們不會(huì)認(rèn)為 &eacute; 等于 &Eacute;.

>> 你不能在變量中內(nèi)插keys , 如, "$foo['key']"是個(gè)語法錯(cuò)誤. 你也不能 unquote it (這樣會(huì)產(chǎn)生警告, 無論什么地方!), 或使用 ${...}/{$...}

>> "${foo[0]}"是對(duì)的. "${foo[0][0]}"是個(gè)語法錯(cuò)誤. 糟糕的拷貝類似 Perl 的語法 (兩個(gè)根本不同的語議)?

數(shù)組

嘔, 騷年.

>> 這家伙扮演list數(shù)據(jù)類型, 操作hash, 和排序set, 解析 list, 偶爾會(huì)有些奇怪的組合. 它是怎樣執(zhí)行的? 以何種方式使用內(nèi)存? 誰知道? 不喜歡, 反正我還有其它的選擇.

>> => 不是操作符. 它是個(gè)特別的結(jié)構(gòu), 僅僅存在于 array(...) 和 foreach 結(jié)構(gòu)中.

>> 負(fù)值索引不工作, 盡管 -1 也是個(gè)和0一樣的合法鍵值.

>> 盡管這是語言級(jí)的數(shù)據(jù)結(jié)構(gòu), 但沒有簡(jiǎn)短語法; array(...)是簡(jiǎn)短語法. (PHP 5.4 帶來了"literals", [...].)

>> => 結(jié)構(gòu)是基于 Perl , Perl允許 foo => 1 而不用引號(hào). 在PHP中, 你這么做會(huì)得到警告; 沒有無需引號(hào)創(chuàng)建 hash 字符串鍵值的方式.

>> 數(shù)組處理函數(shù)常常讓人迷惑或有不確定行為, 因?yàn)樗鼈儾坏貌粚?duì) lists, hashes, 或可能兩者的結(jié)合體做運(yùn)算. 考慮 array 分組, "計(jì)算arrays的不同部分".

$first  = array("foo" => 123, "bar" => 456);  $second = array("foo" => 456, "bar" => 123);  echo var_dump(array_diff($first, $second));

這段代碼將做什么? 如果 array_diff 將參數(shù)以 hashes 看待, 它們明顯是不同的; 相同的keys有不同的值. 如果以list看待, 它們?nèi)匀皇遣煌? 值的順序不同.

事實(shí)上 array_diff 認(rèn)為它們相等, 因?yàn)樗?sets 對(duì)待: 僅僅比較值, 忽略順序.

>> 同樣, array_rand 隨機(jī)選擇keys時(shí), 也有奇怪的行為, 這對(duì)大多數(shù)需要從列表中挑出東西的用例沒什么幫助.

盡管大量PHP代碼依賴key的順序:

array("foo", "bar") != array("bar", "foo")   array("foo" => 1, "bar" => 2) == array("bar" => 2, "foo" => 1)

>> 如果兩個(gè)數(shù)組混合的話, 會(huì)發(fā)生什么? 我留給讀者自己弄清楚. (我不知道)

>> array_fill 不能創(chuàng)建0長(zhǎng)度的數(shù)組; 相反它會(huì)發(fā)出警告并返回 false.

>> 所有的(很多的...) 排序函數(shù)就地操作而什么都不返回. 想新建一個(gè)已排序數(shù)組的拷貝, 沒門; 你不得不自己拷貝數(shù)組, 然后排序, 然后再使用數(shù)組.

>> 但 array_reverse 返回一個(gè)新數(shù)組.

>> 一堆被排序的東西和一些鍵值對(duì)聽起來像是個(gè)某種強(qiáng)大的處理函數(shù)參數(shù)的方式, 但, 沒門.

非數(shù)組

>> 標(biāo)準(zhǔn)庫包含 "快速哈希", "特定的強(qiáng)類型"的hash結(jié)構(gòu)OO實(shí)現(xiàn). 然, 深入它, 有4類, 每種處理不同的鍵值對(duì)類型組合. 不清楚為什么內(nèi)建的數(shù)組實(shí)現(xiàn)不能優(yōu)化這些極其普通情況, 也不清楚它相對(duì)的性能怎樣.

>> 有個(gè) ArrayObject 類 (實(shí)現(xiàn)了4個(gè)不同的接口) , 它包裝數(shù)組讓它看起來像對(duì)象. 自定義類可以實(shí)現(xiàn)同樣的接口. 但只有限的幾個(gè)方法, 其中有一半不像內(nèi)建的數(shù)組函數(shù), 而內(nèi)建的數(shù)組函數(shù)不知道怎樣對(duì)ArrayObject或其它的類數(shù)組的類型操作.

函數(shù)

>> 函數(shù)不是數(shù)據(jù). 閉包實(shí)際上是對(duì)象, 但普通的函數(shù)不是. 你甚至不能通過它們裸名稱引用它們; var_dump(strstr) 會(huì)發(fā)出警告并猜測(cè)你的意思是字符串字面量, "strstr". 想辨別出字符串還是"函數(shù)"引用, 沒門.

>> create_function 基本上是個(gè) eval 的包裝者. 它用普通的名字創(chuàng)建函數(shù)并在全局范圍安裝它(因此永遠(yuǎn)不會(huì)被垃圾回收---不要在循環(huán)中使用!). 它實(shí)際上對(duì)當(dāng)前上下文一無所知, 因?yàn)樗皇情]包. 名字包含一個(gè) NUL 字節(jié), 因此永遠(yuǎn)不會(huì)與普通函數(shù)沖突 (因?yàn)槿绻谖募娜魏蔚胤接?NUL的話, PHP 的解析器會(huì)失敗).

>> Declaring a function named __lambda_func will break create_function&mdash;the actual implementation is to eval-create the function named __lambda_func, then internally rename it to the broken name. If __lambda_func already exists, the first part will throw a fatal error.

其它

>> 對(duì) NULL 使用 (++) 生成 1. 對(duì) NULL 用 (--) 生成 NULL.

>> 沒有生成器.

Web 框架

執(zhí)行環(huán)境

>> 一個(gè)單一共享文件 php.ini, 控制了 PHP 的大部分功能并織入了復(fù)雜的針對(duì)覆蓋什么與何時(shí)覆蓋的規(guī)則. PHP軟件能部署在任意的機(jī)器上, 因此必須覆蓋一些設(shè)置使環(huán)境正常, 這在很大程序上會(huì)違背像 php.ini 這樣的機(jī)制的使用.

>> PHP基本上以CGI運(yùn)行. 每次頁面被點(diǎn)擊, PHP 在執(zhí)行前, 重編譯整個(gè)環(huán)境. 就連 Python 的玩具框架的開發(fā)環(huán)境都不會(huì)這樣.

>> 這就導(dǎo)致了整個(gè) "PHP 加速器" 市場(chǎng)的形成, 僅僅編譯一次, 就能加速PHP, 就像其它的語言一樣. Zend, PHP的幕后公司, 將這個(gè)做為它們的商業(yè)模式.

>> 很長(zhǎng)時(shí)間以來, PHP的錯(cuò)誤默認(rèn)輸出給客戶端 -- 我猜是為開發(fā)環(huán)境提供幫助. 我不認(rèn)為這是真相, 但我仍然看到偶爾會(huì)有mysql 錯(cuò)誤出現(xiàn)在頁面的頂部.

>> 在 <?php ... ?>標(biāo)簽外的空白, 甚至在庫中, PHP以文本對(duì)待并解析給響應(yīng) (或者導(dǎo)致 "headers already sent" 錯(cuò)誤). 一個(gè)流行的做法是忽略 ?>關(guān)閉標(biāo)簽.

部署

部署方式常常被引述為PHP的最高級(jí)部分: 直接部署文件就可以了. 是的, 這比需要啟動(dòng)整個(gè)進(jìn)程的 Python 或 Rury 或 Perl 要容易. 但 PHP 留下了許多待改進(jìn)的地方.

我很樂意以應(yīng)用服務(wù)器的方式運(yùn)行Web應(yīng)用程序并反向代理它們. 這樣的代價(jià)最小, 而好處多多: 你可以單獨(dú)管理服務(wù)器和應(yīng)用程序, 你可以按機(jī)器的多或少運(yùn)行運(yùn)行多個(gè)或少量應(yīng)用進(jìn)程, 而不需要多個(gè)web服務(wù)器,你可以用不同的用戶運(yùn)行應(yīng)用, 你可以選擇web服務(wù)器, 你可以拆下應(yīng)用而無需驚動(dòng)web服務(wù)器, 你可以無縫部署應(yīng)用等等. 將應(yīng)用與web服務(wù)器直接焊接是荒謬的, 沒有什么好的理由支持你這么做.

>> 每個(gè) PHP 應(yīng)用程序都使用 php.ini . 但只有一個(gè) php.ini 文件, 它是全局的; 如果你在一個(gè)共享的服務(wù)器上, 需要修改它, 或者如果你運(yùn)行兩個(gè)應(yīng)用需要不同的設(shè)置, 你就不走運(yùn)了; 你不得不向組織申請(qǐng)所有必須的設(shè)置并放在應(yīng)用程序, 如使用 ini_set 或在 Apache 的配置文件或在 .htaccess設(shè)置. 如果你能做的話. 可能 wow , 你有大量的地方需要檢查以找出怎樣獲取已設(shè)置的值.

>> 類似的, "隔離"PHP應(yīng)用的方法也不容易, 它依賴于系統(tǒng)的其它部分. 想運(yùn)行兩個(gè)應(yīng)用程序,想要不同的庫版本, 或不同的PHP版本本身? 開始構(gòu)建另一人Apache的拷貝吧.

>> "一堆文件"方案, 除了使路由像只病重的笨驢外, 還意味著你不得不小心處理白名單或黑名單, 以控制什么東西可訪問, 這是因?yàn)槟愕?URL 層次也就是你的代碼樹的層次. 配置文件和其它的"局部模塊"需要C之類的東西守護(hù)以避免直接加載. 版本控制系統(tǒng)的文件(如 .svn) 需要保護(hù). 使用 mod_php , 使得文件系統(tǒng)的所有東西都是潛在的入口; 使用應(yīng)用服務(wù)器, 僅有一個(gè)入口, 并且僅通過 URL 控制調(diào)用與否.

>> 你不能無縫的升級(jí)那堆以 CGI-style 運(yùn)行的文件, 除非你想要應(yīng)用崩潰和出現(xiàn)未定義行為, 當(dāng)用戶在升級(jí)的間歇期點(diǎn)擊你的站點(diǎn)時(shí).

>> 盡管配置 Apache 運(yùn)行 PHP 很"簡(jiǎn)單", 仍然會(huì)有一些陷阱. 而 PHP 文檔建議使用 SetHandler 使得 .php 文件以 PHP方式運(yùn)行, AddHandler 看起來運(yùn)行良好, 然而事實(shí)上會(huì)有問題.

當(dāng)你使用 AddHandler, 你在告知 Apache "以 php 執(zhí)行它" , 這是一個(gè)可能的處理 .php 文件的方式. 但! Apache 對(duì)文件的擴(kuò)展名不這樣認(rèn)為. 它被設(shè)計(jì)為能支持如, index.html.en 這樣的文件. 對(duì)于 Apache , 文件可以同時(shí)具有任意數(shù)量的擴(kuò)展名.

猜想, 你有個(gè)文件上傳的表單, 存儲(chǔ)一些文件到公共目錄中. 確保沒人能上傳 PHP 文件, 你僅僅檢查文件不能有.php 擴(kuò)展名. 所有的攻擊需要做的只是上傳以 foo.php.txt 命名的文件; 你的上傳工具不會(huì)看出問題, Apache 會(huì)認(rèn)為它是個(gè) PHP, 它會(huì)很高興的執(zhí)行.

這里不是 "使用原始文件名" 或 "沒有更好的驗(yàn)證"導(dǎo)致的問題; 問題是你的web服務(wù)器要被配置用來運(yùn)行任何舊代碼, 使得PHP "容易部署". 這不是理論上的問題; 我已發(fā)現(xiàn)很多實(shí)際的站點(diǎn)有類似的問題了.

缺失的特性

我認(rèn)為所有這些都是以構(gòu)建一個(gè)Web應(yīng)用為中心的. 對(duì)PHP看起來很合理, 是它的銷售賣點(diǎn)之一, 它是 "Web語言", 理應(yīng)有它們.

>> 無模塊系統(tǒng). PHP就是模版.

>> 無 XSS 過濾器. htmlspecialchars 不是 XSS 過濾器.

>> 無 CSRF 保護(hù). 你必須自己做.

>> 無通用標(biāo)準(zhǔn)的數(shù)據(jù)庫API. 像PDO這類東西不得不包裝每個(gè)特定數(shù)據(jù)庫的API, 分別抽象不同部分.

>> 無路由系統(tǒng). 你的站點(diǎn)結(jié)構(gòu)就是你的文件系統(tǒng)結(jié)構(gòu).

>> 無認(rèn)證或授權(quán).

>> 無開發(fā)服務(wù)器.

>> 無交互調(diào)試模式.

>> 無一致的部署機(jī)制; 僅僅"拷貝所有文件到服務(wù)器中".

安全

語言邊界

PHP的蹩腳安全機(jī)制可能會(huì)放大, 因?yàn)樗媚痴Z言拿出數(shù)據(jù), 又把它轉(zhuǎn)存到另一個(gè)中. 這是個(gè)壞注意. "<script>" 可能在SQL中意味著什么都不是, 但在HTML中就很是了.

讓情況更糟糕的是通常有人哇哇喊到 "你的輸入要消毒". 那完全錯(cuò)誤; 你不可能有什么魔法使塊數(shù)據(jù)完全"干靜". 你需要做的就是對(duì)語言說: SQL使用占位符, 進(jìn)程孵化使用參數(shù)列表, 等等.

>> PHP公然鼓勵(lì) "消毒": 有個(gè)數(shù)據(jù)過濾擴(kuò)展可以做到.

>> 所有的 addslashes, scripslashes, 和其它的 slashes相關(guān)的東西都是廢物, 毫無用處.

>> 我只能告訴你這么多, 無法安全的孵化進(jìn)程. 你僅能通過shell執(zhí)行字符串. 你的選擇是瘋狂的轉(zhuǎn)義, 并希望默認(rèn)的shell使用正確的轉(zhuǎn)義, 或手動(dòng)的 pcntl_fork_exec 和 pcntl_exec.

>> 所有的轉(zhuǎn)義命令和轉(zhuǎn)義參數(shù)存在大致相同的描述. 注意在Windows中, 轉(zhuǎn)義參數(shù)不工作 (因?yàn)樗僭O(shè)成 Bourne shell 語議), 轉(zhuǎn)義命令僅僅用空格替換一堆標(biāo)點(diǎn)符號(hào), 因?yàn)闆]人能搞清楚 Windows 命令轉(zhuǎn)義行為 (它可能默默的破壞你試圖做的任何事情).

>> 原始的內(nèi)建 MySQL 綁定, 仍然廣泛使用, 它無法創(chuàng)建 prepared statements.

直到今天, PHP 文檔關(guān)于SQL注入的建議還是讓人抓狂的做如類型檢查, 使用sprintf 和 is_numeric, 在每個(gè)地方手動(dòng)的使用mysql_real_escape_string , 或在每處手動(dòng)使用 addslashes (這個(gè)"可能更有用"!) 這樣的實(shí)踐. 并沒有提到 PDO 或 參數(shù)化, 除了在用戶評(píng)論中有點(diǎn)線索. 至少在兩年以前, 我就有具體的向 PHP dev 抱怨過了 , 他被驚動(dòng)了, 而頁面卻從未變過.
Insecure-by-default

>> register_globals. 它被默認(rèn)關(guān)閉的,而在5.4中去除了. 我不在乎.

>> include 接受 HTTL URLS. 和上面一樣.

>> Magic quotes. So close to secure-by-default, and yet so far from understanding the concept at all.

核心

PHP解釋器本身就有一些惱人的安全問題.

>> 2007年的時(shí)候, 解析器有個(gè)整數(shù)溢出漏洞. 修復(fù)始于 if(size > INT_MAX) return NULL; 從那以后就走下坡路了. (對(duì)于那些不需要使用C的人: 曾經(jīng), INT_MAX 是適合變量最大整數(shù). 我希望你能從這里搞清楚其余的東西.)

>> 最近, PHP 5.3.7 包括了個(gè) crypt() 函數(shù), 有個(gè)漏洞讓任何人可以用任何密碼登錄.

>> PHP5.4是容易遭受拒絕服務(wù)攻擊,因?yàn)樗枰狢ontent-Length頭(任何人都可以設(shè)置),并試圖分配更多內(nèi)存。這是一個(gè)壞主意。

我可以挖掘更多, 但重點(diǎn)不是這有很多X漏洞 -- 是軟件就有bugs, 無論如何都有. 這些自然是令人咋舌. 我并沒有特意尋找這些; 但在過去的幾個(gè)月里, 它們自己送上門來了.

總結(jié)

一些評(píng)論會(huì)理所當(dāng)然的指出我沒得出任何結(jié)論. 好吧, 我是沒有結(jié)論. 如果你一路看到了這里, 我假設(shè)一開始你就同意我了 :)

如果你僅了解PHP而對(duì)學(xué)習(xí)其它東西感興趣, 可以看看 Python 教程, 嘗試 Flask 這個(gè)為web準(zhǔn)備的家伙. (我不是它的模版語言的鐵桿粉絲, 但它確實(shí)很好的完成了這些工作.) 它將你的應(yīng)用分成多個(gè)部分, 但它們看起來仍然是一致的. 我可能稍后會(huì)寫個(gè)關(guān)于這個(gè)的貼子; 旋風(fēng)般的介紹整個(gè)語言和不同于這里所說的web堆棧.

之后或?qū)τ诟蟮捻?xiàng)目, 你可能需要 Pyramid, 一個(gè)中等規(guī)模的框架, 或者是 Django, 一個(gè)構(gòu)建站點(diǎn)的復(fù)雜的框架, 如 Django站點(diǎn).

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。

網(wǎng)站題目:如何全面分析PHP的糟糕設(shè)計(jì)
標(biāo)題鏈接:http://muchs.cn/article0/jejeio.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、網(wǎng)站內(nèi)鏈、自適應(yīng)網(wǎng)站、網(wǎng)站制作、網(wǎng)站改版虛擬主機(jī)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作