本章將解釋包和庫的概念,什么樣的存儲庫是可用的,以及它們如何工作。
## 概述
在此之前,我們看到存在不同類型的資源庫,我們需要了解一些基本概念,以理解 Composer 是如何構建于其上的。
### 包
Composer 是一個依賴管理工具。它在本地安裝一些資源包。一個包本質上就是一個包含東西的目錄。通常情況下它存儲 PHP 代碼,但在理論上它可以是任何東西。并且它包含一個描述,其中有一個名稱和一個版本號,這個名稱和版本號用于識別該包。
事實上,在 composer 內部將每一個版本都視為一個單獨的包。盡管在你使用 composer 時這種區別無關緊要,但當你想改變它時,這就顯得至關重要。
除了名稱和版本號,還存放了有用的元數據。與安裝關系最密切的是 source 信息,它申明了在哪里可以獲得資源包的內容。包數據指向包內容,并有兩種指向方式:dist 和 source。
**Dist:**?dist 指向一個存檔,該存檔是對一個資源包的某個版本的數據進行的打包。通常是已經發行的穩定版本。
**Source:**?source 指向一個開發中的源。這通常是一個源代碼倉庫,例如 git。當你想要對下載下來的資源包進行修改時,可以這樣獲取。
你可以使用其中任意一個,或者同時使用。這取決于其它的一些因素,比如“user-supplied 選項”和“包的穩定性”,前者將會被優先考慮。
### 資源庫
一個資源庫是一個包的來源。它是一個 packages/versions 的列表。Composer 將查看所有你定義的 repositories 以找到你項目需要的資源包。
默認情況下已經將 Packagist.org 注冊到 Composer。你可以在?`composer.json`?中申明更多的資源庫,把它們加入你的項目中。
資源庫的定義僅可用于“root 包”,而在你依賴的包中定義的資源庫將不會被加載。如果你想了解其中的原因,請閱讀?[FAQ entry](http://docs.phpcomposer.com/faqs/why-can't-composer-load-repositories-recursively.html)。
## Types
### Composer
主資源庫的類型為?`composer`。它使用一個單一的?`packages.json`?文件,包含了所有的資源包元數據。
這也是 packagist.org 所使用的資源類型。要引用一個?`composer`?資源庫,只需要提供一個存放?`packages.json`?文件的?**目錄路徑**。比如要引用?`packagist.org`?下的?`/packages.json`,它的 URL 就應該是`packagist.org`。而?`example.org/packages.json`?的 URL 應該是?`example.org`。
#### packages
唯一必須的字段是?`packages`。它的 JSON 結構如下:
~~~
{
"packages": {
"vendor/package-name": {
"dev-master": { @composer.json },
"1.0.x-dev": { @composer.json },
"0.0.1": { @composer.json },
"1.0.0": { @composer.json }
}
}
}
~~~
`@composer.json`?標記將會從此包的指定版本中讀取?`composer.json`?的內容,其內至少應包含以下信息:
* name
* version
* dist or source
這是一個最簡單的包定義:
~~~
{
"name": "smarty/smarty",
"version": "3.1.7",
"dist": {
"url": "http://www.smarty.net/files/Smarty-3.1.7.zip",
"type": "zip"
}
}
~~~
它還可以包含任何在?[composer.json 架構](http://docs.phpcomposer.com/04-schema.html)?中介紹的字段。
#### notify-batch
`notify-batch`?字段允許你指定一個 URL,它將會在用戶安裝每一個包時被調用。該 URL 可以是(與其資源庫相同域名的)絕對路徑或者一個完整的 URL 地址。
例如使用下面的值:
~~~
{
"notify-batch": "/downloads/"
}
~~~
對于?`example.org/packages.json`?包含的?`monolog/monolog`?包,它將會發送一個?`POST`?請求到?`example.org/downloads/`,使用下面的 JSON request body:
~~~
{
"downloads": [
{"name": "monolog/monolog", "version": "1.2.1.0"},
]
}
~~~
`version`?字段將包含標準化的版本號。
`notify-batch`?字段是可選的。
#### includes
對于較大的資源庫,可以拆分?`packages.json`?為多個文件。`includes`?字段允許你引用這些額外的文件。
實例:
~~~
{
"includes": {
"packages-2011.json": {
"sha1": "525a85fb37edd1ad71040d429928c2c0edec9d17"
},
"packages-2012-01.json": {
"sha1": "897cde726f8a3918faf27c803b336da223d400dd"
},
"packages-2012-02.json": {
"sha1": "26f911ad717da26bbcac3f8f435280d13917efa5"
}
}
}
~~~
文件的 SHA-1 碼允許它被緩存,僅在 hash 值改變時重新請求。
此字段是可選的。你也許并不需要它來自定義存儲庫。
#### provider-includes and providers-url
的對于非常大的資源庫,像 packagist.org 使用 so-called provider 文件是首選方法。`provider-includes`字段允許你設置一個列表,來申明這個資源庫提供的包名稱。在這種情況下文件的哈希算法必須使用 sha256。
`providers-url`?描述了如何在服務器上找到這些 provider 文件。它是以資源庫的根目錄為起點的絕對路徑。
實例:
~~~
{
"provider-includes": {
"providers-a.json": {
"sha256": "f5b4bc0b354108ef08614e569c1ed01a2782e67641744864a74e788982886f4c"
},
"providers-b.json": {
"sha256": "b38372163fac0573053536f5b8ef11b86f804ea8b016d239e706191203f6efac"
}
},
"providers-url": "/p/%package%$%hash%.json"
}
~~~
這些文件包含資源包的名稱以及哈希值,以驗證文件的完整性,例如:
~~~
{
"providers": {
"acme/foo": {
"sha256": "38968de1305c2e17f4de33aea164515bc787c42c7e2d6e25948539a14268bb82"
},
"acme/bar": {
"sha256": "4dd24c930bd6e1103251306d6336ac813b563a220d9ca14f4743c032fb047233"
}
}
}
~~~
上述文件申明了?`acme/foo`?和?`acme/bar`?可以在這個資源庫找到,通過加載由?`providers-url`?引用的文件,替換?`%package%`?為包名并且替換?`%hash%`?為 sha256 的值。這些文件本身只包含上文提到的?[packages](http://docs.phpcomposer.com/05-repositories.html#packages)的定義。
這些字段是可選的。你也許并不需要它們來自定義存儲庫。
#### stream options
`packages.json`?文件是用一個 PHP 流加載的。你可以使用?`options`?參數來設定額外的流信息。你可以設置任何有效的PHP 流上下文選項。更多相關信息請查看?[Context options and parameters](http://php.net/manual/en/context.php)。
### VCS
VCS 表示版本控制系統。這包括像 git、svn 或 hg 這樣的版本管理系統。Composer 有一個資源類型可以從這些系統安裝軟件包。
#### 從 VCS 資源庫加載一個包
這里有幾個用例。最常見的是維護自己 fork 的第三方庫。如果你在項目中使用某些庫,并且你決定改變這些庫內的某些東西,你會希望你項目中使用的是你自己的修正版本。如果這個庫是在 GitHub 上(這種情況經常出現),你可以簡單的 fork 它并 push 你的變更到這個 fork 里。在這之后你更新項目的?`composer.json`?文件,添加你的 fork 作為一個資源庫,變更版本約束來指向你的自定義分支。關于版本約束的命名約定請查看?[庫(資源包)](http://docs.phpcomposer.com/02-libraries.html)。
例如,假設你 fork 了 monolog,在?`bugfix`?分支修復了一個 bug:
~~~
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/igorw/monolog"
}
],
"require": {
"monolog/monolog": "dev-bugfix"
}
}
~~~
當你運行?`php composer.phar update`?時,你應該得到你修改的版本,而不是 packagist.org 上的?`monolog/monolog`。
注意,你不應該對包進行重命名,除非你真的打算擺脫原來的包,并長期的使用你自己的 fork。這樣 Composer 就會正確獲取你的包了。如果你確定要重命名這個包,你應該在默認分支(通常是 master 分支)上操作,而不是特性分支,因為包的名字取自默認分支。
如果其它包依賴你 fork 的這個分支,可能要對它做版本號的行內別名設置,才能夠準確的識別版本約束。更多相關信息請查看?[別名](http://docs.phpcomposer.com/articles/aliases.html)。
#### 使用私有資源庫
完全相同的解決方案,也可以讓你使用你 GitHub 和 BitBucket 上的私人代碼庫進行工作:
~~~
{
"require": {
"vendor/my-private-repo": "dev-master"
},
"repositories": [
{
"type": "vcs",
"url": "git@bitbucket.org:vendor/my-private-repo.git"
}
]
}
~~~
唯一的要求是為一個 git 客戶端安裝 SSH 秘鑰。
#### Git 的備選方案
Git 并不是 VCS 資源庫唯一支持的版本管理系統。
以下幾種都是被支持的:
* **Git:**?[git-scm.com](http://git-scm.com/)
* **Subversion:**?[subversion.apache.org](http://subversion.apache.org/)
* **Mercurial:**?[mercurial.selenic.com](http://mercurial.selenic.com/)
為了從這些系統獲取資源包,你必須安裝對應的客戶端,這可能是不方便的。基于這個原因,這里提供了 GitHub 和 BitBucket 的 API 的特殊支持,以便在無需安裝版本控制系統的情況下獲取資源包。在 VCS 資源庫提供的?`dist`?中獲取 zip 存檔。
* **GitHub:**?[github.com](https://github.com/)?(Git)
* **BitBucket:**?[bitbucket.org](https://bitbucket.org/)?(Git and Mercurial)
VCS 驅動將基于 URL 自動檢測版本庫類型。但如果可能,你需要明確的指定一個?`git`、`svn`?或?`hg`?作為資源庫類型,而不是?`vcs`。
If you set the?`no-api`?key to?`true`?on a github repository it will clone the repository as it would with any other git repository instead of using the GitHub API. But unlike using the?`git`driver directly, composer will still attempt to use github's zip files.
#### Subversion 選項
由于 Subversion 沒有原生的分支和標簽的概念,Composer 假設在默認情況下該代碼位于?`$url/trunk`、`$url/branches`?和?`$url/tags`?內。如果你的存儲庫使用了不同的布局,你可以更改這些值。例如,如果你使用大寫的名稱,你可以像這樣配置資源庫:
~~~
{
"repositories": [
{
"type": "vcs",
"url": "http://svn.example.org/projectA/",
"trunk-path": "Trunk",
"branches-path": "Branches",
"tags-path": "Tags"
}
]
}
~~~
如果你的存儲庫目錄中沒有任何分支或標簽文件夾,你可以將?`branches-path`?或?`tags-path`?設置為?`false`。
如果是一個位于子目錄的包,例如,?`/trunk/foo/bar/composer.json`?和?`/tags/1.0/foo/bar/composer.json`,那么你可以讓 composer 通過?`"package-path"`?選項設置的子目錄進行訪問,在這個例子中可以將其設置為?`"package-path": "foo/bar/"`。
### PEAR
`pear`?類型資源庫,使得從任何 PEAR 渠道安裝資源包成為可能。Composer 將為所有此類型的包增加前綴(類似于?`pear-{渠道名稱}/`)以避免沖突。而在之后使用別名時也增加前綴(如?`pear-{渠道別名}/`)。
例如使用?`pear2.php.net`:
~~~
{
"repositories": [
{
"type": "pear",
"url": "http://pear2.php.net"
}
],
"require": {
"pear-pear2.php.net/PEAR2_Text_Markdown": "*",
"pear-pear2/PEAR2_HTTP_Request": "*"
}
}
~~~
在這種情況下渠道的簡稱(別名)是?`pear2`,因此?`PEAR2_HTTP_Request`?包的名稱應該寫作?`pear-pear2/PEAR2_HTTP_Request`。
> **注意:**?`pear`?類型的資源庫對每個 requires 都要做完整的請求,因此可能大大降低安裝速度。
#### 自定義供應商別名
通過自定義供應商名稱,對 PEAR 渠道包進行別名是允許的。
例:
假設你有一個私人 PEAR 庫,并希望使用 Composer 從 VCS 集成依賴。你的 PEAR 庫包含以下資源包:
* `BasePackage`。
* `IntermediatePackage`?依賴于?`BasePackage`。
* `TopLevelPackage1`?和?`TopLevelPackage2`?都依賴于?`IntermediatePackage`。
如果沒有一個供應商別名,Composer 將使用 PEAR 渠道名稱作為包名的一部分:
* `pear-pear.foobar.repo/BasePackage`
* `pear-pear.foobar.repo/IntermediatePackage`
* `pear-pear.foobar.repo/TopLevelPackage1`
* `pear-pear.foobar.repo/TopLevelPackage2`
假設之后的某個時間,你希望將你的 PEAR 包遷移,使用 Composer 資源庫和命名方案,并且采用?`foobar`?作為供應商名稱。這樣之前使用 PEAR 包的項目將不會看到更新的資源包,因為它們有不同的供應商名稱(`foobar/IntermediatePackage`?與?`pear-pear.foobar.repo/IntermediatePackage`)。
你可以通過從一開始就為 PEAR 資源庫指定?`vendor-alias`?來避免這種情況的發生,以得到一個不會過時的包名。
為了說明這一點,下面的例子會從你的 PEAR 資源庫中得到?`BasePackage`、`TopLevelPackage1`?和?`TopLevelPackage2`?資源包,并從 Github 資源庫中獲取?`IntermediatePackage`?資源包:
~~~
{
"repositories": [
{
"type": "git",
"url": "https://github.com/foobar/intermediate.git"
},
{
"type": "pear",
"url": "http://pear.foobar.repo",
"vendor-alias": "foobar"
}
],
"require": {
"foobar/TopLevelPackage1": "*",
"foobar/TopLevelPackage2": "*"
}
}
~~~
### Package
如果你想使用一個項目,它無法通過上述任何一種方式支持 composer,你仍然可以使用?`package`?類型定義資源庫。
基本上,你可以定義與?`packages.json`?中?`composer`?類型資源庫相同的信息,但需要為每個這樣的資源包分別定義。同樣,至少應該包含以下信息:`name`、`version`、(`dist`?或?`source`)。
這是一個 smarty 模板引擎的例子:
~~~
{
"repositories": [
{
"type": "package",
"package": {
"name": "smarty/smarty",
"version": "3.1.7",
"dist": {
"url": "http://www.smarty.net/files/Smarty-3.1.7.zip",
"type": "zip"
},
"source": {
"url": "http://smarty-php.googlecode.com/svn/",
"type": "svn",
"reference": "tags/Smarty_3_1_7/distribution/"
},
"autoload": {
"classmap": ["libs/"]
}
}
}
],
"require": {
"smarty/smarty": "3.1.*"
}
}
~~~
通常你不需要去定義?`source`,因為你并不是真的需要它。
> **注意:**?該資源庫類型存在以下限制,因此應盡可能避免使用:
>
> * Composer 將不會更新資源包,除非你修改了?`version`?字段。
> * Composer 將不會更新 commit references,因此如果你使用?`master`?reference,將不得不刪除該程序包以強制更新,并且將不得不面對一個不穩定的 lock 文件。
## Hosting your own
盡管大部分的時間,你大概都會把資源包放在 packagist.org 上,但這里還將告訴你一些用例,以便你可以自行托管資源庫。
* **Private company packages:**?如果你是一個公司的職員,對公司內部的資源包使用 composer,你可能會想讓這些包保持私有的狀態。
* **Separate ecosystem:**?如果你的項目有自己的生態系統,并且自己的資源包不需要被其它項目所復用,你可能會想將它們從 packagist.org 上分離出來。其中一個例子就是 wordpress 的插件。
對于自行托管的軟件包,建議使用?`composer`?類型資源庫設置,它將提供最佳的性能。
這里有一些工具,可以幫助你創建?`composer`?類型的資源庫。
### Packagist
packagist 的底層是開源的。這意味著你可以只安裝你自己拷貝的 packagist,改造并使用它。這真的是很直接簡單的事情。然而,由于其規模和復雜性,對于大多數中小型企業還是建議使用 Satis。
Packagist 是一個 Symfony2 應用程序,并且托管在 GitHub 上?[github.com/composer/packagist](https://github.com/composer/packagist)。它內部使用了 composer 并作為 VCS 資源庫與 composer 用戶之間的代理。它擁有所有 VCS 資源包的列表,定期重新抓取它們,并將其作為一個 composer 資源庫。
要設置你的副本,只需要按照?[github.com/composer/packagist](https://github.com/composer/packagist)?的說明進行操作。
### Satis
Satis 是一個靜態的?`composer`?資源庫生成器。它像是一個超輕量級的、基于靜態文件的 packagist 版本。
你給它一個包含?`composer.json`?的存儲庫,定義好 VCS 和 資源庫。它會獲取所有你列出的包,并打印?`packages.json`?文件,作為?`composer`?類型的資源庫。
更多詳細信息請查看?[github.com/composer/satis](https://github.com/composer/satis)?和?[Satis article](http://docs.phpcomposer.com/articles/handling-private-packages-with-satis.html)。
### Artifact
在某些情況下,或許沒有能力擁有之前提到的任何一種線上資源庫。Typical example could be cross-organisation library exchange through built artifacts。當然大部分的時間他們都是私有的。為了簡化維護,可以簡單的使用?`artifact`?資源庫類型,來引用一個包含那些私有包的 ZIP 存檔的文件夾:
~~~
{
"repositories": [
{
"type": "artifact",
"url": "path/to/directory/with/zips/"
}
],
"require": {
"private-vendor-one/core": "15.6.2",
"private-vendor-two/connectivity": "*",
"acme-corp/parser": "10.3.5"
}
}
~~~
每個 zip artifact 都只是一個 ZIP 存檔,放置在?`composer.json`?所在的根目錄:
~~~
unzip -l acme-corp-parser-10.3.5.zip
composer.json
...
~~~
如果有兩個不同版本的資源包,它們都會被導入。當有一個新版本的存檔被添加到 artifact 文件夾,并且你運行了?`update`?命令,該版本就會被導入,并且 Composer 將更新到最新版本。
## 禁用 Packagist
你可以在?`composer.json`?中禁用默認的 Packagist 資源庫。
~~~
{
"repositories": [
{
"packagist": false
}
]
}
~~~