在PHP项目中,Composer最容易被误解的地方之一,就是把composer install和composer update都理解成“把依赖包装上或者更新一下”。
实际上,这两个命令处理的是完全不同的问题。
composer install解决的是“按照项目已经确定的依赖版本,把运行环境恢复出来”;composer update解决的是“重新计算依赖关系,并决定项目下一组应该使用什么版本”。
这一区别看起来只是两个命令,实际上直接关系到生产环境稳定性、团队协作、CI/CD部署以及依赖升级风险。
Composer的核心文件有两个:composer.json和composer.lock。
composer.json描述的是项目允许使用什么依赖。例如:
{
"require": {
"monolog/monolog": "^3.0",
"guzzlehttp/guzzle": "^7.0"
}
}
这里的^3.0和^7.0并不是具体安装版本,而是版本约束。它告诉Composer哪些版本符合项目要求。
真正记录“这一次项目到底安装了哪些版本”的,是composer.lock。
例如:
monolog/monolog 3.9.0
guzzlehttp/guzzle 7.9.3
psr/log 3.0.2
这就是Composer设计中非常重要的两个层次:composer.json描述“允许什么”,composer.lock描述“已经决定使用什么”。
所以,当一个项目已经存在有效的composer.lock时,开发者从Git仓库拉取代码后,通常应该执行:
composer install
Composer会读取composer.json,同时使用composer.lock中已经确定的精确版本安装依赖。这样开发机、测试服务器、CI环境和生产服务器就可以使用同一套依赖版本。Composer官方文档也明确建议应用项目将composer.lock提交到版本控制系统,以保证不同环境使用一致的依赖版本。
这里有一个非常重要的细节。
composer install并不是“永远不会更新任何东西”。
如果项目没有composer.lock,Composer需要根据composer.json重新解析依赖并生成lock文件。换句话说,新项目第一次初始化时,可能需要通过依赖解析得到一套版本并产生composer.lock。之后这个lock文件才成为稳定安装的依据。
因此,更准确的理解是:
有composer.lock时,install主要负责复现已经锁定的依赖环境。
没有composer.lock时,install需要先进行依赖解析并生成lock文件。
而composer update的性质完全不同。
执行:
composer update
Composer会重新读取composer.json中的版本约束,对整个依赖树进行重新求解,然后选择当前约束范围内符合条件的版本,并把新的精确版本写入composer.lock。之后Composer会安装这些解析出来的依赖。
例如:
"guzzlehttp/guzzle": "^7.0"
并不意味着永远使用7.0.0。
如果lock文件原来锁定7.7.0,而当前存在符合约束的7.x新版本,那么执行完整的:
composer update
就可能导致Guzzle以及它的部分依赖发生变化。
这里的关键并不是“Composer自动找最新版本”,而是“重新进行依赖求解”。
因为依赖关系通常不是一棵简单的树,而是一张复杂的依赖图。
假设你的项目直接依赖A和B:
项目
├── A
│ └── C ^2.0
└── B
└── C ^2.3
Composer需要找到一个同时满足A、B以及其他所有依赖约束的C版本。
如果某个包的新版本改变了依赖要求,Composer的求解器就可能重新调整整个依赖图中的某些版本。
所以composer update绝不仅仅是“下载新版本”。
它实际上是在重新计算项目的依赖闭包。
这也是为什么生产服务器通常不应该直接执行:
composer update
如果生产环境已经部署了经过测试的composer.lock,直接update相当于在生产机器上重新打开依赖版本选择器。
你原来测试的是一组明确版本,生产服务器却可能得到另一组版本。
这会让生产环境出现一个非常麻烦的问题:代码仓库是同一个,但运行结果却不一定相同。
而:
composer install --no-dev --optimize-autoloader
则更加符合典型生产部署场景。
它的核心思路是:代码版本已经确定,依赖版本也已经通过lock文件确定,现在只是把这套经过测试的环境部署出来。
对于团队开发也一样。
假设开发人员A修改了PHP代码,开发人员B刚刚更新了依赖。如果B执行了:
composer update
然后把新的composer.lock提交到Git,那么团队其他成员拉取代码之后执行:
composer install
就会得到B已经解析并提交的那一整套依赖版本。
因此,composer.lock并不是一个应该“尽量少碰”的普通配置文件。
它是应用项目依赖环境的一部分。
真正应该避免的是未经测试的lock文件变化,而不是正常维护lock文件。
当你确实需要升级依赖时,应该主动更新它,然后测试,再提交。
例如项目需要升级某个包,不必每次都执行整个:
composer update
可以指定目标包:
composer update guzzlehttp/guzzle
Composer支持针对特定包进行更新,这可以明显缩小依赖变化范围。官方文档也提供了针对单个或多个包更新的方式。
如果项目规模较大,这种做法尤其重要。
因为一次完整的依赖更新可能涉及几十甚至上百个间接依赖,而单独更新一个直接依赖通常更容易审查变化。
还可以使用:
composer update -m
也就是--minimal-changes,要求Composer尽量保持现有lock版本,只进行必要的依赖变化。对于希望控制更新范围的项目,这类选项非常有价值。
另一个常见错误是认为“有了composer.lock,就完全不用检查composer.json”。
实际上两者必须保持逻辑一致。
可以使用:
composer validate
检查composer.json是否合法,同时检查lock文件是否与项目配置保持同步。Composer官方也建议在提交composer.json以及适用的composer.lock之前运行这个命令。
开发者还应该区分“更新Composer”和“更新项目依赖”。
:
composer self-update
更新的是Composer自身。
:
composer update
更新的是当前项目的PHP依赖。
这两个动作完全不是一回事。
如果项目出现依赖问题,也不要一上来就删除composer.lock。
例如:
rm composer.lock
composer update
会彻底改变依赖解析结果,这有时候可以作为排查手段,但不应该成为日常维护方法。
对于生产环境尤其危险,因为你可能只是想解决一个包的问题,却把整个依赖树重新排列了一遍。
真正专业的依赖维护流程通常应该是这样的。
开发人员修改composer.json或者通过:
composer require vendor/package
引入新的直接依赖。
Composer完成依赖解析并修改composer.lock。
然后运行项目测试、静态分析、代码检查以及必要的集成测试。
确认没有问题后,把composer.json和composer.lock一起提交。
CI环境通过:
composer install
从lock文件恢复完全一致的依赖环境。
生产部署继续使用:
composer install --no-dev --optimize-autoloader
这样依赖版本的决定发生在开发和测试阶段,而不是发生在生产服务器上。
安全维护则是另外一件事情。
如果只是想检查依赖是否存在已知安全问题,可以使用:
composer audit
而不是为了“看看有没有安全更新”就直接执行完整的composer update。
对于大型项目,还可以使用:
composer outdated
查看哪些依赖存在可用更新,然后逐项评估,而不是把整个依赖树一次性升级。
这里还有一个非常重要的例外:PHP库项目。
Composer官方对应用和可复用库的lock文件处理方式并不完全相同。对于一个最终运行的应用程序,提交composer.lock通常是推荐做法;对于一个供其他项目依赖的库,lock文件不会决定下游项目最终安装什么版本,因此它的作用完全不同。
所以不能简单地说“所有Composer项目都必须提交composer.lock”。
更准确的原则是:应用项目通常应该提交,库项目则根据开发和测试需求决定。
如果把整个过程压缩成一句话,可以这样理解:
composer.json决定“允许安装什么版本”,composer.lock决定“这次项目已经选定什么版本”,composer update负责重新做这个选择,而composer install负责把已经选择好的依赖恢复出来。
这也是为什么成熟项目通常不是“开发时一直composer update,部署时再看看能不能跑”,而是把依赖升级本身当作一次需要测试、审查和回滚准备的代码变更。
Composer真正解决的问题,从来不只是帮PHP开发者下载几个第三方包,而是把一个复杂的依赖关系图变成可以重复构建、可以审计、可以部署、可以回滚的确定性软件环境。
当composer.lock被认真对待以后,开发环境、测试环境和生产环境之间的差异会大幅减少。对于PHP项目来说,这往往比单纯追求“依赖永远是最新版本”更加重要。
Composer install 和 composer update 有什么区别?实际开发中应该怎样选择
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
分享 Facebook | X | WhatsApp | LinkedIn
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP