Composer默认采用copy模式而非symlink,这是为保障构建可重现性而设计的保守行为;启用symlink必须在repositories的path条目中显式配置"symlinks": true(≥2.2)或"options": {"symlink": true}(旧版),且需执行composer update或--prefer-source重装并验证符号链接是否生效。

默认就是 copy 模式,改本地代码不会生效——这不是 bug,是 Composer 故意设计的保守行为。
symlink 模式必须显式启用,且配置位置很关键
Composer 对 path 类型仓库默认禁用符号链接,只在明确声明后才尝试创建。但“明确声明”的位置极易出错:
- ✅ 正确写法(写在
repositories的具体 path 条目内):{"type": "path", "url": "../my-pkg", "symlinks": true}(Composer ≥ 2.2) - ✅ 或旧版本写法:
{"type": "path", "url": "../my-pkg", "options": {"symlink": true}} - ❌ 错误写法(塞进
config段):{"config": {"symlinks": true}}—— Composer 完全忽略 - ❌ 错误写法(写在本地包的
composer.json根级,但主项目没配repositories)—— 不触发路径发现逻辑
copy 模式会静默 fallback,导致改代码不生效却查不出原因
当你以为 symlink 已启用,实际却 fallback 到 copy,是最隐蔽的调试陷阱:
- 运行
ls -la vendor/vendor/name(Linux/macOS)或dir vendor\vendor\name(Windows),输出中**必须含->或JUNCTION** 才算成功 - 若显示为普通目录,说明已 fallback,此时改
../my-pkg源码毫无影响 - fallback 常见诱因:Windows 未以管理员身份运行终端、Docker 容器缺
--cap-add=SYS_ADMIN、本地包composer.json漏了"options": {"symlink": true}
install 不重建 symlink,update 才走完整解析流程
composer install 完全依赖 composer.lock,它不会重新判断仓库类型或重试 symlink 创建;只有 update 会重走“发现 → 解析 → 链接”全流程:
- 首次启用 symlink:必须执行
composer update vendor/name(推荐精确更新,避免波及其他包) - 后续改本地包代码:无需再
update,只要 symlink 存在,改源码即刻反映 - 若改了本地包的 autoload 映射(如新增 PSR-4 命名空间):必须手动运行
composer dump-autoload,否则自动加载器找不到新类 - 验证是否真走 path 模式:打开
composer.lock,搜索该包,确认"source": {"type": "path"},而非"type": "git"
路径和 name 字段错一个字符,symlink 就根本不会启动
Invalid target path for symlink 这个报错不是权限问题,而是路径校验直接失败——Composer 连创建链接的动作都不会触发:
- 路径不能含
~或$HOME,只能是绝对路径(如/home/user/my-pkg)或项目根目录起算的相对路径(如../my-pkg) -
url指向的必须是**真实存在、非空、含合法composer.json的目录**,且该目录不能是另一个软链接(禁止嵌套 symlink) -
name字段必须与require中声明的字符串**逐字一致**:大小写、分隔符(只允许短横线-)、vendor 名全部对齐,"acme/utils"≠"Acme/utils" - Windows 下路径含中文或空格,某些 PHP 版本会解析失败,建议规避
最容易被忽略的是:即使所有配置都对,只要 vendor/ 和 composer.lock 里还存着旧的 copy 模式记录,Composer 就永远不会尝试 symlink——必须删干净再用 composer install --no-cache --prefer-source 强制重装。


















