直接改vendor代码无效,因Composer默认从远程拉取静态快照,下次update或CI构建即被覆盖;本地调试必须用path仓库声明并确保symlink生效。

为什么直接改 vendor 里的包代码根本不管用
因为 composer install 默认从 Packagist 或私有仓库拉取已发布的压缩包,vendor/ 下的文件是完整复制或解压出来的静态快照——你改了里面任何一行,下次 composer update 或 CI 构建时立刻被覆盖。这不是“没生效”,而是根本不在 Composer 的开发路径上。
- 本地包必须通过
repositories显式声明,否则 Composer 根本不扫描你硬盘上的目录 -
require字段写"^1.2"这类语义版本约束,只会匹配远程已发布的 tag,不会识别你本地 git 分支 - Windows 下 path 类型仓库默认是复制而非软链接,改源码后必须手动
composer update vendor/name才能同步
用 path 仓库 + dev 分支别名是最稳的调试方式
它把本地目录变成一个“假远程仓库”,让 Composer 认为你在装一个真实存在的开发版包。关键不是“能不能连”,而是“怎么让它每次都读你刚改的代码”。
- 在主项目
composer.json顶部加:"repositories": [ { "type": "path", "url": "./packages/my-local-package" } ]注意:url必须是相对路径,不能含../跨出项目根目录(Composer 2.4+ 会直接拒绝) - 本地包的
composer.json至少要有"name"和"version": "dev-main"(分支名要和你 git 当前分支一致) - 主项目
require写"my-vendor/my-local-package": "dev-main",并确保顶层有"minimum-stability": "dev" - 执行
composer update my-vendor/my-local-package—— 不带包名的裸update可能误触其他依赖
如何验证本地包是否真被链接上了
别信 composer show 输出的路径,它可能显示正确但实际是硬拷贝。最直接的办法是看文件系统行为。
- Linux/macOS:进
vendor/my-vendor/my-local-package,执行ls -l。如果是软链接,会显示-> ../packages/my-local-package - Windows:检查该目录是否为 junction(用
dir命令能看到标识),或用fsutil reparsepoint query确认 - 改一行本地包的 PHP 代码,然后在主项目里触发那个逻辑,用
debug_print_backtrace()或 Xdebug 看调用栈里的文件路径是不是指向./packages/...而非vendor/... - 删掉
vendor/后只跑composer install,如果失败报 “Could not find package”,说明repositories配置没生效或路径不对
composer link 是什么情况下才该碰的雷区
它只对「已经安装过的包」起作用,本质是在 vendor/ 下强行创建软链接,绕过 composer.json 的版本约束。适合临时验证,但绝不能进 Git 提交或 CI 流程。
- 必须先
composer require my-vendor/my-local-package把包装进去,再进本地包目录执行composer link - 回到主项目后,必须再运行一次
composer link my-vendor/my-local-package,否则链接不生效 - 它不修改
composer.lock,也不刷新 autoload 映射——改了本地包的命名空间或新增类,得额外跑composer dump-autoload - 一旦有人执行
composer update(哪怕只更新另一个包),这个链接大概率被自动还原成普通目录,且毫无提示
真正容易被忽略的是:path 类型仓库的链接行为是“一次性映射”,不是文件系统级实时监听。你改了 ./packages/ 下的代码,vendor/ 里对应文件的修改时间不会变——它只是指向同一份磁盘内容。所以调试时看到“没更新”,第一反应不该是重装,而是确认当前执行的确实是链接后的路径,而不是某个被缓存、被 opcache 加载的老版本字节码。











