本质区别在于安装依据:composer install严格按composer.lock精确还原依赖,确保环境一致;composer update则忽略lock文件,重新解析composer.json并升级至最新兼容版本,易引发不兼容问题。

composer install 和 composer update 本质区别在哪
关键不是“装不装”,而是“按谁的版本装”。composer install 只读 composer.lock,不管 composer.json 里写了什么新版本,所有包都严格按 lock 文件里的哈希和版本号安装;composer update 则完全忽略 lock 文件,重新解析依赖树,可能升到最新兼容版——这在 CI/CD 或多人协作中极易导致线上行为不一致。
- 本地开发想试新功能?用
composer update,但别直接 push 新的composer.lock - CI 流水线或生产部署?必须用
composer install,且确保composer.lock已提交 - 想只更新某一个包?
composer update monolog/monolog,它仍会重算该包及其子依赖,但其他包不动
--no-dev 参数不是“可选开关”,是环境隔离硬边界
--no-dev 的作用不是“少装几个包”,而是让 Composer 彻底跳过 require-dev 区块里的所有定义,包括包、脚本、autoload-dev 映射。它不关心你本地有没有写测试,只认配置结构。
- 生产镜像构建必须加
--no-dev,否则 PHPUnit、PHPStan 这类工具会打进镜像,增大体积、暴露攻击面 -
composer dump-autoload默认不处理autoload-dev,改了测试自动加载路径后,得手动跑composer dump-autoload --dev - CI 脚本里用了
--no-dev,但本地没加,就会出现 “phpunit 找得到类,线上报 Class not found” —— 因为 autoload-dev 没生效,测试命名空间根本没注册进 autoloader
怎么让 test 脚本在不同环境走不同参数
硬编码 "test": "vendor/bin/phpunit --verbose" 是反模式。真正可控的方式是靠环境变量 + Composer 的 env 配置,而不是靠 shell 前置赋值或 --env=testing 这种不存在的参数。
- 在
composer.json的scripts里定义时,用env键显式声明变量:"scripts": { "test": "vendor/bin/phpunit", "test:ci": "vendor/bin/phpunit --no-coverage --fail-on-warning" }, "scripts-descriptions": { "test": "Run tests locally with coverage and colors", "test:ci": "Run tests in CI: no coverage, fail fast" } - 本地执行
composer test,CI 中执行composer test:ci,逻辑分离清晰,无需传参猜意图 - 如果真要动态传参,用双横线:
composer test -- --filter=MyFeatureTest,后面的内容会原样透传给vendor/bin/phpunit,但注意:它不会影响环境判断逻辑
autoload-dev 配错位置,--no-dev 也救不了你
autoload-dev 必须和 autoload 并列在 composer.json 顶层,拼成 autoloadDev 或塞进 require-dev 里,整个区块都会被静默忽略。结果就是:测试类映射进了生产 autoloader,--no-dev 完全无效。
- 正确写法:
"autoload": { "psr-4": { "App\": "src/" } }, "autoload-dev": { "psr-4": { "Tests\": "tests/" } } - 错误示例:
"require-dev": { "...": "...", "autoload-dev": { ... } }—— 这个autoload-dev根本不被识别 - 验证是否生效:运行
composer dump-autoload --dev后,打开vendor/autoload.php搜索Tests\,看是否注册;再跑一次composer install --no-dev,检查vendor/composer/autoload_psr4.php里还有没有"Tests\"键
复杂点在于:autoload-dev 不只是“让测试类能被加载”,它还决定了哪些命名空间会被写进最终的 autoloader。一旦配错,生产环境就可能意外加载测试代码,而这种问题在线上往往表现为偶发的类冲突或内存泄漏,很难复现。











