你必須知道的 17 個 Composer 最佳實踐(已更新至 22 個)
這是一篇社群協同翻譯的文章,已完成翻譯,更多資訊請點選 協同翻譯介紹 。
儘管大多數 PHP 開發人員都知道如何使用 Composer
,但並不是所有的人都在有效地或以最好的方式使用它。 所以我決定總結一些對我日常工作流程很重要的東西。 大部分技巧的理念是_「 Play it safe 」_,這意味著如果有更多的方法來處理某些事情,我會使用最不容易出錯的方法。
Tip 1: 閱讀文件
我是認真的。 官方的文件 寫得非常棒,現在只需幾個小時的閱讀,會給你未來節省很多時間。你會驚訝於 Composer 如此之多能。
Tip 2: 認識 "專案" 和 "庫" 間的不同
建立的是“專案”還是“庫”,意識到這點非常重要。這兩者在使用過程中,都存在非常巨大的差異。
庫是一個可重用的包,需要作為一個依賴項進行新增 - 比如 symfony/symfony
, doctrine/orm
或 elasticsearch/elasticsearch
.
而典型的專案是一個應用程式,要依賴於多個庫。它通常不可重用(其他專案不需要它成為一個依賴項)。像電子商務網站、客戶服務系統等型別的應用就是典型的例子。
在下面的 Tip 中,我會更仔細地講解庫和專案兩者的區別。
Tip 3: 為應用程式使用指定的依賴版本
建立應用程式時,應使用最清晰的版本號定義依賴項。 如果需要解析 YAML 檔案,就應該以 "symfony/yaml": "4.0.2"
這樣的形式明確依賴項。 即使依賴的庫遵循了 "symfony/symfony": "^3.1"
,有可能存在在 3.2 版本廢棄的東西,而這會破壞你的應用程式在該版本下通過測試。或者可能在 PHP_CodeSniffer 中存在一個已修復的 bug ,程式碼就會檢測出新的格式問題,這會再次導致錯誤的構建。
依賴的升級要慎之又慎,不能撞大運。下面 Tip 當中會有一條對此進行更詳細的講解。
聽起來有些危言聳聽,但是注意這個要點就會避免你的合作伙伴向專案中在新增新庫時不小心更新了所有依賴(程式碼審查時可能忽略這一點)。
Tip 4: 對庫依賴項使用版本範圍
建立庫時,應儘可能定義最大的可用版本範圍。比如建立了一個庫,要使用 symfony/yaml
"symfony/yaml": "^3.0 || ^4.0"
這表示該庫能從 Symfony 3.x 或 4.x 中任意版本中使用 `symfony/yaml` 。這相當重要,因為這個版本約束會傳遞給使用該庫的應用程式。 萬一有兩個庫的請求存在衝突,比如一個要 `~3.1.0` ,另一個需要 `~3.2.0` ,則安裝會失敗。 [![image](https://upload-images.jianshu.io/upload_images/27242968-98ca24f983e8ace0.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Fusers%2F12155 "https://laravel-china.org/users/12155")bigqiang 翻譯於 1周前 [0](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2F7609%2Fsections%2F153%2Fvote "https://laravel-china.org/translations/7609/sections/153/vote") [重譯](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2Fsections%2F153 "https://laravel-china.org/translations/sections/153")
# Tip 5: 開發應用程式要提交 `composer.lock` 檔案到 git 版本庫中
----------------------------------------------
建立了 _一個專案_,一定要把 `composer.lock` 檔案提交到 git 中。 這會確保每一個人——你、你的合作伙伴、你的 CI 伺服器以及你的產品伺服器——所執行的應用程式擁有相同依賴的版本。 乍一看有些畫蛇添足,在 Tip #3 中已經提過要使用明確的版本號的約束了啊。這並不多餘,要知道你使用的依賴項的依賴項並不受這些約束繫結(如 `symfony/console` 還依賴 `symfony/polyfill-mbstring`)。如果不提交 `composer.lock` 檔案,就不會獲取到相同版本的依賴集合。 [![image](https://upload-images.jianshu.io/upload_images/27242968-085517b8e2d0e050.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Fusers%2F12155 "https://laravel-china.org/users/12155")bigqiang 翻譯於 1周前 [0](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2F7609%2Fsections%2F154%2Fvote "https://laravel-china.org/translations/7609/sections/154/vote") [重譯](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2Fsections%2F154 "https://laravel-china.org/translations/sections/154")
Tip #6: 開發庫要把 `composer.lock` 檔案新增到 `.gitignore` 檔案中
----------------------------------------------------
建立 _一個庫_ (比如說叫 `acme/my-library`), 這就不應該把 `composer.lock` 檔案提交到 git 庫中了。該檔案對使用該庫的專案 It [不會有任何影響](https://link.juejin.cn/?target=https%3A%2F%2Fgetcomposer.org%2Fdoc%2F02-libraries.md%23lock-file "https://getcomposer.org/doc/02-libraries.md#lock-file") 。 假設 `acme/my-library` 使用 `monolog/monolog` 作依賴項。你已經在版本庫中提交了 `composer.lock`,開發 `acme/my-library` 的每個人都可能在使用 Monolog 的老舊版本。該庫開發完成後,在實際專案中使用該庫,就可能存在安裝的 Monolog 是一個新版本 , 而此時就會和該庫存在不相容。可是你在之前根本就不會注意到相容問題就因為這個 `composer.lock`!
因此,最佳處理方式就是把 `composer.lock` 新增到 `.gitignore` 檔案中,這樣就避免了不小心提交它到版本庫中引發的問題。
如果還想確保該庫與它的依賴項的不同版本保持相容性,那繼續閱讀下一個 Tip !
[![image](https://upload-images.jianshu.io/upload_images/27242968-64a472f50df347eb.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Fusers%2F12155 "https://laravel-china.org/users/12155")
# Tip 7: Travis CI 構建依賴項的不同版本
----------------------------
> 當前 Tip 僅適合庫(對於應用程式要指明具體的版本號)。
如果你在構建開源的庫,很有可能你會使用 Travis CI 來跑構建過程。
預設情況下,在 `composer.json` 檔案約束允許的條件下,composer 安裝會安裝依賴的最新可能版本。這就意味著對於 `^3.0 || ^4.0` 這樣的依賴約束,構建安裝總是使用最新的 v4 版本發行包。 而 3.0 版本根本不會測試,所構建的庫就可能與該版本不相容,你的使用者要哭了。 幸好,composer 為安裝低版本依賴項提供了一個開關 `--prefer-lowest` (應使用 `--prefer-stable` ,可阻止不穩定版本的安裝)。 已上傳的 `.travis.yml` 配置類似下面的格式:```
language: php
php:
- 7.1
- 7.2
env:
matrix:
- PREFER_LOWEST="--prefer-lowest --prefer-stable"
- PREFER_LOWEST=""
before_script:
- composer update $PREFER_LOWEST
script:
- composer ci
程式碼詳見 my mhujer/fio-api-php library 及 the build matrix on Travis CI
雖然這解決了多數的不相容問題,不過仍然要記得,依賴項的最低和最高版本間有太多的組合。他們仍舊可能存在不相容的情況。
Tip 8: 按名稱對 require 和 require-dev 中的包排序
按名稱對 require
及 require-dev
中的包排序是非常好的實踐。這在衍合一個分支時可以避免不必要的合併衝突。假如你把一個包新增到兩個分支檔案中的列表末尾,那每次合併都可能遇到衝突。 手動進行包排序的話會很乏味,所以最好辦法就是在 composer.json
中 配置一下 即可:```
{
...
"config": {
"sort-packages": true
},
...
}
以後再要 `require` 一個新的包,它會自動新增到一個正確位置(不會跑到尾部)。
[![image](https://upload-images.jianshu.io/upload_images/27242968-61c883a85581fef9.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Fusers%2F12155 "https://laravel-china.org/users/12155")bigqiang 翻譯於 1周前 [0](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2F7609%2Fsections%2F157%2Fvote "https://laravel-china.org/translations/7609/sections/157/vote") [重譯](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2Fsections%2F157 "https://laravel-china.org/translations/sections/157")
# Tip 9: 進行版本衍合或合併時不要合併 `composer.lock`
---------------------------------------
如果你在 `composer.json` (和 `composer.lock`)中添加了一個新依賴項,並且在該分支被合併前主分支中新增另一個依賴項,此時就需要對你的分支進行衍合處理。那麼 `composer.lock` 檔案就會得到一個合併衝突。 千萬別試圖手動解決衝突,這是因為 `composer.lock` 檔案包含了定義 `composer.json` 中依賴項的雜湊值。所以即使你解決了衝突,這個最終合併結果的lock檔案仍是錯誤的。 最佳方案應該這樣做,用下面一行程式碼在專案根目錄建立一個 `.gitattributes` 檔案,它會告訴 git 不要試圖對 `composer.lock` 檔案進行合併操作:```
/composer.lock -merge
推薦 Trunk Based Development 方式(常用佳品,不會有錯),使用臨時的特性分支糾正這種問題。當你有個臨時分支需要即時合併時,因此導致的 composer.lock
檔案合併衝突的風險極小。你甚至可以僅僅為新增一個依賴項而建立分支,然後馬上進行合併。
假如在衍合過程中 composer.lock
遇到合併衝突又當如何呢? 使用主分支版本解決,這樣僅僅修改 composer.json
檔案即可(新增一個包)。然後執行 composer update --lock
,就會把 composer.json
檔案的修改更新到 composer.lock
檔案中。現在把已經更新的 composer.lock
檔案提交到版本暫存區,然後繼續衍合操作。
Tip 10:瞭解 require
和 require-dev
之間的區別
能夠意識到require
和require-dev
模組之間的區別是非常重要的。 需要執行在應用中或者庫中的包都應該被定義在 require
(例如: Symfony, Doctrine, Twig, Guzzle, ...)中。如果你正在建立一個庫, 注意將什麼內容定義為 require
。因為這個部分的 每個依賴項同時也是使用了該庫的應用的依賴。
開發應用程式(或庫)所需的包應該定義在require-dev
(例如:PHPUnit, PHP_CodeSniffer, PHPStan)中。
Tip 11: 安全地升級依賴項
我想大家對如下事實存有共識:應該定期對依賴項升級。 此處我想討論的是依賴項的升級應該放在明處且慎之又慎,而不能是因其他活計的需要才順手為之。如果在重構應用的同時又升級了庫,那麼就很難區分應用崩潰的原因是重構還是升級帶來的。
可用 composer outdated
命令檢視哪些依賴項需要升級。追加一個 --direct
(或 -D
)引數開關是個聰明之舉,這隻會檢視 composer.json
指定的依賴項。還有一個 -m
引數開關,只檢視次版本號的升級列表。
對每一個老版本的依賴項進行升級都要尊循如下步驟:
- 建立新分支
- 在
composer.json
檔案中更新該依賴項版本到最新版本號 - 執行
composer update phpunit/phpunit --with-dependencies
(使用升級過的庫替換phpunit/phpunit
) - 檢查 Github 上庫的版本庫中 CHANGELOG 檔案,檢查是否存在重大變化。 如果存在就升級應用程式
- 本地測試應用程式(使用 Symfony 的話還能在除錯欄看到棄用警告)
- 提交修改(包括
composer.json
、composer.lock
及其他新版本正常執行所做的必要修改) - 等 CI 構建結束
- 合併然後部署
有時需要一次升級多個依賴項,比如升級 Doctrine 或 Symfony。這種情況下,就要在升級命令中把他們全部羅列出來:
composer update symfony/symfony symfony/monolog-bundle --with-dependencies
或者使用萬用字元升級所有指定名稱空間的依賴:
composer update symfony/* --with-dependencies
這全都是很乏味的工作,但相對於不小心升級依賴項而言,這提供了額外保障。
一個可接受的簡捷方式就是一次升級所有 require-dev
中的依賴項(如果程式程式碼沒有修改的話,否則還是建議建立獨立分支以便程式碼審查)。
Tip 12: 在 composer.json
中定義其他型別的依賴
除了定義庫作為依賴項外,也以在這兒定義其他東西。
可以定義應用程式和庫所支援的 PHP 版本:
"require": {
"php": "7.1.* || 7.2.*",
},
也能定義應用程式和庫所需要的擴充套件。在嘗試 docker 化自己的應用時,或是你的同伴頭一次設定應用環境時,這招超級實用。
"require": {
"ext-mbstring": "*",
"ext-pdo_mysql": "*",
},
(當 擴充套件版本不一致 時,版本號要用 *
)。
Tip 13: 在CI構建期間驗證 composer.json
composer.json
和 composer.lock
應當一直保持同步. 因此, 一直為他們保持自動核對是一個好主意. 將此新增成為你的構建指令碼的一部分將會確保 composer.lock
與 composer.json
保持同步:
composer validate --no-check-all --strict
Tip 14: 在 PHPStorm 中使用 Composer 外掛
這裡有一個 composer.json plugin for PHPStorm. 當手動修改 composer.json
時,外掛會自動完成及執行一些驗證.
如果你在使用其他 IDE (或者只是一個編輯器), 你可以使用 its JSON schema 設定驗證.
Tip 15: 在 composer.json
中指明生產環境的PHP版本號
如果你和我一樣,有時還 在本地環境跑PHP最新預釋版本, 那麼就會處於升級依賴項的版本不能運行於生產環境的風險。現在我就在使用 PHP 7.2.0 ,也就意味著我安裝的庫可能在 7.1 版本中執行不了。如果生產環境跑的是 7.1 版本,安裝就會失敗。 不過不用擔心,有個非常簡單的解決辦法,在 composer.json
檔案的config
部分指明生產環境的 PHP 版本號即可:
"config": {
"platform": {
"php": "7.1"
}
}
別把它和 require
部分的設定搞混了,它的作用不同。你的應用就可以執行 7.1 或 7.2 版本下,而且同時指定了平臺版本為 7.1 (這意味著依賴項的升級版本要和 平臺版本 7.1 保持相容):
"require": {
"php": "7.1.* || 7.2.*"
},
"config": {
"platform": {
"php": "7.1"
}
}
Tip 16: 使用自有託管 Gitlab 上的私有包
推薦使用 vcs
作為版本庫型別,並且 Composer 決定獲取包的合適的方法。比如,從Github上新增一個 fork,使用它的 API 下載整個版本庫的 .zip 檔案,而不用克隆。 不過對一個私有的 Gitlab 安裝來講會更復雜。如果用 vcs
作版本庫型別,Composer 會檢測到它是個 Gitlab 型別的安裝,會嘗試使用 API 下載包(這要求有 API key。我不想設定,所以我只用 SSH 克隆安裝了) : 首先指明版本庫型別是 git
:
"repositories": [
{
"type": "git",
"url": "[email protected]:package-namespace/package-name.git"
}
]
然後指明常用的包:
"require": {
"package-namespace/package-name": "1.0.0"
}
[](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Fusers%2F12155 "https://laravel-china.org/users/12155"
Tip 17: 臨時使用 fork 下 bug 修復分支的方法
如果在某個公共的庫中找到一個 bug,並且在Github上自己的 fork 中修復了它, 這就需要從自己的版本庫裡安裝這個庫,而不是官方版本庫(要到修復合併且修復的版本釋出才行)。
使用 內嵌別名 可輕鬆搞定:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/you/monolog"
}
],
"require": {
"symfony/monolog-bundle": "2.0",
"monolog/monolog": "dev-bugfix as 1.0.x-dev"
}
}
可以通過 設定 path
作為版本庫型別 在本地測試這次修復,然後再 push 更新版本庫。
Tip 18:使用 prestissimo 加速你的包安裝
Composer 有個 hirak/prestissimo 外掛,通過該外掛能夠以並行的方式進行下載,從而提高依賴包的安裝速度。
那麼,這麼好的東西,你現在該如何做?你僅僅需要馬上全域性安裝這個外掛,然後就可以自動地在所有專案中使用。
composer global require hirak/prestissimo
Tip 19: 當你不確定時,測試你的版本約束
即使在閱讀 the documentation 之後,書寫正確的版本約束在一些時候也是很棘手的. 幸運的是, 這裡有 Packagist Semver Checker 可以用來檢查哪個本部匹配特定的約束. 他不是僅僅的分析版本約束, 他從 Packagist
下載資料以來展示實際的釋出版本. 檢視 the result for symfony/symfony:^3.1
.
Tip 20: 在生產環境中使用使用權威類對映檔案
應該在生產環境中 生成權威類對映檔案 。這會讓類對映檔案中包含的所有類快速載入,而不必到磁碟檔案系統進行任何檢查。
可以在生產環境構建時執行以下命令:
composer dump-autoload --classmap-authoritative
Tip 21: 為測試配置 autoload-dev
你也不想在生產環境中載入測試檔案(考慮到測試檔案的大小和記憶體使用)。這可以通過配置 autoload-dev
解決(與 autoload
相似):
"autoload": {
"psr-4": {
"Acme\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Acme\\": "tests/"
}
},
Tip 22: 嘗試 Composer 指令碼
Composer
指令碼是一個建立構建指令碼的輕量級工具。關於這個,我有另文述及。
總結
如果你不同意某些觀點且闡述出你為什麼不同意的意見(不要忘記標註 tip
的編號)我將很高興。
本文中的所有譯文僅用於學習和交流目的,轉載請務必註明文章譯者、出處、和本文連結
我們的翻譯工作遵照 CC 協議,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
文章轉自 https://juejin.cn/post/6844903558433734663 如有侵權,請聯絡刪除。