尽管大多数 PHP 开发人员都知道如何使用 Composer,但并不是所有的人都在有效的或以最好的方式来使用它。 所以我决定总结一些在我日常工作流程很重要的东西。
大多数技巧的哲学是 “稳,不冒险”,这意味着如果有更多的方法来处理某些事情,我会使用最有把握不容易出错的方法。
Tip #1: 阅读文档
我是真心这样认为的. 文档 是一个非常有用的东西并且在长远看来几个小时的阅读将会为您节省更多的时间。你会惊讶原来 composer 可以做这么多事情.
Tip #2: 注意 项目(Project) 和 库(Libary) 之间的区别
无论你是创建 项目 还是 库 , 了解这一点非常重要,它们每一个都需要单独的一套做法。
一个库是一个可重用的包,你可以添加一个依赖
比如: symfony / symfony,doctrine / orm 或者 elasticsearch/elasticsearch.
一个项目通常是一个应用程序,依赖于几个库。它通常是不可重用的(没有其它项目会要求它作为依赖。典型的例子是电子商务网站,客户支持系统等)。
我将在下面的提示中区分库和项目。
Tip #3: 使用特定的依赖关系 #关于应用程序的版本
如果你正在创建一个应用程序,您应该使用最具体的版本来定义依赖项,假如你需要解析 YAML 文件,你应该指定这样的依赖版本 “symfony / YAML”:“4.0.2”。
即使你遵循了库的依赖版本控制,在次要版本和补丁版本中也有可能中断向后兼容性。例如,如果你使用的是 “symfony / Symfony”:“^ 3.1”, 会有 3.2 的版本有可能会破坏你的应用程序测试用列。或者可能新版本有 bug 没有修正,那么 php_codesniffer 检测你的代码格式的时候会导致新问题,这在很大程度上可能会破坏一个应用的构建。
依赖关系的更新应该是深思熟虑的,而不是偶然的。其中的一个技巧更详细地讨论了它。
Tip #4: 对库依赖项使用版本范围
创建库时,应尽可能定义最大的可用版本范围。比如创建了一个库,要使用symfony/yaml 库进行 YAML 解析,就应这样写:
"symfony/yaml": "^3.0 || ^4.0"
这表示该库能从 Symfony 3.x 或 4.x 中任意版本中使用 symfony/yaml 。这相当重要,因为这个版本约束会传递给使用该库的应用程序。
万一有两个库的请求存在冲突,比如一个要 ~3.1.0
,另一个需要 ~3.2.0
,则安装会失败。
Tip #5: 开发应用程序要提交 composer.lock 文件到 git 版本库中
创建了 一个项目,一定要把 composer.lock 文件提交到 git 中。 这会确保每一个人 —— 你、你的合作伙伴、你的 CI 服务器以及你的产品服务器 —— 所运行的应用程序拥有相同依赖的版本。
乍一看有些画蛇添足,在 Tip #3 中已经提过要使用明确的版本号的约束了啊。这并不多余,要知道你使用的依赖项的依赖项并不受这些约束绑定(如 symfony/console 还依赖 symfony/polyfill-mbstring)。如果不提交 composer.lock 文件,就不会获取到相同版本的依赖集合。
Tip #6: 开发库要把 composer.lock
文件添加到 .gitignore 文件中
创建 一个库 (比如说叫 acme/my-library), 这就不应该把 composer.lock 文件提交到 git 库中了。该文件对使用该库的项目 It 不会有任何影响 。
假设 acme/my-library 使用 monolog/monolog 作依赖项。你已经在版本库中提交了 composer.lock,开发 acme/my-library 的每个人都可能在使用 Monolog 的老旧版本。该库开发完成后,在实际项目中使用该库,就可能存在安装的 Monolog 是一个新版本 , 而此时就会和该库存在不兼容。可是你在之前根本就不会注意到兼容问题就因为这个 composer.lock!
因此,最佳处理方式就是把 composer.lock 添加到 .gitignore 文件中,这样就避免了不小心提交它到版本库中引发的问题。
如果还想确保该库与它的依赖项的不同版本保持兼容性,那继续阅读下一个 Tip !
Tip #7: 按名称对 require 和 require-dev 中的包排序
按名称对 require 及 require-dev 中的包排序是非常好的实践。这在衍合一个分支时可以避免不必要的合并冲突。假如你把一个包添加到两个分支文件中的列表末尾,那每次合并都可能遇到冲突。
手动进行包排序的话会很乏味,所以最好办法就是在 composer.json 中 配置一下 即可:
{
...
"config": {
"sort-packages": true
},
...
}
以后再要 require 一个新的包,它会自动添加到一个正确位置(不会跑到尾部)。
Tip #8: 进行版本衍合或合并时不要合并 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 #9: 了解require 和 require-dev 之间的区别
能够意识到 require 和 require-dev 模块之间的区别是非常重要的。
需要运行在应用中或者库中的包都应该被定义在 require (例如: Symfony, Doctrine, Twig, Guzzle, ...) 中。如果你正在创建一个库, 注意将什么内容定义为 require。因为这个部分的 每个依赖项同时也是使用了该库的应用的依赖。
开发应用程序 (或库) 所需的包应该定义在 require-dev (例如:PHPUnit, PHP_CodeSniffer, PHPStan) 中。
Tip #10: 安全地升级依赖项
我想大家对如下事实存有共识:应该定期对依赖项升级。 此处我想讨论的是依赖项的升级应该放在明处且慎之又慎,而不能是因其他活计的需要才顺手为之。如果在重构应用的同时又升级了库,那么就很难区分应用崩溃的原因是重构还是升级带来的。
可用 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 中的依赖项(如果程序代码没有修改的话,否则还是建议创建独立分支以便代码审查)。
参考链接:https://learnku.com/php/t/7609/you-have-to-know-17-composer-best-practices-updated-to-22