You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优雅对比Composer包当前版本与新版本的文件?

解决Composer插件中对比包文件变更的优雅方案

你可以直接复用Composer内置的核心API来获取包的文件信息,无需依赖临时目录安装的方式,完美适配私有仓库认证场景,具体实现如下:

核心思路

利用Composer的PackageInterface实例获取包的分发/源码地址,通过内置的DownloadManager下载包内容到临时目录,再遍历文件计算哈希值。这种方式会自动复用当前Composer环境的认证配置(包括私有仓库的token、密钥等),解决私有仓库访问问题。

完整代码实现

use Composer\Composer;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Installer\PackageEvent;
use Composer\Installer\PackageEvents;
use Composer\Plugin\PluginInterface;
use Composer\Downloader\DownloadManager;
use Composer\Package\PackageInterface;
use Composer\Util\Filesystem;

class Plugin implements PluginInterface, EventSubscriberInterface
{
    private Composer $composer;
    private DownloadManager $downloadManager;
    private Filesystem $filesystem;

    public function activate(Composer $composer, \Composer\IO\IOInterface $io): void
    {
        $this->composer = $composer;
        $this->downloadManager = $composer->getDownloadManager();
        $this->filesystem = new Filesystem();
    }

    public static function getSubscribedEvents(): array
    {
        return [
            PackageEvents::POST_PACKAGE_UPDATE => 'handlePackageUpdate',
        ];
    }

    public function handlePackageUpdate(PackageEvent $event): void
    {
        $operation = $event->getOperation();
        assert($operation instanceof UpdateOperation);

        $initialFileHashes = $this->getPackageFileHashes($operation->getInitialPackage());
        $targetFileHashes = $this->getPackageFileHashes($operation->getTargetPackage());

        // 对比文件变更
        $addedFiles = array_diff_key($targetFileHashes, $initialFileHashes);
        $removedFiles = array_diff_key($initialFileHashes, $targetFileHashes);
        $changedFiles = array_filter($targetFileHashes, function ($hash, $file) use ($initialFileHashes) {
            return isset($initialFileHashes[$file]) && $initialFileHashes[$file] !== $hash;
        }, ARRAY_FILTER_USE_BOTH);

        // 有变更时执行自定义逻辑
        if (!empty($addedFiles) || !empty($removedFiles) || !empty($changedFiles)) {
            // 替换为你的业务代码,比如重新生成项目文件
            echo "Package " . $operation->getTargetPackage()->getName() . " has file changes\n";
        }
    }

    private function getPackageFileHashes(PackageInterface $package): array
    {
        $tmpDir = $this->filesystem->tempDir(sys_get_temp_dir(), 'composer-plugin-');
        $fileHashes = [];

        try {
            // 直接下载包到临时目录,自动复用当前认证配置
            $this->downloadManager->download($package, $tmpDir);

            // 遍历文件计算哈希
            $iterator = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($tmpDir, \RecursiveDirectoryIterator::SKIP_DOTS),
                \RecursiveIteratorIterator::LEAVES_ONLY
            );

            foreach ($iterator as $file) {
                if ($file->isFile()) {
                    $relativePath = str_replace($tmpDir . DIRECTORY_SEPARATOR, '', $file->getPathname());
                    $fileHashes[$relativePath] = md5_file($file->getPathname());
                }
            }
        } catch (\Exception $e) {
            error_log("Failed to process package " . $package->getName() . ": " . $e->getMessage());
        } finally {
            // 自动清理临时目录
            $this->filesystem->removeDirectory($tmpDir);
        }

        return $fileHashes;
    }

    public function deactivate(Composer $composer, \Composer\IO\IOInterface $io): void {}
    public function uninstall(Composer $composer, \Composer\IO\IOInterface $io): void {}
}

关键优势

  • 原生适配私有仓库:无需额外配置,直接复用当前Composer的私有仓库认证信息
  • 性能提升:跳过完整的Composer安装流程,仅下载包归档文件,速度更快
  • 资源友好:临时目录自动清理,避免残留文件占用空间
  • 稳定性更高:依赖Composer官方API,避免shell_exec带来的环境兼容性问题

注意事项

  • 确保插件有创建、删除临时目录的权限
  • 可根据需求优化哈希计算逻辑,比如只监控特定目录或文件类型
  • 建议捕获下载过程中的异常,避免插件崩溃影响主流程

内容的提问来源于stack exchange,提问作者halloei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 11:12:09