Composer会删除你手动修改的vendor代码,这是设计使然而非bug;它严格依据composer.lock中的哈希值校验,一旦发现本地文件内容不一致(如加var_dump、修typo),即强制覆盖整个包目录,无论改动大小。

Composer 会删你改过的 vendor 代码,不是 bug,是设计如此。 它只认 composer.lock 里记录的哈希值,一旦发现本地文件内容和锁文件不一致,就会强制重写——哪怕你只改了一行注释、加了个 var_dump() 或修复了某个 class 的 typo。
vendor 目录不是你的工作区,而是只读快照
Composer 把 vendor/ 当作纯输出目录,所有变更都应通过修改 composer.json + composer update 或打 patch(用 composer-patches)来管理。它不区分“你是调试临时改的”还是“你真想长期覆盖”,只要哈希对不上,就判定为“损坏”,直接覆盖。
- 常见现象:你在
vendor/symfony/console/Command.php里加了日志,下次composer install或composer update后,这行日志就没了 - 更隐蔽的情况:CI 构建时用了
--no-dev,但你本地装了 dev 包,导致某些类加载路径不同,composer dump-autoload后又触发重写,连带清掉你的修改 - Git 误提交
vendor/是最大诱因:一旦有人把改过的 vendor 提交进仓库,别人git pull后再composer install,就会立刻被还原,还可能报file has been modified警告
为什么 --no-scripts 和 --no-plugins 也救不了你
这两个参数只跳过脚本执行和插件逻辑,不影响核心文件校验流程。只要 composer.lock 里某包的 dist.shasum 或 source.reference 没变,Composer 就认定“该包应该长这样”,然后硬覆盖。
-
composer install --no-scripts --no-plugins:跳过 post-install-cmd,但 vendor 文件照删 -
composer update --dry-run:能预览哪些包会被重装,但无法阻止后续真实执行时的覆盖 - 试图用
chown -R root:root vendor/或chmod -w锁文件?Composer 会直接报错退出,而不是跳过
真要改第三方代码,只有三条路可走
绕开覆盖机制不是靠参数,而是换思路。任何“先改 vendor 再 commit”的做法,都在给团队埋雷。
- 用
composer-patches:在composer.json里声明 patch 文件路径,Composer 安装后自动打补丁,且 patch 内容可 Git 管理 - fork + 替换 repository:把目标包 fork 到自己仓库,修完后在
repositories里指向它,并在require中指定分支或 commit hash - 抽离逻辑到自己的包:如果改的是业务相关行为(比如某个 SDK 的请求拦截),不如封装一层 wrapper 类,让项目依赖自己的包,而非直接依赖原始 vendor
最常被忽略的一点:哪怕你用 composer update --lock 重生成锁文件,只要没动 vendor/ 里的源码,就不会触发覆盖;但只要运行一次 install 或 update,所有未受控的本地修改就注定消失。











