根本原因不是中文路径,而是子模块未初始化、autoload冲突或vendor目录污染;应进入子模块目录清理残留后执行composer install,并在CI中显式运行git submodule update --init --recursive。

Composer 在中文环境里报 Git 子模块相关错误,和字符编码无关——真正卡住的是子模块路径未初始化、autoload 冲突或 vendor 目录被污染。直接删缓存、改 locale 或重装 PHP 都没用。
为什么 composer update 会因子模块失败
根本不是“中文路径”导致的,而是 Composer 尝试解析某个含子模块的包(比如私有 SDK)时,发现 vendor/xxx/package 目录下只有空文件夹、没有 .git/modules 或 composer.json,于是跳过 autoload 注册,后续类加载就崩了。
- 常见错误现象:
Class 'VendorPackageSomeClass' not found,但vendor/xxx/package/src/确实存在,只是里面是空的 - 进入
vendor/xxx/package执行git status,输出fatal: not a git repository—— 说明子模块根本没初始化 - 如果该包是通过
"type": "vcs"引入的,而你本地没提前git submodule update --init --recursive,Composer 就读不到子模块里的composer.json,直接放弃安装
用 post-install-cmd 自动初始化子模块
别靠人工记着每次 composer install 后手动进 vendor 执行命令——CI 和新同事都会漏。把初始化逻辑写进 composer.json 的脚本钩子里,让 Composer 自己触发。
在 OpenClaw 中管理 GitHub 仓库、Pull Request、Issue 与工作流。适用于审查 PR、管理 Issue、自动化 GitHub Actions、同步仓库等场景。
- 在根项目
composer.json的"scripts"段里加: "post-install-cmd": ["@php -r "if (is_dir('./vendor/xxx/package/.git')) { chdir('./vendor/xxx/package'); system('git submodule update --init --recursive'); }""]- 把
xxx/package替换成实际出问题的包名;多个包就复制多行,每行一个 - 确保部署机上
git在 PATH 里,且能访问子模块仓库(SSH key 或 HTTPS token 配置正确) - 注意:这个钩子只在
install和update成功后运行,不会干预依赖解析阶段
避免子模块干扰的两种替代方案
如果你控制不了上游包是否用子模块,或者 CI 权限受限没法跑 git submodule,就绕开 Git 协议本身。
- 强制走 dist 包:
composer install --prefer-dist,或在composer.json里设"preferred-install": {"*": "dist"}—— 这样 Composer 下载 zip 归档,不 clone git,自然没子模块这回事 - 改用
"path"类型仓库:"repositories": [{"type": "path", "url": "./modules/my-submodule"}],前提是子模块目录已存在且含合法composer.json;这样 Composer 当普通本地目录处理,完全跳过 Git 流程 - 别用
"type": "package"+"dist"手动打包,除非你有私有 Satis 或 Artifactory——否则维护成本远高于收益
子模块里运行 composer install 的致命陷阱
千万别在父项目根目录下用 --working-dir 去子模块里装依赖——autoload 会错乱,vendor 目录可能被写到父项目里,甚至覆盖掉父项目的 autoloader。
- 必须 cd 进子模块目录:
cd modules/my-submodule,再执行composer install - 进目录后先删掉残留的
vendor/和composer.lock,否则旧 lock 可能锁死不兼容版本 - 确认子模块
composer.json的"name"字段唯一,且 autoload 前缀(如"MyOrg\SubModule\")和父项目不重叠 - 父项目的
autoload段里绝对不要 include 子模块源码路径——它该用自己的vendor/autoload.php加载
最常被忽略的一点:子模块的 .gitmodules 文件里 URL 是 SSH 还是 HTTPS?如果 CI 机器只配了 HTTPS 凭据,而 URL 写的是 git@github.com:xxx/yyy.git,git submodule update 就会静默失败,连错误都不报——得进 vendor 目录手动试一次才能发现。

















