Composer如何分环境装包_Composer环境依赖分离方法【核心】
Composer 这东西,表面上看是个依赖管理工具,但说到“分环境装包”,很多人的第一反应可能是“它能不能自动识别环境然后装对应的包?”——答案是:想多了。Composer 根本不关心你运行在本地开发还是生产服务器上,它只认你传给它的命令参数。所谓的“分环境”,本质上是靠 require 和 req
Composer 这东西,表面上看是个依赖管理工具,但说到“分环境装包”,很多人的第一反应可能是“它能不能自动识别环境然后装对应的包?”——答案是:想多了。Composer 根本不关心你运行在本地开发还是生产服务器上,它只认你传给它的命令参数。所谓的“分环境”,本质上是靠 require 和 require-dev 的职责分离,加上安装时显式传参来控制的。不是环境感知,而是命令驱动。

为什么 composer install 默认会装 phpunit、var-dumper 这些包
因为这些包被写在了 require-dev 里。而 composer install 默认不加 --no-dev 的话,会把 require-dev 里的所有包一股脑全装进去——它才不管你是本地调试还是线上跑业务,只看参数。
require-dev里的包参与composer install的默认安装流程,和require里的包一样,走解析、下载、解压、autoload 注册全流程。- 常见翻车点:Dockerfile 里只写了
composer install,没加--no-dev,结果生产镜像里塞进了phpunit/、sebastian/、symfony/var-dumper/等一堆开发工具。 composer.lock文件会同时记录packages(require)和packages-dev(require-dev),但--no-dev只跳过后者安装,不会删除 lock 里的记录。所以 lock 文件里依然有 dev 包的信息。
composer install --no-dev 到底跳过了什么
它跳过 require-dev 声明的所有包的安装,也跳过这些包注册的 autoload 规则(比如 autoload-dev 里定义的 "Tests": "tests/")。但它不会影响 require 包的行为,也不影响你的业务代码。
vendor/目录下不会出现phpunit/phpunit、mockery/mockery等目录。vendor/composer/autoload_dev.php文件不会生成——这是关键,漏删这个文件会导致类找不到的错误。- 如果你的代码里写了
new TestsFooTest(),即使--no-dev成功了,运行时依然会报错。这不是 Composer 的问题,是你代码逻辑越界了,生产环境不应该引用测试类。 --no-dev不影响config.platform、scripts或其他配置字段,它只作用于依赖安装阶段。
Docker 和 CI 里怎么避免漏掉 --no-dev
不能靠记忆,也不能靠环境变量自动判断。必须硬编码在命令里。CI 缓存、Docker 构建、PHAR 打包,每个环节都要显式声明,否则前一步缓存污染后一步部署。
- Dockerfile 中必须写死:
RUN composer install --no-dev --optimize-autoloader --no-scripts。别用ENV COMPOSER_NO_DEV=1,那样会影响所有后续命令,包括本地开发环境。 - GitHub Actions / GitLab CI 的缓存 key 必须包含
--no-dev标志,例如:composer-${{ hashFiles('**/composer.lock') }}--no-dev。否则缓存复用时可能把 dev 包带进生产构建。 - CI 测试阶段要用完整安装:
composer install && ./vendor/bin/phpunit;但构建生产镜像那一步,必须切回--no-dev,两者不能混用。也就是说,测试和生产两条流水线要分开。 - 用 Box 打包 PHAR 时,除了
--no-dev,还得手动删掉vendor/composer/autoload_dev.php,否则 PHAR 加载时仍会尝试读取它。
容易被忽略的 autoload 边界问题
autoload-dev 不是给 PHPUnit 用的,是给你写的测试类用的。它只在本地跑完整 composer install 后才生效,生产环境即使配置再全也没用。
- 如果你的
tests/目录里用了MockeryLegacyMockInterface,除了把mockery/mockery放进require-dev,还得确保autoload-dev包含"Tests\\": "tests/",否则new TestsFooTest()仍会找不到类。 composer dump-autoload --classmap-authoritative在生产环境执行时,不会扫描autoload-dev路径。哪怕你忘了加--no-dev,只要 vendor 里没装那些包,就扫不到。- 不要试图用
"config": {"dev": false}一劳永逸——它会让本地开发也永远装不上 dev 包,连composer require --dev都失效,得不偿失。


































