Composer脚本无法自动切换cwd,需显式用sh -c 'cd dir && cmd'或PHP中chdir(__DIR__)控制执行路径,--working-dir仅影响配置加载。

Composer 脚本中如何指定独立的 cwd 执行路径
Composer 本身不支持在 scripts 中直接声明每个脚本的执行目录,但可以通过封装命令或使用 shell 特性绕过默认行为——关键在于让脚本在目标路径下启动,而不是依赖 Composer 自身的路径切换逻辑。
composer run-script 不会自动切到包根目录?
很多人误以为 composer run-script 会在当前 package 的根目录执行,其实它只保证在 composer.json 所在目录运行(即项目根),对 workspace 子包、monorepo 中的子模块无效。如果你在 packages/foo/composer.json 里定义了脚本,却从项目根运行 composer run-script build --working-dir=packages/foo,--working-dir 只影响 Composer 加载配置的位置,不改变子进程的工作目录。
- 真实行为:
composer run-script启动的子进程仍以当前 shell 目录为cwd - 验证方式:在脚本里加
pwd或echo __DIR__,你会发现输出的是你调用composer的位置,不是packages/foo - 正确做法:显式用
cd切换,或用php -d cwd=...(仅限 PHP 脚本)
推荐写法:用 sh -c 或 bash -c 包裹并切换目录
这是最通用、跨平台(Linux/macOS/WSL)且无需额外依赖的方式。Windows CMD 不支持内联 cd && 链式调用,所以优先用 shell 封装。
"scripts": {
"build:foo": "sh -c 'cd packages/foo && npm run build'",
"test:bar": "sh -c 'cd packages/bar && vendor/bin/phpunit'"
}
-
sh -c是 POSIX 兼容方案,比直接写cd ... && cmd更可靠(尤其当路径含空格或特殊字符时) - 避免用
cd packages/foo && npm run build—— 如果cd失败,后续命令仍会执行(静默出错) - 如果必须支持原生 Windows CMD(非 Git Bash/WSL),改用
cmd /c "cd /d packages\foo && npm run build",注意反斜杠和/d(跨盘符)
PHP 脚本里怎么拿到正确的当前目录?
如果你在 scripts 中调用的是自定义 PHP 文件(如 "post-install-cmd": "MyScript::run"),不要依赖 getcwd(),它返回的是 Composer 进程启动时的路径。应通过 __DIR__ 或 dirname(__FILE__) 定位脚本自身位置,再相对跳转。
// scripts/BuildFoo.php
class BuildFoo
{
public static function run()
{
$targetDir = dirname(__DIR__) . '/packages/foo';
chdir($targetDir); // 显式切换
// 后续 exec('npm run build') 就在正确路径下了
}
}
-
__DIR__指向该 PHP 文件所在目录,稳定可靠;getcwd()不可控 - 如果脚本被 symlink 引用,
__DIR__返回的是 symlink 目标路径(PHP 默认行为),一般符合预期 - 用
chdir()前建议先if (!is_dir($targetDir)) { throw new RuntimeException(...); },避免静默失败
真正容易被忽略的点是:Composer 的 --working-dir 参数只控制配置加载路径,不影响脚本实际执行环境。所有“路径相关”的行为,最终都得靠你自己在命令层或 PHP 层显式控制,没有魔法开关。











