顯示廣告
隱藏 ✕
※ 本文為 dinos 轉寄自 ptt.cc 更新時間: 2014-12-11 21:45:46
看板 PHP
作者 poopoo888888 (阿川)
標題 [心得] COMPOSER進階原理:PHP命名空間與PSR-0
時間 Wed Dec 10 23:42:19 2014


小弟花了很長的時間

才稍微搞懂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
yoyotvyoo: 推1F 12/10 23:48
rickysu: 推2F 12/11 09:00
lucky1lk: 推推3F 12/11 09:31
DJake: 推推推推推4F 12/11 09:43
onininon: 推5F 12/11 09:51

--
※ 看板: dinos 文章推薦值: 0 目前人氣: 0 累積人氣: 156 
分享網址: 複製 已複製
guest
x)推文 r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇