[心得] COMPOSER進階原理:PHP命名空間與PSR-0

看板PHP作者 (阿川)時間11年前 (2014/12/10 23:42), 編輯推噓11(1102)
留言13則, 11人參與, 最新討論串1/1
小弟花了很長的時間 才稍微搞懂Composer、namespace、PSR-0 希望這篇文章能幫一些人節省時間 <(_ _)> 網頁好讀版 http://blog.turn.tw/?p=1122 ----------------------------------- 上次的Composer設計原理與基本用法說明了PHP套件管理的歷史與社群提出的解決之道,本 篇文章接著談類別管理的進階議題。 當類別名稱一樣… 當專案大了起來,有時候會有類別名稱重複的問題。 假設今天要撰寫一個論壇模組,提供討論區與留言板功能。 你一定很想將討論區的文章與留言板的文章都命名類別為Article: // BoardArticle.php <?php class Article{ //... } // ForumArticle.php <?php class Article{ //... } 當然了,這麼做會得到一個結果: Fatal error: Cannot redeclare class Article 這種問題有一個簡單的解決辦法,就是加上前綴字。 類別分別命名為ForumArticle與BoardArticle就可以了。 Q1: 等一下!這個解法好陽春!我看到至少4個問題: 1. 類別名稱容易變得冗長。 2. 有些類別一開始你以為不會跟人重複,結果之後真的重複了。 難道永遠替類別加前綴字? 3. 類別名稱寫Article俐落多了!文章就是概念上的文章,不要逼我告訴你是討論區 還是留言板!如果專案用到兩種留言板模組,分別由以前的兩個前輩寫好, 難道還要逼我把作者名稱寫進去? class TonyBoardArticle{ //... } class JackBoardArticle{ //... } 4. 如果我在打造框架(framework)呢?幾乎會把所有常見名詞用過一次(像是Request、 Loader、Response、Controller、Model等類別)! 難道前面全部前綴?看看Codeigniter的原始碼,全部用CI_當作前綴。超醜的。 命名空間(namespace)登場 於是PHP從5.3版之後支援了命名空間(namespace)。 所以可以用Article替類別定義了: // BoardArticle.php <?php namespace Board; class Article{ //... } // ForumArticle.php <?php namespace Forum; class Article{ //... } 使用類別時只要加上命名空間即可: //index.php <?php include 'BoardArticle.php'; include 'ForumArticle.php'; $article = new Forum\Article(); $post = new Board\Article(); 如果當前的php檔只用到其中一個Article類別, 可以指定當前的php檔只用哪個命名空間+類別的組合: <?php include 'BoardArticle.php'; include 'ForumArticle.php'; use Forum\Article; $article = new Article(); $fArticle = new Forum\Article(); $bArticle = new Board\Article(); 如此一來,當php找不到Article類別時,便會去使用use關鍵字宣告的組合。 當然了,就算用use指定過,原本的宣告方式還是可以用的。(如最下方兩行所示) 當東西多了起來… OK,可以繼續完成我們的論壇模組了! 討論區跟留言板有各自的文章,再來還需要「推文」: <?php namespace Board; class Comment{ // ... } <?php namespace Forum; class Comment{ // ... } 使用剛剛學到的命名空間去載入他們: <?php // index.php include 'BoardArticle.php'; include 'ForumArticle.php'; include 'BoardComment.php'; include 'ForumComment.php'; $article = new Forum\Article(); $comment = new Forum\Comment(); // ... 果然是漂亮的各種命名阿! Q2: include有好多行!上次的Composer設計原理與基本用法提到了Composer可以解決這種 問題,當引入命名空間之後,Composer也能發揮作用嗎? 是的。 Composer登場 跟上次初學Composer一樣,建立一個composer.json檔: { "autoload": { "files": [ "ForumArticle.php", "ForumComment.php", "BoardArticle.php", "BoardComment.php" ] } } 注意,上次我們用”classmap”指定資料夾、把資料夾內檔案全掃一次,這次我們用” files”分別設定各個檔案。 再來,在terminal輸入 composer install 執行完畢之後,跟上次一樣,只要載入一個檔案: <?php require 'vendor/autoload.php'; $article = new Forum\Article(); $comment = new Forum\Comment(); 就可以使用各個類別囉! Q3: 等一下!看起來跟沒有命名空間的時候差不多啊! 一樣是把php檔自動require進去而已? 對啊,你最上面的原始寫法,也只是手動載入好幾個檔案, 在載入的時候本來就沒有特別之處: <?php // index.php include 'BoardArticle.php'; include 'ForumArticle.php'; include 'BoardComment.php'; include 'ForumComment.php'; $article = new Forum\Article(); $comment = new Forum\Comment(); // ... 載入php檔就只是載入,跟命名空間是兩回事。 Q4: 還是不太對啊!上次我用classmap一次把好幾個資料夾內容掃過,這次我用files分別 指定每個檔案幹嘛?Composer不是厲害在能指定資料夾去自動掃過? ……你說的沒錯。 開個my_lib資料夾,把4個php檔都丟進去吧。composer.json這樣寫就好了: { "autoload": { "classmap": [ "my_lib" ] } } 再來,在terminal輸入 composer install 這樣就搞定了! 其實我只是想示範,除了用classmap設定資料夾之外,也可以用files直接指定檔案。 Q5: OK,原諒你。不過,我必須說,我今天什麼都沒學到。最後還是在composer.json寫 classmap而已,跟上次一模一樣。 是的…我剛說了,載入php檔就只是載入,跟命名空間是兩回事。 我今天介紹的namespace功能是PHP本身提供的。而Composer只是協助你載入的工具、 當然不可能改變程式語言本身。 Composer只是幫助你少打那一串require而已。 Q6: ㄟ等等…有個地方我覺得很醜。我們現在的my_lib資料夾裡面有4個檔案,檔名很醜: "ForumArticle.php" "ForumComment.php" "BoardArticle.php" "BoardComment.php" 類別名稱本身都是俐落的Article跟Comment了,檔案名稱還是有前綴字。 但也不可能有兩個Article.php、兩個Comment.php。你有沒有辦法解決這個美感問題? 這個問題簡單,把那個my_lib資料夾刪掉,開一個Forum資料夾、一個Board資料夾。 本來的ForumArticle.php改成Article.php丟進Forum資料夾、 本來的BoardArticle.php改成Article.php丟進Board資料夾。 composer.json改寫成這樣: { "autoload": { "classmap": [ "Board", "Forum" ] } } 再來,在terminal輸入 composer install 這招不錯吧!檔案名稱就是類別名稱,乾淨俐落! 而且資料夾的名字本身就是namespace的名稱! 以後都這樣做啦!一用到命名空間就開個同名資料夾把檔案丟進去! Q7: 這招我覺得還好耶…。本來檔案都放在my_lib,我在composer.json只要填my_lib一行 就好,現在變成要填兩行。要是我這個論壇模組有一大堆類別、用了一大堆命名空間呢? 那我classmap底下不就要填入好幾行?那我寧可全部丟進my_lib,只填my_lib一行! 唔,這樣說也是有道理。那重新建立my_lib資料夾,把Board跟Forum資料夾丟進 my_lib資料夾。composer.json改回這樣寫: { "autoload": { "classmap": [ "my_lib" ] } } 再來,在terminal輸入 composer install classmap不只是告訴Composer去載入哪幾個資料夾內的檔案,還會把資料夾內的 資料夾也全部掃過一次。 怎麼樣,Composer夠神吧。 Q8: 原來classmap底下會遞迴掃描下去…。我決定了,我的Forum跟Board都是在 提供線上討論功能,我決定替我這個模組命名為Discussion。 我要在my_lib底下開Discussion資料夾,然後把原本的Forum跟Board資料夾都丟進去。 你覺得這個想法如何? 還不錯。一個Discussion資料夾就是你的整個Discussion模組。 提供了討論區、留言板功能的Discussion模組,我喜歡。 Q9: 好像忘了什麼…。啊,剛剛說命名空間跟檔案結構符合會最漂亮。那我要把那4個檔案 的namespace改成這樣: <?php namespace Dicussion/Forum; class Article{ //... } 剛剛說了,載入檔案就只是載入檔案,跟命名空間無關。 現在檔案結構沒變,所以我應該不用重新輸入composer install。 讓我試試…。 靠!怎麼噴error了!你騙我? Fatal error: Class 'Discussion\Forum\Comment' not found 呃…,我前面的說法確實有點誤導。 PHP自動載入的基本函式長這樣: void __autoload ( string $class ) 如你所見,PHP至少需要Composer提供資訊指出$class該去哪個檔案找。 namespace改變之後,PHP會找不到對應的$class在哪。 所以還是輸入一下composer install吧!Composer會把需要的資訊整理好的。 Q10: OKOK,我知道了,我駕馭這一切了。我覺得這個Dicsussion模組真的超屌的, 不但命名空間漂亮,連檔案結構都漂亮。 我要把這個Discussion資料夾整個丟給我朋友,他們公司最近需要論壇模組。 讓我打電話給他…。 「什麼?你們已經做好半個論壇模組了?你只需要我模組的其中幾個功能? 你們的模組也是放在Discussion資料夾?」 糟糕,資料夾名稱重複了!所以我的模組拿給別人還是有不相容的可能,怎麼辦? 沒有錯..還記得你Q1提到的第3個狀況嗎?確實有把作者名稱加進去的必要! 別怕,我教你。你開一個Tony資料夾,把整個Discussion資料夾丟進去。 接著所有檔案namespace改成像這樣: <?php namespace Tony/Dicussion/Forum; class Article{ //... } 要用的時候就這樣喔: <?php require 'vendor/autoload.php'; $article = new Tony\Discussion\Forum\Article(); $comment = new Tony\Discussion\Forum\Comment(); 是變得有點長啦。 但這下搞定了吧!作者名稱再撞到的話,就改個獨特的名稱就是了! 終於。讓我們談談PSR-0 你一定常聽到PSR-0對吧! PSR-0是PHP跨框架相容性統一標準組織訂出來的自動載入慣例。 來談談PSR-0幾個最重要的要求吧! * 命名空間加上類別名稱一定要長這樣: \<Vendor Name>\(<Namespace>\)*<Class Name> * 前面一定要是作者名稱 * 中間可以有任意層次的命名空間、最後是類別名稱。 * 中間任意層次的命名空間直接對應到檔案結構。 發現了嗎?在剛剛Q1~Q10的過程中,其實你已經把PSR-0學完了, 連設計原理都一起搞懂了。 懂這些之後,你也可以做好自己的模組、發佈到Packagist給全世界 透過composer下載、使用了! 最後,如果遵守psr-0的話,composer.json可以這樣寫: { "autoload": { "psr-0": { "Tony\\Discussion\\": "my_lib/" } } } 注意,雙引號、兩次反斜線並沒有特別意思,只是json規定的格式。 跟classmap一樣都可以完成任務。兩者其實是有差別的…,我們下次再談。 結語 Composer工具以及PHP-FIG組織的出現, 讓一直以來散落各地的PHP社群開始有集中的趨勢。 換句話說,各社群終於能共享彼此的library了。 然而,如你所見,psr-0不但導致檔案結構容易變得深層,還要求檔案結構必須配合程式碼 ,這也招致了不少批評。 除此之外,composer autoload內的classmap跟psr-0到底如何分工? 效能差異又為何?這些問題也都還在爭論與驗證當中。 不過,PSR-0在各框架已被廣泛支援,因此建議你還是需要有所瞭解。 最後… 現在已經出現psr-0的替代方案,稱為psr-4。 PSR-0從2014-10-21開始被註明為不建議使用。 至於PSR-4..我們下次再談。 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 118.160.223.112 ※ 文章網址: http://www.ptt.cc/bbs/PHP/M.1418226146.A.0AE.html

12/10 23:48, , 1F
12/10 23:48, 1F

12/11 09:00, , 2F
12/11 09:00, 2F

12/11 09:31, , 3F
推推
12/11 09:31, 3F

12/11 09:43, , 4F
推推推推推
12/11 09:43, 4F

12/11 09:51, , 5F
12/11 09:51, 5F

12/11 22:20, , 6F
12/11 22:20, 6F

12/12 09:01, , 7F
題外話, 其實這種 namespace 的命名規則也是 java package
12/12 09:01, 7F

12/12 09:02, , 8F
建議的命名方式, 因為這些都是可以到處 deploy 的東西
12/12 09:02, 8F

12/12 09:02, , 9F
因此定出來的規則也就大同小異
12/12 09:02, 9F

12/13 14:55, , 10F
推,用心寫的易於消化
12/13 14:55, 10F

12/15 04:23, , 11F
推!有在用,但不是很熟悉……
12/15 04:23, 11F

12/16 11:46, , 12F
推,問答的方式很活潑,讓我想到 Head First 系列
12/16 11:46, 12F

02/23 02:17, , 13F
02/23 02:17, 13F
文章代碼(AID): #1KY6dY2k (PHP)
文章代碼(AID): #1KY6dY2k (PHP)