Composer无“白名单配置”概念,所谓白名单实为对require/require-dev的误称;真正实现模块级依赖隔离靠autoload-dev路径隔离与require-dev作用域控制,二者配合确保测试类和开发依赖仅在dev环境加载且不污染生产。

Composer 没有“白名单配置”这个官方概念,所谓“白名单”通常是开发者对 require 或 require-dev 中显式声明依赖的误称。真正需要拆分的,是「哪些包只在特定模块/场景下可用」——这靠的是 autoload-dev 路径隔离 + require-dev 作用域控制,而不是虚构的白名单机制。
为什么不能用多个 composer.json 按目录“白名单” require?
子目录放独立 composer.json(比如 src/User/composer.json)是无效的。Composer 只读取项目根目录下的 composer.json,其余文件被完全忽略。强行这么做只会导致:
- 本地
composer install不会加载子目录依赖,new UserPdfGenerator()直接报Class not found - CI 构建时因路径混乱失败,且无法被 PHPStan 或 IDE 正确索引
- Git 子模块或 path 仓库方式虽可行,但属于跨项目协作范畴,不是“项目内白名单”
用 autoload-dev + require-dev 实现模块级依赖可见性
这是最轻量、最符合 Composer 设计意图的做法:把模块代码放在 autoload-dev 映射路径下,对应依赖放进 require-dev,就能确保它们只在开发/测试环境加载,且不会污染生产部署。
例如订单 PDF 功能只在 CLI 命令和测试中使用:
{
"autoload": {
"psr-4": {
"App\Schema\": "src/Schema/",
"App\User\": "src/User/"
}
},
"autoload-dev": {
"psr-4": {
"App\Order\": "src/Order/"
}
},
"require": {
"webonyx/graphql-php": "^14.11"
},
"require-dev": {
"mpdf/mpdf": "^8.2"
}
}
关键点:
-
AppOrder下的类只在composer dump-autoload --dev后才可被自动加载 -
mpdf/mpdf不会出现在生产vendor/中(只要不执行composer install --no-dev) - 若某
OrderResolver需在 HTTP 请求中调用 PDF,就必须把它移到autoload,同时把mpdf/mpdf移到require—— 不存在“运行时按需加载包”的魔法
想让某些包只在特定环境下生效?用 repositories + packagist.org 禁用
这不是白名单,而是源优先级控制。比如你 fork 了 laravel/framework 并修复了一个 GraphQL 兼容问题,希望主项目只用你的版本:
- 必须在
repositories数组顶部加{"packagist.org": false} - 私有源 URL 必须返回包含
"laravel/framework"的packages.json,否则 Composer 会跳过它继续找官方源 - 验证是否生效:运行
composer update -vvv laravel/framework,看日志里是否出现Found package "laravel/framework"来自你的地址
这种策略管的是“从哪下载”,不是“给谁用”;它影响整个项目,无法限定到某个命名空间或模块。
真正容易被忽略的点在于:autoload 路径和 require/require-dev 的绑定是静态的,没有运行时开关。所谓“按项目拆分”,本质是厘清代码位置、加载时机、依赖作用域三者关系——而不是幻想存在一个配置项能动态开启/关闭某个包的可见性。


















