Composer自动删除软链接是设计使然,因vendor目录应完全由其管理;正确做法是用path仓库类型配合dev版本,由Composer自动生成并维护软链接及autoload。

软链接被Composer自动删除或覆盖
Composer在执行 install 或 update 时,默认会清空 vendor/ 目录再重装依赖,所有手动创建的软链接(比如 ln -s ../my-local-package vendor/my-vendor/my-package)都会被一并删掉。这不是bug,是设计使然:Composer认为 vendor/ 应完全由它管理。
常见错误现象:ls -l vendor/my-package 显示链接还在,但下次 composer install 后就变成真实目录或直接消失;或者链接指向的源路径被误删,导致后续操作报错 No such file or directory。
- 别把软链接当“持久化方案”——它只是临时绕过安装流程的权宜之计
- 如果真要本地开发调试,优先用
pathrepository +minimum-stability: dev配合require的"dev-main"版本,这样 Composer 会自动 symlink,且不会在下次 install 中破坏 - 若坚持手动 ln,必须在
composer install后立即重建,建议写成脚本并绑定到post-install-cmd脚本钩子中
用path仓库类型让Composer自己建软链接
这是官方支持的、最稳妥的本地包开发方式。Composer 会识别 path 类型仓库,并在满足条件时自动生成软链接而非复制文件。
使用场景:你正在开发一个独立包(如 my-utils),同时在主项目里试用它,又不想每次改完都 composer update 发版。
- 在主项目的
composer.json中添加:"repositories": [ { "type": "path", "url": "../my-utils" } ] -
require中写"my-vendor/my-utils": "dev-main"(分支名需与my-utils/composer.json中的"name"和实际分支匹配) - 确保
my-utils/composer.json有合法"name"和"version"(或使用"version": "dev-main") - 执行
composer update my-vendor/my-utils,成功后vendor/my-vendor/my-utils就是软链接,且不会被后续install破坏
为什么有时软链接没生成,反而复制了文件?
Composer 并非对所有 path 仓库都创建软链接——它有一套隐式判断逻辑,容易踩坑。
- 默认只对
"type": "path"且"url"是绝对路径或相对于composer.json的相对路径才考虑 symlink;如果路径含~或环境变量(如$HOME/my-pkg),会退回到复制模式 - 目标目录(即
url指向的路径)必须存在且可读;若不存在,Composer 会静默跳过 symlink,转而尝试下载(然后失败) - 如果
my-utils/composer.json缺少"name"字段,或"name"与require中不一致,Composer 会忽略该仓库,最终 fallback 到 Packagist 上同名包 - Windows 下需启用开发者模式或管理员权限才能创建符号链接,否则也降级为复制
vendor下已有软链接,但autoload不生效
软链接存在 ≠ 自动加载可用。Composer 的 autoloader 是基于 vendor/composer/autoload_psr4.php 等文件生成的,这些文件只在 dump-autoload 或安装/更新时刷新。
- 手动加的软链接不会触发 autoload 重生成,必须显式运行
composer dump-autoload - 用
path仓库方式引入的包,Composer 会在 symlink 后自动更新 autoload —— 但前提是该包的composer.json中定义了正确的"autoload"段(如"psr-4"映射) - 检查
vendor/composer/autoload_psr4.php是否包含你的命名空间映射;没有的话,说明包本身 autoload 配置无效,或 Composer 根本没把它当有效包处理










