在 composer.json 中定义可执行脚本需显式引入 autoloader,推荐用 php scripts/deploy.php 方式调用;区分 post-install-cmd(仅 install 后触发)与 post-autoload-dump(每次 autoload 更新时触发);安全加载 .env、避免硬编码、跨平台用 php 内置函数而非 shell 命令。

如何在 composer.json 中定义可执行的自定义脚本
Composer 的 scripts 段不是“写完就能跑”的魔法区域,它本质是命令映射表,不自动加载环境或依赖。必须明确两点:脚本命令默认在项目根目录执行;PHP 类路径不会自动包含 vendor/autoload.php,除非你显式 require。
常见错误是直接写 "post-install-cmd": "MyDeployer::run" —— 这会报 Class 'MyDeployer' not found,因为 Composer 不会自动引入 autoloader。
- 正确做法:用
php -r或封装为独立 PHP 文件(如scripts/deploy.php),并在开头写require __DIR__ . '/vendor/autoload.php'; - 推荐方式:把逻辑写成 CLI 可调用的类,再通过
"post-install-cmd": "php scripts/deploy.php"调用 - 注意路径:所有相对路径都基于项目根目录,
__DIR__在脚本内指向的是scripts/目录,不是项目根目录
post-autoload-dump 和 post-install-cmd 的触发时机差异
这两个钩子常被混用,但行为完全不同:post-autoload-dump 在每次生成或更新 vendor/autoload.php 时触发(包括 composer install、composer update、甚至 composer dump-autoload);而 post-install-cmd 仅在 composer install 完成后触发一次,且只在 composer.lock 存在时才运行。
如果你的部署脚本依赖已安装的包(比如要用 symfony/console 构建交互式部署),必须选 post-install-cmd;如果只是刷新缓存或生成配置文件,post-autoload-dump 更轻量、更可靠。
-
post-install-cmd不会在 CI 环境中因--no-scripts被跳过,但需确认 CI 命令未加该参数 -
post-autoload-dump无法访问composer.lock中的精确版本信息,因为它不读取 lock 文件 - CI 流水线中建议优先用
post-install-cmd,避免因 autoload 变更意外触发部署
脚本中安全获取环境变量与敏感配置
别把 .env 文件内容硬编码进脚本,也别用 getenv('DEPLOY_TARGET') 依赖全局环境 —— 这些在不同部署环境(本地 / staging / prod)下极易出错。Composer 脚本本身不解析 .env,必须自己加载。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
推荐方案是复用项目已有的 dotenv 加载逻辑(如 vizualize/dotenv 或 Laravel 的 vlucas/phpdotenv),并在脚本开头统一初始化:
require __DIR__ . '/../vendor/autoload.php'; $dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..'); $dotenv->safeLoad(); // 不抛异常,缺失变量设为空字符串
- 用
$dotenv->required('DEPLOY_TARGET')->notEmpty()显式校验必要变量,失败时exit(1) - 避免在脚本中 echo 敏感值(如数据库密码),即使只是调试 —— CI 日志可能被归档
- 不要用
$_SERVER直接取值,某些 SAPI(如 php-cli -n)会清空它
如何让自定义脚本支持传参并兼容 Windows
Composer 默认不支持向脚本传递参数,比如 composer run deploy -- --force 是无效的。真正可行的方式只有两种:环境变量(推荐)、或在 composer.json 中预定义带参数的 script alias。
Windows 下最易踩坑的是路径分隔符和 shell 命令语法:rm -rf 不能用,cp 不存在,&& 行为也和 bash 不同。所以跨平台脚本必须避开 shell 命令,全用 PHP 内置函数实现。
- 定义带参 alias:
"deploy:prod": "DEPLOY_ENV=prod php scripts/deploy.php",然后在 PHP 中读getenv('DEPLOY_ENV') - 删除目录用
exec('rm -rf ...', $output, $return);在 Windows 上会失败,改用FilesystemIterator+unlink+rmdir - 不要依赖
shell_exec('git rev-parse HEAD'),改用exec('git rev-parse --short=8 HEAD 2>/dev/null', $out) && $commit = $out[0] ?? '',并检查$return === 0
复杂点不在语法,而在状态一致性 —— 比如部署中途失败,临时文件没清理,下次运行就可能覆盖错位置。每个脚本入口最好先做锁检查(如写 deploy.lock 文件)和清理前缀目录的习惯动作。










