Java有Maven, Node.js有npm, ROR有gem, 這些語言的程序員在開心地使用包管理工具加速開發效率時,PHPer們還在複製粘貼的黑暗中。PHP在Composer以前,包管理的歷史不堪回首。php
在至關長的一段時間內,若是應用依賴於第三方庫,PHPer須要拷貝這些庫的源代碼, 或者經過PEAR、PECL安裝。若是第三方庫又依賴於更多的第三方庫,那麼很快就會進入依賴的黑洞。直到Composer出現,PHPer們看到了屬於PHP的包管理的曙光。git
注:Composer更新很慢,很難成功的,請看另外一篇博文 >>Composer更新慢的解決方案<<程序員
下面將以建立一個電商網站爲例,介紹Composer的使用方法。github
在咱們開始一個項目的時候,首先會給項目取一個名字,咱們暫且叫絲綢之路吧,代號silk。首先要寫一個Composer的配置文件,來描述項目,爲此,在項目的根目錄下,創建文件名爲composer.json的配置文件。內容以下:web
<!-- lang: js --> { "name": "meta/silk", "description": "another e-commerce website", "keywords": ["silk", "online shop", "good"], "homepage": "http://www.xxx.com ", "time": "2014-12-30", "license": "MIT", "authors": [ { "name": "Elvis Lim", "email": "elvis@xxx.com", "homepage": "http://www.xxx.com", "role": "Engineer" } ]}
若是您熟悉JSON格式,那麼上面這段內容不言而喻。事實上,這些鍵值對都是可選的。也就是說,能夠都不寫。可是若是要把項目打包成公共包發佈,那麼這些仍是須要寫上的,給你的包取個名字總不爲過。讓咱們來過一下這些鍵值對的意義吧。面試
<!-- lang: js --> "name": "meta/silk",
name, 表示包的名稱。若是你常常在Github上混,那這個值的表達方式必定很是熟悉啦。解釋下,一般包名包含兩部分,而且以 / 分隔。斜杆前面部分,表明包的全部者。目前大部分的包做者都喜歡用Github的用戶名做爲這部分的值。斜杆後面部分表明包的名稱。儘可能保持簡單和有意義些,便於記憶和傳播。大部分狀況下,不少人會用Github的代碼庫名稱來命名,固然,這種狀況下,代碼要存在Github比較有意義。shell
<!-- lang: js --> "description": "another e-commerce website",
應用簡介,這部分儘可能簡潔介紹下項目,別長篇大論。若是確實有不少話要說,那麼能夠寫在README.md文件裏。npm
<!-- lang: js --> "keywords": ["silk", "online shop", "good"],
關鍵詞的值是一個字符串數組,在發佈成公用庫的是時候,做爲元數據信息,有利於包的搜索和發現。json
<!-- lang: js --> "homepage": "http://www.xxx.com ",
主頁,能夠放你想放的任何頁面地址。swift
<!-- lang: js --> "license": "MIT",
若是你決定將包公開發布,那麼記得選擇一個合適的許可證。這樣別的程序員在引用包的時候,經過查看許可證,確保沒有法律上的問題。
<!-- lang: js --> "authors":[{}]
做者字段能夠包含一個對象數組,也就是說能夠提供多個做者信息。
目前爲止,都是關於包自己的信息描述。做爲一個電商網站,可以發送電子郵件、導出訂單到Excel表是基本需求,這個時候天然想到了使用現有的庫來實現這些功能。要獲取這些庫,最簡單的方式是,搜索下這些庫,找到下載地址,下載個zip包,而後解壓到相應目錄下,根據文檔引入相應的文件。使用Composer,能夠更加自動和優雅地完成這個過程,這就是Composer的依賴管理。
在composer.json文件裏增長一個新的字段:require。這個字段的值是一個對象,一樣以鍵值對的形式構成。以上述提到的兩個依賴位置,寫成composer管理的方式以下:
<!-- lang: js --> 「require」: { "swiftmailer/swiftmailer": 5.3.*[@dev](https://my.oschina.net/Thinker277), "phpoffice/phpexcel": "dev-master" }
以swiftmailer爲例,swiftmailer/swiftmailer 表明的是包名稱,5.3.*@dev, 是版本信息。合起來的意思就是說,咱們將要開發的應用,依賴於swiftmailer的5.3.*版本。其中:
5.3.*表示,可使用5.3.1版本,也可使用5.3.2版本,composer在獲取的時候,將尋找5.3版本下最新的版本。版本號支持一些更加寬泛的約束,好比>=1.0, >=1.0, <2.0,更加具體的信息能夠查看:http://docs.phpcomposer.com/01-basic-usage.md#The-require-Key
@dev表示能夠獲取開發版本。一般,開發版本意味非穩定版本,極可能存在bug。穩定性標籤能夠做用於特定的依賴項,也能夠做用於全局。
做用特定依賴項:默認狀況下,composer只會獲取穩定版本,若是這個例子咱們不加@dev約束,而5.3.*版本都是開發版本,那麼在獲取的時候composer就會報錯,指出改版本不符合要求。若是肯定這個開發版本沒有問題,那麼就能夠經過加@dev,讓Composer獲取這個開發版本。
全局穩定性設置:經過設置minimum-stability的值,來告訴Composer當前開發的項目的依賴要求的包的全局穩定性級別,它的值包括:dev、alpha、beta、RC、stable,stable是默認值。
至此,兩個依賴添加完畢,咱們能夠運行下Composer包更新命令,看看效果啦。
<!-- lang: shell --> composer install
成功運行完畢,會在根目錄下發現vendor文件夾,裏面包含了剛剛咱們列出來的兩個包文件代碼。
有時候,咱們會發現,有些包依賴只會在開發過程當中使用,正式發佈的程序不須要這些包,這個時候,就須要用到另一個鍵,即require-dev。例如,咱們想用codeception進行單元測試,那麼就能夠經過require-dev引入這個開發環境下的依賴包:
<!-- lang: js --> 「require-dev」: { "codeception/codeception": "2.0.0 " }
加了這個依賴後,再運行下命令看看效果。
<!-- lang: shell --> composer install
自此,composer已經幫咱們把須要的庫文件下載下來啦,接下去想到的就是如何引用這些庫文件。最簡單的方式就是require或者include,但這就不夠高大上了啊,須要花時間去庫文件裏查看須要引入哪些文件,費事並且容易出錯。好在composer能夠幫咱們解決這個問題。那就是autoload。
在運行完composer install命令後,怎麼調用PHPExcel庫呢?很簡單,只要引入vendor目錄下的autoload.php文件就能夠了。能夠在根目錄下,建一個index.php文件,加入一下內容:
<!-- lang: php --> include 「vendor/autoload.php」 $excel = new PHPExcel(); var_dump($excel);
用瀏覽器訪問一下這個頁面,就會發現PHPExcel對象已經被成功建立啦,是否是很方便?
其實到目前爲止,咱們並沒用在composer.json文件里加入autoload字段,那麼何時須要加入呢? 那就是當咱們想讓composer幫咱們自動加載咱們本身定義的類的時候。例如,咱們本身寫了個訂單管理類,取名OrderManager,放在lib目錄下的OrderManager.php文件裏。內容以下:
<!-- lang: php --> class OrderManager { public function test() { echo "hello"; } }
那麼如何讓composer幫咱們自動加載這個類呢? 在composer.json里加入下面的內容:
<!-- lang: js --> 「autoload」:{ "files":["lib/OrderManager.php"] }
files鍵對應的值是一個數組,數組元素是文件的路徑,路徑是相對於應用的根目錄。加上上述內容後,運行命令:
<!-- lang: js --> composer dump-autoload
讓composer重建自動加載的信息,完成以後,就能夠在index.php裏調用OrderManager類啦。
經過文件引入的方法雖然直觀,可是很費勁,每一個文件都得引入一次,實在不是好的解決辦法。有沒有更好的辦法呢?嘗試將autoload的值改爲:
<!-- lang: js --> "classmap":["lib"]
再此運行composer dump-autoload,嘗試調用,依然可以成功建立OrderManager類。其實,classmap經過創建類到文件的對應關係,當程序須要OrderManager類時,compoer的自動加載類經過查找OrderManager類所在的文件,而後再將改文件include進來。所以,這又致使了一個問題,那就是每加一個新類,就須要運行一次composer dump-autoload來建立類到文件到對應關係,比files方法雖然好一點,可是仍是很不夠舒爽啊!因而,PSR-0出場了。先了解下什麼是PSR-0。
FIG組織制定的一組PHP相關規範,簡稱PSR,其中
PSR-0自動加載 PSR-1基本代碼規範 PSR-2代碼樣式 PSR-3日誌接口 PSR-4 自動加載
目前就這五個規範,乍一看,PSR-0和PSR-4是重複了,實際上,在功能上確實有所重複。區別在於PSR-4的規範比較乾淨,去除了兼容PHP 5.3之前版本的內容,有一點PSR-0升級版的感受。固然,PSR-4也不是要徹底替代PSR-0,而是在必要的時候補充PSR-0——固然,若是你願意,PSR-4也能夠替代PSR-0。PSR-4能夠和包括PSR-0在內的其餘自動加載機制共同使用。
PSR-0規範的具體內容見:https://github.com/hfcorriez/fig-standards/blob/zh_CN/%E6%8E%A5%E5%8F%97/PSR-0.md PSR-4規範的具體內容見:https://github.com/hfcorriez/fig-standards/blob/zh_CN/%E6%8E%A5%E5%8F%97/PSR-4-autoloader.md
簡而言之,就是但願經過一組約定的目錄,文件名,類名定義方式,來實現快速經過類查找到文件,而後包含進來,實現自動加載。 PSR-4和PSR-0最大的區別是對下劃線(underscore)的定義不一樣。PSR-4中,在類名中使用下劃線沒有任何特殊含義。而PSR-0則規定類名中的下劃線_會被轉化成目錄分隔符。
不論是PSR-0仍是PSR-4,都要求有個命名空間,因此咱們須要對OrderManager類進行一些小的修改,加上命名空間:
<!-- lang: php --> namespace SilkLib; class OrderManager { public function test() { echo "hello"; } }
同時,文件夾的結構也要修改爲:應用根目錄\lib\SilkLib\OrderManager.php
而後修改composer.json裏的autoload部分以下:
<!-- lang: js --> "autoload":{ "psr-0":{ "SilkLib":"lib/" } }
這裏須要注意的是,SlikLib是命名空間,lib是目錄名,他們的組合告訴composer,文件搜索是在:lib/SilkLib/ 目錄下,而不是在 SilkLib/lib 目錄下,這一點要特別注意,有點繞,容易弄錯。
若是咱們把命名空間改爲 Slik\lib, 相應的目錄結構要改爲:應用根目錄\lib\Silk\lib\OrderManager.php,autoload部分的寫法相應的也要改爲:
<!-- lang: js --> "autoload":{ "psr-0":{ "Silk\\lib":"lib/" } }
注意Silk\lib是雙斜杆。好了,那咱們試試再加一個類,而後不用運行composer dump-autoload命令,看看新類是否能加載上。在lib目錄下,新增一個ShipManager.php文件,內容以下:
<!-- lang: php --> namespace Silk\lib; class ShipManager { public function test() { echo 'hello ship class'; } }
嘗試在index.php文件中調用:
<!-- lang: php --> $orderMgr = new Silk\lib\OrderManager(); $orderMgr->test(); $shipMgr = new Silk\lib\ShipManager(); $shipMgr->test();
運行成功,說明使用psr-0規範進行自動加載,比classmap更加方便。下面試試psr-4方式,整理下目錄結構,改爲:應用根目錄\lib\OrderManager.php,修改命名空間爲Silk, 修改autoload部分爲:
<!-- lang: php --> "autoload":{ "psr-4":{ "Silk":"lib" } }
嘗試調用,發現報錯Fatal error: Uncaught exception 'InvalidArgumentException' with message 'A non-empty PSR-4 prefix must end with a namespace separator. 提示要加上分隔符,那就加上吧:
<!-- lang: js --> "autoload":{ "psr-4":{ "Silk\\":"lib" } }
再次composer dump-autoload,運行測試,OK經過!
掌握require和autoload部分,其實就算Compoer入門啦,在詳細的內容,能夠經過查看composer文檔來了解。Happy Coding!