推荐用数组而非字符串定义脚本命令,如["@php","artisan","storage:link"],避免shell解析差异;权限操作须用PHP内置函数;vendor/bin工具调用应无扩展名;平台判断必须在PHP代码中实现。

脚本值用数组而非字符串,避免 shell 解析差异
字符串脚本(如 "php artisan migrate")在 Windows 和 Linux 下由不同 shell 执行:Linux 走 /bin/sh,Windows 走 cmd.exe,容易因空格、引号、路径分隔符出错。数组格式强制 Composer 直接调用二进制,跳过 shell 层。
- ✅ 推荐写法:
"post-install-cmd": ["@php", "artisan", "storage:link"] - ❌ 风险写法:
"post-install-cmd": "php artisan storage:link"(Windows 下可能找不到php或解析artisan路径失败) -
@php固定调用 Composer 自带 PHP,不依赖环境 PATH 中的 php 版本 - 参数含空格或特殊字符时(如
--env=production),数组天然安全,字符串需手动转义
权限与路径操作必须用 PHP 内置函数,别信 chmod/chown
Linux/macOS 的 chmod、chown 在 Windows 上根本不存在,直接写进 scripts 会导致 Script not found 或静默失败。跨平台权限/目录初始化必须用 php -r 或封装好的 PHP 类。
- ✅ 安全写法:
"@php -r "mkdir('storage', 0775, true) && chmod('storage', 0775);"" - ✅ 更健壮:写个
SetupPermissions.php,用 PSR-4 加载后以 callable 方式调用:"App\Scripts\SetupPermissions::run" - 注意
0775是八进制字面量,不要写成字符串"0775",PHP 会当十进制处理 - Windows 下
is_writable()行为和 Linux 不同,建议用touch()+unlink()实测写入能力
vendor/bin 工具调用要加 .bat 兼容层,但别硬编码扩展名
Composer 会在 Windows 下为 vendor/bin 里的命令自动生成 .bat 文件,但你在脚本里调用时不能写 vendor/bin/phpunit.bat —— 这在 Linux 下就挂了。正确方式是直接调用无扩展名版本,靠系统 $PATH 或 Composer 自动适配。
- ✅ 正确:
["vendor/bin/phpunit", "--testdox"](Composer 运行时自动补.bat或选可执行文件) - ❌ 错误:
"vendor/bin/phpunit.bat"或"vendor/bin/phpunit"作为字符串(失去数组安全性和平台适配) - 确保
vendor/bin已加入系统 PATH,否则即使有 .bat 也找不到命令 - CI 环境中若禁用
vendor/bin,改用php vendor/bin/phpunit更稳妥(@php会自动定位到当前项目 PHP)
运行时平台判断必须放在 PHP 代码里,别塞进 composer.json
composer.json 是声明式配置,不是执行环境。你想“Windows 下跳过某步,Linux 下执行 chmod”,不能靠 if 写在 JSON 里 —— 它根本不支持逻辑分支。所有条件判断必须下沉到 PHP 脚本中。
- ✅ 做法:写一个
DeployHelper.php,里面用PHP_OS_FAMILY === 'Windows'或defined('PHP_WINDOWS_VERSION_BUILD')判断 - ✅ callable 示例:
"App\DeployHelper::postInstall",方法内自由调用exec()、chmod()或跳过 - ❌ 别试图在 scripts 字段里拼接命令:
"php -r "if(PHP_OS_FAMILY=='Windows') echo 'skip';""—— 可读性差、难调试、无法复用 - callable 必须 public static,且类能被 autoload 加载(确认
autoload配置覆盖该路径)
composer.json 只负责调度,不负责执行逻辑。所有跨平台分支、错误恢复、路径探测,都得塞进 PHP 里跑 —— 否则你写的“兼容脚本”,其实只在你本地那台机器上兼容。


















