不能直接触发。composer 的 run-script 命令仅执行 scripts 中定义的脚本,与 install/update 自动触发的钩子(如 post-install-cmd)机制分离;二者虽共用脚本定义,但触发时机和上下文完全不同。

Composer run-script 能不能触发钩子函数?
不能直接触发。Composer 的 run-script 命令只执行 scripts 配置里定义的脚本,和 install/update 过程中自动触发的钩子(如 post-install-cmd)是两套机制——它们共用脚本定义,但触发时机和上下文完全不同。
怎么让 run-script 手动“模拟”钩子行为?
把钩子逻辑抽出来,作为独立脚本名复用。比如你写了 "post-install-cmd": "MyScript::buildAssets",那就不能靠 composer run-script post-install-cmd 来调,因为 Composer 会把它当普通命令去 shell 执行,而 MyScript::buildAssets 是 PHP 回调,不是可执行命令。
正确做法是:
- 在
composer.json的scripts里显式定义一个同名或别名脚本,指向可执行形式:"post-install-cmd": "php -r "(new My\Script)->buildAssets();"" - 或者更干净:写个封装命令类,实现
__invoke(),并在scripts中用类名+方法引用:"post-install-cmd": "MyScript::buildAssets"(前提是该类已自动加载且 Composer 支持回调语法) - 确保该脚本在
autoload-dev或主autoload中注册,否则运行时报class not found
为什么 run-script 后脚本不生效或报错?
常见原因集中在环境与作用域错位:
-
run-script默认不加载vendor/autoload.php—— 如果脚本依赖第三方包,必须手动 require,或改用php -d auto_prepend_file=vendor/autoload.php - 钩子函数里常用
$Event->getComposer()或$event->getIO(),但run-script不传$event对象,直接调用会报undefined variable: event - 路径相关操作容易出错:钩子中
getInstallPath()返回的是包安装目录,而run-script当前工作目录可能是项目根,也可能是子模块,__DIR__行为不稳定
推荐的最小可行方案
不要试图“复用钩子定义”,而是明确区分用途:
- 钩子函数只用于 Composer 生命周期(install/update),专注副作用控制(如生成 autoload、清理缓存)
- 需要手动触发的功能,单独定义 script,例如:
"build": "php build.php",然后把逻辑写进build.php,里面可以安全 requirevendor/autoload.php,也能自由访问Composer实例(通过Factory::create()) - 如果真要共享逻辑,把核心代码提到独立类方法里,钩子函数和手动脚本都调它,而不是反过来
最易被忽略的一点:Composer 的脚本执行环境默认关闭了 display_errors,报错静默。调试时加一句 ini_set('display_errors', 1);,或者用 composer run-script xxx -v 看完整输出。