pre-install是composer 2.2+唯一合法的安装前钩子,需写在composer.json的scripts下,值为数组,如["echo 'checking env'", "mybuild::beforeinstall"],此时vendor为空,不可require autoload.php。

pre-install 钩子怎么写才真正生效
Composer 2.2+ 里 pre-install-cmd 已被彻底移除,不是配置错,是它根本不存在了。你现在能用的唯一合法“安装前”钩子是 pre-install(注意:没有 -cmd 后缀)。
它触发时机很关键:依赖已解析完成、composer.lock 校验通过,但所有包都还没下载,vendor/ 目录仍是空的或不完整。
-
pre-install支持 shell 命令和 PHP 回调,例如:"pre-install": ["echo 'checking env'", "MyBuild::beforeInstall"] - 此时
require 'vendor/autoload.php'会失败——vendor/可能为空,第三方类不可用 - 别指望调用
phpstan或phpcs这类 CLI 工具,它们还没装进PATH - 误配
pre-install-cmd、preInstall、on-pre-install全部静默忽略,不报错也不执行
post-install-cmd 为什么经常不执行
post-install-cmd 在首次运行 composer install 时大概率不会触发,因为此时 vendor/autoload.php 尚未生成,Composer 无法加载任何类或脚本。
它只在 vendor/ 已存在、autoloader 可用的前提下才执行——换句话说,它更适合“二次安装”或 CI 中的增量构建场景。
- 想确保逻辑稳定执行,优先用
post-update-cmd:每次composer update或非首次composer install都会跑,且 autoloader 已就绪 - PHP 方法必须声明为
public static,且所在类需在autoload-dev下注册(不是autoload) - 别在方法里
require 'vendor/autoload.php'——Composer 已加载好,重复 require 可能破坏命名空间解析 - 验证是否生效:加
-v参数运行命令,看到Executing script MyAppScripts::onUpdate才算到位
自定义 Installer 必须绕开的三个坑
想真正干预包怎么装、装哪、装不装,光监听事件远远不够——你得接管 Installer,否则所有逻辑都在文件落盘之后才开始,已经晚了。
常见错误是只绑 post-package-install 事件,结果包早解压完了,路径、autoload、签名校验全没法改。
-
supports()返回false?不是代码错,是目标包的composer.json里"type"字段没严格匹配你插件声明的类型(比如写成"my-module",但包里是"my_module"或大小写不一致) -
getInstallPath()报InvalidArgumentException?返回值不能以/开头,也不能是绝对路径;正确写法如:return 'plugins/' . $package->getPrettyName(); -
install()执行完目录还是空?别直接用copy()或file_put_contents(),必须用$filesystem实例操作——它是 Composer 内部统一的文件入口,处理跨平台和权限更可靠
allow-plugins 白名单不是可选项
从 Composer 2.2 起,所有插件默认被禁用。即使插件包已 require、composer show 能看到、autoload 和 extra.class 全对,只要没进白名单,activate() 就永远不会被调用。
报错信息很直白:Plugin myvendor/myplugin is not allowed to be loaded,但很多人卡在这儿反复检查插件代码,却忘了看根项目的 composer.json。
- 必须在项目根
composer.json的config.allow-plugins下显式放行,例如:"config": { "allow-plugins": { "myvendor/myplugin": true } } - 设为
true是全局允许,但生产环境建议精确到包名,避免第三方插件意外激活 - 如果插件要提供自定义命令(如
composer my:build),还得多一步:插件主类必须实现CommandProviderInterface,且getCommands()返回的是继承自BaseCommand的实例,不是 Symfony 的Command
最常被忽略的其实是时机判断:你以为的“安装前”,可能已经是 Composer 内部流程的中后段;你以为的“可用类”,其实还没 autoload;你以为的“路径安全”,其实正斜杠和反斜杠在不同环节会被不同方式处理。插件机制不是胶水层,它是嵌入 Composer 生命周期的手术刀——下刀位置错了,再锋利也没用。











