【转载】你必须知道的 17 个 Composer 最佳实践(已更新至 22 个)


尽管大多数 PHP 开发人员都知道如何使用 Composer ,但并不是所有的人都在有效地或以最好的方式使用它。 所以我决定总结一些对我日常工作流程很重要的东西。
大部分技巧的理念是「 Play it safe 」,这意味着如果有更多的方法来处理某些事情,我会使用最不容易出错的方法。

Tip #1: 阅读文档

我是认真的。 官方的文档 写得非常棒,现在只需几个小时的阅读,会给你未来节省很多时间。你会惊讶于 Composer 如此之多能。

Tip #2: 认识 "项目" 和 "库" 间的不同

创建的是“项目”还是“库”,意识到这点非常重要。这两者在使用过程中,都存在非常巨大的差异。

库是一个可重用的包,需要作为一个依赖项进行添加 - 比如 symfony/symfony, doctrine/ormelasticsearch/elasticsearch.

而典型的项目是一个应用程序,要依赖于多个库。它通常不可重用(其他项目不需要它成为一个依赖项)。像电子商务网站、客户服务系统等类型的应用就是典型的例子。

在下面的 Tip 中,我会更仔细地讲解库和项目两者的区别。

Tip #3: 为应用程序使用指定的依赖版本

创建应用程序时,应使用最清晰的版本号定义依赖项。 如果需要解析 YAML 文件,就应该以 "symfony/yaml": "4.0.2" 这样的形式明确依赖项。

即使依赖的库遵循了 语义化版本 规范,也会因次版本号和修订号的不同破坏后向兼容性。 例如,使用形如 "symfony/symfony": "^3.1",有可能存在在 3.2 版本废弃的东西,而这会破坏你的应用程序在该版本下通过测试。或者可能在 PHP_CodeSniffer 中存在一个已修复的 bug ,代码就会检测出新的格式问题,这会再次导致错误的构建。

依赖的升级要慎之又慎,不能撞大运。下面 Tip 当中会有一条对此进行更详细的讲解。

听起来有些危言耸听,但是注意这个要点就会避免你的合作伙伴向项目中在添加新库时不小心更新了所有依赖(代码审查时可能忽略这一点)。

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: 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 librarythe build matrix on Travis CI

虽然这解决了多数的不兼容问题,不过仍然要记得,依赖项的最低和最高版本间有太多的组合。他们仍旧可能存在不兼容的情况。

Tip #8: 按名称对 require 和 require-dev 中的包排序

按名称对 requirerequire-dev 中的包排序是非常好的实践。这在衍合一个分支时可以避免不必要的合并冲突。假如你把一个包添加到两个分支文件中的列表末尾,那每次合并都可能遇到冲突。

手动进行包排序的话会很乏味,所以最好办法就是在 composer.json配置一下 即可:

{
...
    "config": {
        "sort-packages": true
    },
...
}

以后再要 require 一个新的包,它会自动添加到一个正确位置(不会跑到尾部)。

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:了解 requirerequire-dev之间的区别

能够意识到requirerequire-dev模块之间的区别是非常重要的。

需要运行在应用中或者库中的包都应该被定义在 require (例如: Symfony, Doctrine, Twig, Guzzle, ...)中。如果你正在创建一个库, 注意将什么内容定义为 require。因为这个部分的 每个依赖项同时也是使用了该库的应用的依赖。
开发应用程序(或库)所需的包应该定义在require-dev (例如:PHPUnit, PHP_CodeSniffer, PHPStan)中。

Tip #11: 安全地升级依赖项

我想大家对如下事实存有共识:应该定期对依赖项升级。 此处我想讨论的是依赖项的升级应该放在明处且慎之又慎,而不能是因其他活计的需要才顺手为之。如果在重构应用的同时又升级了库,那么就很难区分应用崩溃的原因是重构还是升级带来的。

可用 composer outdated 命令查看哪些依赖项需要升级。追加一个 --direct (或 -D)参数开关是个聪明之举,这只会查看 composer.json 指定的依赖项。还有一个 -m 参数开关,只查看次版本号的升级列表。

对每一个老版本的依赖项进行升级都要尊循如下步骤:

  1. 创建新分支
  2. composer.json 文件中更新该依赖项版本到最新版本号
  3. 运行 composer update phpunit/phpunit --with-dependencies (使用升级过的库替换 phpunit/phpunit
  4. 检查 Github 上库的版本库中 CHANGELOG 文件,检查是否存在重大变化。 如果存在就升级应用程序
  5. 本地测试应用程序(使用 Symfony 的话还能在调试栏看到弃用警告)
  6. 提交修改(包括 composer.jsoncomposer.lock 及其他新版本正常运行所做的必要修改)
  7. 等 CI 构建结束
  8. 合并然后部署

有时需要一次升级多个依赖项,比如升级 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.jsoncomposer.lock 应当一直保持同步. 因此, 一直为他们保持自动核对是一个好主意. 将此添加成为你的构建脚本的一部分将会确保 composer.lockcomposer.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": "git@gitlab.mycompany.cz:package-namespace/package-name.git"
    }
]

然后指明常用的包:

"require": {
    "package-namespace/package-name": "1.0.0"
}

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 更新版本库。

更新于 2018-01-08:

文章发布后,我收到了一些建议,提供了更多的使用技巧。它们分别是:

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 的编号)我将很高兴。


英文原文:https://blog.martinhujer.cz/17-tips-for-using-composer-efficiently/
翻译者:Summer
译文:https://laravel-china.org/topics/7609/you-have-to-know-17-composer-best-practices-updated-to-22

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,468评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,620评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,427评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,160评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,197评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,334评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,775评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,444评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,628评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,459评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,508评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,210评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,767评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,850评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,076评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,627评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,196评论 2 341

推荐阅读更多精彩内容

  • 转自:你必须知道的 17 个 Composer 最佳实践(已更新至 22 个) 尽管大多数PHP开发人员都知道如...
    summerbluet阅读 7,105评论 0 7
  • 是什么 如果你知道yum、apt-get、npm、bower等命令中的一种或者多种,那么,你也能很快知道compo...
    旱魃一样阅读 3,109评论 0 9
  • Composer Repositories Composer源 Firegento - Magento模块Comp...
    零一间阅读 3,956评论 1 66
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,569评论 18 139
  • 感恩春节在老家过年,让我看到了一些事情,那天我肚子不好第二老公肚子也不好我们都吃的一个医生的药都好了,我知道这是帮...
    谭小华阅读 336评论 0 0