子项目需在自身目录执行composer install --no-autoloader --no-scripts,配置唯一vendor-dir路径,显式声明psr-4 autoload,主项目autoload不得包含子项目路径,并在子项目根目录放.gitignore仅含/vendor且git add -f。

Composer 本身不提供“项目级隔离”能力,它只管理依赖下载与自动加载映射。所谓“隔离”,全是靠你控制 vendor 目录位置、autoload 范围、加载时机和运行时上下文来实现的——没有开关一键开启,只有组合策略。
怎么让子项目不污染主项目的 vendor
子项目(如 Git subtree)直接 composer install 会把包装进主项目 vendor/,导致类名冲突、autoload 错乱、CI 构建失败。
- 在子项目目录下执行:
composer install --no-autoloader --no-scripts,跳过自动注册 autoload 和执行钩子 - 子项目
composer.json的config段必须设"vendor-dir": "lib/my-subtree/vendor",路径要和主项目完全不重叠 - 子项目的
autoload必须用psr-4显式声明命名空间(如"MySubtree\": "src/"),不能依赖 Composer 自动发现 - 主项目
composer.json的autoload和autoload-dev里,绝对不能包含子项目源码路径(比如"src/subtree/") - 子项目根目录放一个
.gitignore,内容仅一行:/vendor,并用git add -f确保它被提交
多租户场景下如何避免不同租户的依赖互相覆盖
一个 SaaS 应用里,租户 A 需要 monolog/monolog:^2.0,租户 B 强制要求 ^3.0,共用 vendor/ 必然报错或加载错版本。
- 为每个租户生成独立
composer.json,只允许动态字段:name(如tenant/123)、require(白名单校验后注入)、autoload(如"TenantS\": "src/tenants/123/") - 安装时必须加
-d指定租户目录,并禁用脚本:composer install --no-scripts --no-interaction -d tenants/123 - 运行时不能
require vendor/autoload.php,而是按需include_once 'tenants/123/vendor/autoload.php',且注意ClassLoader注册顺序(先租户后主项目) - 禁止在租户级
composer.json中配置scripts或autoload-dev,这些属于部署控制层,不该由租户输入决定
为什么加了 --no-dev 还是报 Class not found
--no-dev 只跳过安装 require-dev 包,但不会清理残留的 autoload 映射、lock 文件记录或已生成的 autoload_dev.php 文件。
立即学习“PHP免费学习笔记(深入)”;
- 检查
vendor/是否真没 dev 包:ls vendor/ | grep -E 'phpunit|phpstan|psalm',有输出就说明没隔离干净 - 确认
composer.lock里"packages-dev"下有 dev 包,而"packages"数组里没有它们 - 运行
composer show --dev,返回No dependencies installed for development才算成功 - 关键一步:检查
vendor/composer/autoload_dev.php是否存在——PHAR 打包或容器部署时漏删这个文件,是运行时报Class not found的常见根因 - Docker 构建中别依赖
ENV COMPOSER_NO_DEV=1,应在RUN指令里显式写全:composer install --no-dev --optimize-autoloader
APCU 缓存前缀不生效?三个地方必查
apcu-prefix 是 composer.json 里的 config 字段,不是 PHP ini 设置。它只影响 vendor/autoload.php 生成时注入的缓存 key 前缀,不改这个配置,多个项目共享 APCU 就必然互相覆盖。
- 确认你执行了
composer dump-autoload -o(或composer install),否则新前缀不会写入vendor/composer/autoload_real.php - 打开
vendor/composer/autoload_real.php,搜索apcu_fetch,看缓存 key 是否含你设的前缀(如composer-{DOCUMENT_ROOT}-) - 检查是否被全局配置覆盖:
composer config --list | grep apcu-prefix,若有--global输出,需用--unset清掉 - Web 环境用
{DOCUMENT_ROOT}插值时,确保apc.use_request_time=1且apc.stat=0,否则变量不解析 - 清缓存只需:
php -r "apcu_clear_cache('user');",不用清 opcode 缓存
所有隔离手段都绕不开一个事实:Composer 不是运行时加载器,它的作用止于构建阶段。真正决定“哪个类从哪来”的,是你怎么组织 vendor 目录、怎么写 autoload 映射、以及运行时 require 了哪个 autoload.php —— 这些细节一旦错位,隔离就形同虚设。



















