composer插件下载文件必须在post-autoload-dump等安装完成事件中执行,通过getcomposer()->getconfig()->get('vendor-dir')获取路径,配合超时设置、sha256校验及目录创建确保安全可靠。

Composer 插件里不能直接用 file_get_contents() 或 curl 下载文件到项目目录——它没有运行时的「当前项目根路径」上下文,且下载行为必须绑定到 Composer 的事件生命周期,否则会失败或路径错乱。
为什么 PluginInterface::activate() 里下载会失败
插件激活时,Composer 尚未解析完 composer.json,$event->getComposer()->getPackage() 可能为空,getInstallationManager() 也未就绪。此时调用 file_put_contents() 写入 vendor/ 或项目根目录,极大概率写到错误位置(比如插件自己的 vendor/),甚至因权限被拒绝。
- 下载逻辑必须放在
post-autoload-dump或post-install-cmd这类「安装完成、路径已确定」的事件中 - 获取目标路径必须通过
$event->getComposer()->getConfig()->get('vendor-dir')或$event->getComposer()->getPackage()->getAutoload()['psr-4']等 Composer 原生 API,而非__DIR__或getcwd() - 若需写入项目根目录(如
.env.example),应使用$event->getComposer()->getPackage()->getExtra()['your-plugin']['target-dir'] ?? getcwd()显式声明,避免硬编码
post-install-cmd 和 post-update-cmd 的关键区别
这两个事件都可用于触发下载,但行为差异直接影响文件是否被重复覆盖或遗漏:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
-
post-install-cmd:仅在首次composer install时触发,适合下载一次性的模板文件(如config/example.php) -
post-update-cmd:每次composer update都执行,若没加版本比对逻辑,会导致文件反复重写,覆盖用户已修改的内容 - 更稳妥的做法是监听
post-autoload-dump—— 它在每次 autoloader 重建时触发(install/update 都包含),且此时$event->getComposer()->getPackage()已完整加载,可读取extra中定义的版本号做增量判断
下载文件时如何避免超时和校验缺失
Composer 默认不设 HTTP 超时,插件中若用 file_get_contents('https://...'),遇到网络抖动会卡住整个安装流程。同时,缺少哈希校验会让文件完整性无法保障。
- 必须用
stream_context_create(['http' => ['timeout' => 30]])包裹file_get_contents(),或改用cURL并显式设置CURLOPT_TIMEOUT - 推荐从包的
extra字段读取预计算的sha256值:$hash = $package->getExtra()['your-plugin']['file-sha256'] ?? null;,下载后用hash_file('sha256', $target)校验 - 若校验失败,应抛出
RuntimeException并附带Download failed: checksum mismatch,让 Composer 中断流程,而不是静默覆盖
写入文件前必须检查目标目录是否存在且可写
插件无法假设用户环境已创建好 resources/ 或 config/ 目录。直接 file_put_contents('config/app.php', ...) 在某些部署环境下会因目录不存在而报 Warning: file_put_contents(config/app.php): failed to open stream。
- 用
is_dir()+mkdir($dir, 0755, true)创建嵌套目录,true参数不可省略 - 写入前用
is_writable($dir)检查权限,Windows 下尤其容易因 UAC 或只读属性失败 - 建议将目标路径记录到日志:
$io->write("Writing to <info>{$target}</info>");,方便用户排查路径问题
最常被忽略的是事件时机与路径解析的耦合性——不是「能不能下」,而是「下到哪、什么时候下、下错了怎么办」。一旦跳过 post-autoload-dump 的上下文准备,或绕过 getComposer()->getConfig() 直接拼路径,后续所有校验和容错都失去意义。










