Skip to content

插件机制概述

概述

RabbitMQ 插件系统是 RabbitMQ 提供的一种扩展机制,允许开发者通过安装和配置插件来增强 RabbitMQ 的功能。插件可以添加新的交换器类型、认证机制、管理界面、协议支持等功能。

核心知识点

插件系统架构

mermaid
graph TB
    subgraph RabbitMQ核心
        Core[核心服务]
        Broker[消息代理]
        Mnesia[数据存储]
    end

    subgraph 插件系统
        PM[插件管理器]
        Registry[插件注册表]
        Loader[插件加载器]
    end

    subgraph 插件类型
        Auth[认证插件]
        Exchange[交换器插件]
        Protocol[协议插件]
        Management[管理插件]
        Federation[联邦插件]
    end

    Core --> PM
    PM --> Registry
    PM --> Loader

    Loader --> Auth
    Loader --> Exchange
    Loader --> Protocol
    Loader --> Management
    Loader --> Federation

插件分类

类型说明代表插件
认证授权用户认证和权限控制rabbitmq_auth_backend_ldap
交换器扩展自定义交换器类型rabbitmq_consistent_hash_exchange
协议支持支持其他消息协议rabbitmq_mqtt, rabbitmq_stomp
管理监控管理界面和监控rabbitmq_management
消息处理消息处理增强rabbitmq_delayed_message_exchange
集群扩展集群和联邦功能rabbitmq_federation

插件生命周期

mermaid
sequenceDiagram
    participant CLI as 命令行工具
    participant PM as 插件管理器
    participant FS as 文件系统
    participant App as 应用程序

    Note over CLI: 1. 发现插件
    CLI->>PM: rabbitmq-plugins list
    PM->>FS: 扫描插件目录
    FS-->>PM: 返回插件列表
    PM-->>CLI: 显示可用插件

    Note over CLI: 2. 启用插件
    CLI->>PM: rabbitmq_plugins enable
    PM->>FS: 加载插件代码
    PM->>App: 注册插件
    App-->>PM: 插件已激活

    Note over CLI: 3. 禁用插件
    CLI->>PM: rabbitmq_plugins disable
    PM->>App: 注销插件
    App-->>PM: 插件已停用

PHP 代码示例

插件管理命令封装

php
<?php

class RabbitMQPluginManager
{
    private $rabbitmqctlPath = '/usr/sbin/rabbitmq-plugins';
    private $sudoEnabled = true;

    public function __construct($rabbitmqctlPath = null)
    {
        if ($rabbitmqctlPath) {
            $this->rabbitmqctlPath = $rabbitmqctlPath;
        }
    }

    public function listPlugins($enabledOnly = false)
    {
        $command = $this->buildCommand('list');

        if ($enabledOnly) {
            $command .= ' -e';
        }

        $output = $this->executeCommand($command);

        return $this->parsePluginList($output);
    }

    public function enablePlugin($pluginName)
    {
        $command = $this->buildCommand("enable {$pluginName}");

        $output = $this->executeCommand($command);

        echo "插件 {$pluginName} 已启用\n";

        return $output;
    }

    public function disablePlugin($pluginName)
    {
        $command = $this->buildCommand("disable {$pluginName}");

        $output = $this->executeCommand($command);

        echo "插件 {$pluginName} 已禁用\n";

        return $output;
    }

    public function isPluginEnabled($pluginName)
    {
        $plugins = $this->listPlugins(true);

        foreach ($plugins as $plugin) {
            if ($plugin['name'] === $pluginName) {
                return true;
            }
        }

        return false;
    }

    public function getPluginInfo($pluginName)
    {
        $plugins = $this->listPlugins();

        foreach ($plugins as $plugin) {
            if ($plugin['name'] === $pluginName) {
                return $plugin;
            }
        }

        return null;
    }

    private function buildCommand($action)
    {
        $command = $this->rabbitmqctlPath . ' ' . $action;

        if ($this->sudoEnabled && posix_getuid() !== 0) {
            $command = 'sudo ' . $command;
        }

        return $command;
    }

    private function executeCommand($command)
    {
        $output = [];
        $returnCode = 0;

        exec($command . ' 2>&1', $output, $returnCode);

        if ($returnCode !== 0) {
            throw new RuntimeException(
                "命令执行失败: " . implode("\n", $output)
            );
        }

        return $output;
    }

    private function parsePluginList(array $output)
    {
        $plugins = [];

        foreach ($output as $line) {
            if (preg_match('/^\[(.)\]\s+(\S+)\s+(.*)$/', $line, $matches)) {
                $plugins[] = [
                    'enabled' => $matches[1] === 'E' || $matches[1] === '*',
                    'name' => $matches[2],
                    'description' => trim($matches[3])
                ];
            }
        }

        return $plugins;
    }
}

插件状态监控

php
<?php

class PluginStatusMonitor
{
    private $pluginManager;
    private $requiredPlugins = [
        'rabbitmq_management',
        'rabbitmq_delayed_message_exchange',
        'rabbitmq_consistent_hash_exchange'
    ];

    public function __construct()
    {
        $this->pluginManager = new RabbitMQPluginManager();
    }

    public function checkPluginStatus()
    {
        $status = [];

        foreach ($this->requiredPlugins as $plugin) {
            $status[$plugin] = [
                'required' => true,
                'enabled' => $this->pluginManager->isPluginEnabled($plugin),
                'info' => $this->pluginManager->getPluginInfo($plugin)
            ];
        }

        return $status;
    }

    public function ensureRequiredPlugins()
    {
        $results = [];

        foreach ($this->requiredPlugins as $plugin) {
            if (!$this->pluginManager->isPluginEnabled($plugin)) {
                try {
                    $this->pluginManager->enablePlugin($plugin);
                    $results[$plugin] = 'enabled';
                } catch (Exception $e) {
                    $results[$plugin] = 'failed: ' . $e->getMessage();
                }
            } else {
                $results[$plugin] = 'already_enabled';
            }
        }

        return $results;
    }

    public function getMissingPlugins()
    {
        $missing = [];

        foreach ($this->requiredPlugins as $plugin) {
            if (!$this->pluginManager->isPluginEnabled($plugin)) {
                $missing[] = $plugin;
            }
        }

        return $missing;
    }
}

插件配置管理

php
<?php

class PluginConfigManager
{
    private $configPath = '/etc/rabbitmq/rabbitmq.conf';
    private $enabledPluginsPath = '/etc/rabbitmq/enabled_plugins';

    public function getPluginConfig($pluginName)
    {
        $config = $this->loadConfig();

        $pluginConfig = [];

        foreach ($config as $line) {
            if (strpos($line, $pluginName) !== false) {
                $pluginConfig[] = $line;
            }
        }

        return $pluginConfig;
    }

    public function setPluginConfig($pluginName, array $settings)
    {
        $config = $this->loadConfig();

        $newConfig = $this->updatePluginSettings($config, $pluginName, $settings);

        $this->saveConfig($newConfig);
    }

    public function getEnabledPlugins()
    {
        if (!file_exists($this->enabledPluginsPath)) {
            return [];
        }

        $content = file_get_contents($this->enabledPluginsPath);

        if (preg_match('/\[([^\]]+)\]/', $content, $matches)) {
            $plugins = explode(',', $matches[1]);

            return array_map(function ($plugin) {
                return trim($plugin, " \t\n\r\0\x0B'\"");
            }, $plugins);
        }

        return [];
    }

    public function addToEnabledPlugins($pluginName)
    {
        $plugins = $this->getEnabledPlugins();

        if (!in_array($pluginName, $plugins)) {
            $plugins[] = $pluginName;
            $this->saveEnabledPlugins($plugins);
        }
    }

    public function removeFromEnabledPlugins($pluginName)
    {
        $plugins = $this->getEnabledPlugins();

        $plugins = array_filter($plugins, function ($p) use ($pluginName) {
            return $p !== $pluginName;
        });

        $this->saveEnabledPlugins(array_values($plugins));
    }

    private function loadConfig()
    {
        if (!file_exists($this->configPath)) {
            return [];
        }

        return file($this->configPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    }

    private function saveConfig(array $config)
    {
        $content = implode("\n", $config) . "\n";

        file_put_contents($this->configPath, $content);
    }

    private function updatePluginSettings($config, $pluginName, $settings)
    {
        $prefix = $this->getConfigPrefix($pluginName);

        $newLines = [];

        foreach ($settings as $key => $value) {
            $newLines[] = "{$prefix}.{$key} = {$value}";
        }

        $updatedConfig = [];
        $inPluginSection = false;

        foreach ($config as $line) {
            if (strpos($line, $prefix) === 0) {
                if (!$inPluginSection) {
                    $updatedConfig = array_merge($updatedConfig, $newLines);
                    $inPluginSection = true;
                }
                continue;
            }

            $updatedConfig[] = $line;
        }

        if (!$inPluginSection) {
            $updatedConfig = array_merge($updatedConfig, $newLines);
        }

        return $updatedConfig;
    }

    private function getConfigPrefix($pluginName)
    {
        $prefixes = [
            'rabbitmq_management' => 'management',
            'rabbitmq_mqtt' => 'mqtt',
            'rabbitmq_stomp' => 'stomp',
            'rabbitmq_web_stomp' => 'web_stomp',
        ];

        return $prefixes[$pluginName] ?? str_replace('rabbitmq_', '', $pluginName);
    }

    private function saveEnabledPlugins(array $plugins)
    {
        $pluginsStr = implode(', ', array_map(function ($p) {
            return "'{$p}'";
        }, $plugins));

        $content = "[{$pluginsStr}].\n";

        file_put_contents($this->enabledPluginsPath, $content);
    }
}

插件依赖检查

php
<?php

class PluginDependencyChecker
{
    private $pluginManager;

    private $dependencies = [
        'rabbitmq_management' => [],
        'rabbitmq_management_agent' => [],
        'rabbitmq_web_dispatch' => [],
        'rabbitmq_federation' => ['rabbitmq_management'],
        'rabbitmq_federation_management' => ['rabbitmq_federation', 'rabbitmq_management'],
        'rabbitmq_shovel' => ['rabbitmq_management'],
        'rabbitmq_shovel_management' => ['rabbitmq_shovel', 'rabbitmq_management'],
        'rabbitmq_mqtt' => [],
        'rabbitmq_stomp' => [],
        'rabbitmq_web_stomp' => ['rabbitmq_stomp'],
        'rabbitmq_delayed_message_exchange' => [],
        'rabbitmq_consistent_hash_exchange' => [],
        'rabbitmq_tracing' => ['rabbitmq_management'],
        'rabbitmq_auth_backend_ldap' => [],
    ];

    public function __construct()
    {
        $this->pluginManager = new RabbitMQPluginManager();
    }

    public function checkDependencies($pluginName)
    {
        $dependencies = $this->dependencies[$pluginName] ?? [];

        $result = [
            'plugin' => $pluginName,
            'dependencies' => [],
            'all_satisfied' => true
        ];

        foreach ($dependencies as $dep) {
            $enabled = $this->pluginManager->isPluginEnabled($dep);

            $result['dependencies'][$dep] = [
                'enabled' => $enabled,
                'satisfied' => $enabled
            ];

            if (!$enabled) {
                $result['all_satisfied'] = false;
            }
        }

        return $result;
    }

    public function getMissingDependencies($pluginName)
    {
        $check = $this->checkDependencies($pluginName);

        $missing = [];

        foreach ($check['dependencies'] as $dep => $info) {
            if (!$info['satisfied']) {
                $missing[] = $dep;
            }
        }

        return $missing;
    }

    public function enableWithDependencies($pluginName)
    {
        $missing = $this->getMissingDependencies($pluginName);

        $results = [];

        foreach ($missing as $dep) {
            try {
                $this->pluginManager->enablePlugin($dep);
                $results[$dep] = 'enabled';
            } catch (Exception $e) {
                $results[$dep] = 'failed: ' . $e->getMessage();
            }
        }

        try {
            $this->pluginManager->enablePlugin($pluginName);
            $results[$pluginName] = 'enabled';
        } catch (Exception $e) {
            $results[$pluginName] = 'failed: ' . $e->getMessage();
        }

        return $results;
    }

    public function getDependents($pluginName)
    {
        $dependents = [];

        foreach ($this->dependencies as $plugin => $deps) {
            if (in_array($pluginName, $deps)) {
                $dependents[] = $plugin;
            }
        }

        return $dependents;
    }
}

实际应用场景

1. 自动化插件部署

php
<?php

class PluginDeployer
{
    private $pluginManager;
    private $dependencyChecker;
    private $configManager;

    private $deploymentConfig = [
        'management' => [
            'plugin' => 'rabbitmq_management',
            'config' => [
                'listener.port' => 15672,
                'listener.ip' => '0.0.0.0'
            ]
        ],
        'delayed_message' => [
            'plugin' => 'rabbitmq_delayed_message_exchange',
            'config' => []
        ],
        'mqtt' => [
            'plugin' => 'rabbitmq_mqtt',
            'config' => [
                'allow_anonymous' => false,
                'tcp_listeners' => 1883
            ]
        ]
    ];

    public function __construct()
    {
        $this->pluginManager = new RabbitMQPluginManager();
        $this->dependencyChecker = new PluginDependencyChecker();
        $this->configManager = new PluginConfigManager();
    }

    public function deploy(array $features)
    {
        $results = [];

        foreach ($features as $feature) {
            if (!isset($this->deploymentConfig[$feature])) {
                $results[$feature] = 'unknown_feature';
                continue;
            }

            $config = $this->deploymentConfig[$feature];

            try {
                $this->dependencyChecker->enableWithDependencies($config['plugin']);

                if (!empty($config['config'])) {
                    $this->configManager->setPluginConfig(
                        $config['plugin'],
                        $config['config']
                    );
                }

                $results[$feature] = 'deployed';
            } catch (Exception $e) {
                $results[$feature] = 'failed: ' . $e->getMessage();
            }
        }

        return $results;
    }

    public function undeploy(array $features)
    {
        $results = [];

        foreach ($features as $feature) {
            if (!isset($this->deploymentConfig[$feature])) {
                $results[$feature] = 'unknown_feature';
                continue;
            }

            $config = $this->deploymentConfig[$feature];

            $dependents = $this->dependencyChecker->getDependents($config['plugin']);

            $enabledDependents = array_filter($dependents, function ($dep) {
                return $this->pluginManager->isPluginEnabled($dep);
            });

            if (!empty($enabledDependents)) {
                $results[$feature] = 'has_dependents: ' . implode(', ', $enabledDependents);
                continue;
            }

            try {
                $this->pluginManager->disablePlugin($config['plugin']);
                $results[$feature] = 'undeployed';
            } catch (Exception $e) {
                $results[$feature] = 'failed: ' . $e->getMessage();
            }
        }

        return $results;
    }
}

2. 插件健康检查

php
<?php

class PluginHealthChecker
{
    private $pluginManager;
    private $apiClient;

    public function __construct()
    {
        $this->pluginManager = new RabbitMQPluginManager();
        $this->apiClient = new RabbitMQApiClient('localhost', 15672, 'guest', 'guest');
    }

    public function performHealthCheck()
    {
        $health = [
            'timestamp' => date('c'),
            'plugins' => [],
            'issues' => []
        ];

        $plugins = $this->pluginManager->listPlugins(true);

        foreach ($plugins as $plugin) {
            $pluginHealth = $this->checkPluginHealth($plugin);

            $health['plugins'][$plugin['name']] = $pluginHealth;

            if (!$pluginHealth['healthy']) {
                $health['issues'] = array_merge(
                    $health['issues'],
                    $pluginHealth['issues']
                );
            }
        }

        $health['overall_healthy'] = empty($health['issues']);

        return $health;
    }

    private function checkPluginHealth($plugin)
    {
        $health = [
            'name' => $plugin['name'],
            'healthy' => true,
            'issues' => []
        ];

        if ($plugin['name'] === 'rabbitmq_management') {
            if (!$this->checkManagementApi()) {
                $health['healthy'] = false;
                $health['issues'][] = 'Management API 不可访问';
            }
        }

        if ($plugin['name'] === 'rabbitmq_mqtt') {
            if (!$this->checkMqttPort()) {
                $health['healthy'] = false;
                $health['issues'][] = 'MQTT 端口不可访问';
            }
        }

        return $health;
    }

    private function checkManagementApi()
    {
        try {
            $response = $this->apiClient->get('/api/overview');
            return isset($response['rabbitmq_version']);
        } catch (Exception $e) {
            return false;
        }
    }

    private function checkMqttPort()
    {
        $socket = @fsockopen('localhost', 1883, $errno, $errstr, 5);

        if ($socket) {
            fclose($socket);
            return true;
        }

        return false;
    }
}

class RabbitMQApiClient
{
    private $host;
    private $port;
    private $user;
    private $password;

    public function __construct($host, $port, $user, $password)
    {
        $this->host = $host;
        $this->port = $port;
        $this->user = $user;
        $this->password = $password;
    }

    public function get($endpoint)
    {
        $url = "http://{$this->host}:{$this->port}{$endpoint}";

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERPWD, "{$this->user}:{$this->password}");
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            throw new RuntimeException("API 请求失败: HTTP {$httpCode}");
        }

        return json_decode($response, true);
    }
}

3. 插件配置验证

php
<?php

class PluginConfigValidator
{
    private $rules = [
        'rabbitmq_management' => [
            'listener.port' => ['type' => 'integer', 'min' => 1, 'max' => 65535],
            'listener.ip' => ['type' => 'ip'],
        ],
        'rabbitmq_mqtt' => [
            'allow_anonymous' => ['type' => 'boolean'],
            'tcp_listeners' => ['type' => 'integer', 'min' => 1, 'max' => 65535],
        ]
    ];

    public function validate($pluginName, array $config)
    {
        $errors = [];

        if (!isset($this->rules[$pluginName])) {
            return ['valid' => true, 'errors' => []];
        }

        $rules = $this->rules[$pluginName];

        foreach ($config as $key => $value) {
            if (!isset($rules[$key])) {
                $errors[$key] = "未知配置项: {$key}";
                continue;
            }

            $rule = $rules[$key];

            $error = $this->validateValue($key, $value, $rule);

            if ($error) {
                $errors[$key] = $error;
            }
        }

        return [
            'valid' => empty($errors),
            'errors' => $errors
        ];
    }

    private function validateValue($key, $value, $rule)
    {
        switch ($rule['type']) {
            case 'integer':
                if (!is_int($value)) {
                    return "{$key} 必须是整数";
                }
                if (isset($rule['min']) && $value < $rule['min']) {
                    return "{$key} 不能小于 {$rule['min']}";
                }
                if (isset($rule['max']) && $value > $rule['max']) {
                    return "{$key} 不能大于 {$rule['max']}";
                }
                break;

            case 'boolean':
                if (!is_bool($value)) {
                    return "{$key} 必须是布尔值";
                }
                break;

            case 'ip':
                if (!filter_var($value, FILTER_VALIDATE_IP)) {
                    return "{$key} 必须是有效的 IP 地址";
                }
                break;

            case 'string':
                if (!is_string($value)) {
                    return "{$key} 必须是字符串";
                }
                break;
        }

        return null;
    }
}

常见问题与解决方案

问题 1:插件启用失败

原因:依赖插件未启用或版本不兼容

解决方案

php
<?php

class PluginEnablementHandler
{
    private $pluginManager;
    private $dependencyChecker;

    public function safeEnable($pluginName)
    {
        $result = [
            'plugin' => $pluginName,
            'success' => false,
            'actions' => []
        ];

        $check = $this->dependencyChecker->checkDependencies($pluginName);

        if (!$check['all_satisfied']) {
            foreach ($check['dependencies'] as $dep => $info) {
                if (!$info['enabled']) {
                    try {
                        $this->pluginManager->enablePlugin($dep);
                        $result['actions'][] = "已启用依赖插件: {$dep}";
                    } catch (Exception $e) {
                        $result['actions'][] = "启用依赖插件失败: {$dep} - " . $e->getMessage();
                        return $result;
                    }
                }
            }
        }

        try {
            $this->pluginManager->enablePlugin($pluginName);
            $result['success'] = true;
            $result['actions'][] = "已启用插件: {$pluginName}";
        } catch (Exception $e) {
            $result['actions'][] = "启用插件失败: " . $e->getMessage();
        }

        return $result;
    }
}

问题 2:插件配置不生效

原因:配置文件格式错误或未重启服务

解决方案

php
<?php

class PluginConfigApplier
{
    public function applyConfig($pluginName, array $config)
    {
        $configManager = new PluginConfigManager();
        $validator = new PluginConfigValidator();

        $validation = $validator->validate($pluginName, $config);

        if (!$validation['valid']) {
            throw new InvalidArgumentException(
                "配置验证失败: " . json_encode($validation['errors'])
            );
        }

        $configManager->setPluginConfig($pluginName, $config);

        $this->restartRabbitMQ();
    }

    private function restartRabbitMQ()
    {
        $commands = [
            'systemctl restart rabbitmq-server',
            'service rabbitmq-server restart',
            '/etc/init.d/rabbitmq-server restart'
        ];

        foreach ($commands as $command) {
            exec($command . ' 2>&1', $output, $returnCode);

            if ($returnCode === 0) {
                echo "RabbitMQ 服务已重启\n";
                return;
            }
        }

        throw new RuntimeException("无法重启 RabbitMQ 服务");
    }
}

最佳实践建议

1. 插件版本管理

php
<?php

class PluginVersionManager
{
    private $versionFile = '/var/lib/rabbitmq/plugin_versions.json';

    public function recordVersions()
    {
        $pluginManager = new RabbitMQPluginManager();
        $plugins = $pluginManager->listPlugins(true);

        $versions = [];

        foreach ($plugins as $plugin) {
            $versions[$plugin['name']] = [
                'version' => $this->extractVersion($plugin),
                'enabled_at' => date('c')
            ];
        }

        file_put_contents($this->versionFile, json_encode($versions, JSON_PRETTY_PRINT));
    }

    public function getRecordedVersions()
    {
        if (!file_exists($this->versionFile)) {
            return [];
        }

        return json_decode(file_get_contents($this->versionFile), true);
    }

    public function detectVersionChanges()
    {
        $current = $this->getCurrentVersions();
        $recorded = $this->getRecordedVersions();

        $changes = [];

        foreach ($current as $name => $info) {
            if (!isset($recorded[$name])) {
                $changes[$name] = 'new_plugin';
            } elseif ($recorded[$name]['version'] !== $info['version']) {
                $changes[$name] = [
                    'old_version' => $recorded[$name]['version'],
                    'new_version' => $info['version']
                ];
            }
        }

        return $changes;
    }

    private function getCurrentVersions()
    {
        $pluginManager = new RabbitMQPluginManager();
        $plugins = $pluginManager->listPlugins(true);

        $versions = [];

        foreach ($plugins as $plugin) {
            $versions[$plugin['name']] = [
                'version' => $this->extractVersion($plugin)
            ];
        }

        return $versions;
    }

    private function extractVersion($plugin)
    {
        if (preg_match('/(\d+\.\d+\.\d+)/', $plugin['description'], $matches)) {
            return $matches[1];
        }

        return 'unknown';
    }
}

2. 插件备份与恢复

php
<?php

class PluginBackupManager
{
    private $backupDir = '/var/backups/rabbitmq/plugins';

    public function backup()
    {
        if (!is_dir($this->backupDir)) {
            mkdir($this->backupDir, 0755, true);
        }

        $timestamp = date('Ymd_His');
        $backupFile = "{$this->backupDir}/plugins_{$timestamp}.json";

        $pluginManager = new RabbitMQPluginManager();
        $plugins = $pluginManager->listPlugins(true);

        $backup = [
            'timestamp' => date('c'),
            'plugins' => []
        ];

        foreach ($plugins as $plugin) {
            $backup['plugins'][] = $plugin['name'];
        }

        $configManager = new PluginConfigManager();
        $backup['enabled_plugins'] = $configManager->getEnabledPlugins();

        file_put_contents($backupFile, json_encode($backup, JSON_PRETTY_PRINT));

        echo "插件配置已备份到: {$backupFile}\n";

        return $backupFile;
    }

    public function restore($backupFile)
    {
        if (!file_exists($backupFile)) {
            throw new RuntimeException("备份文件不存在: {$backupFile}");
        }

        $backup = json_decode(file_get_contents($backupFile), true);

        $pluginManager = new RabbitMQPluginManager();

        foreach ($backup['plugins'] as $pluginName) {
            try {
                $pluginManager->enablePlugin($pluginName);
                echo "已恢复插件: {$pluginName}\n";
            } catch (Exception $e) {
                echo "恢复插件失败: {$pluginName} - " . $e->getMessage() . "\n";
            }
        }
    }

    public function listBackups()
    {
        $backups = [];

        $files = glob("{$this->backupDir}/plugins_*.json");

        foreach ($files as $file) {
            $backups[] = [
                'file' => basename($file),
                'path' => $file,
                'size' => filesize($file),
                'modified' => filemtime($file)
            ];
        }

        usort($backups, function ($a, $b) {
            return $b['modified'] - $a['modified'];
        });

        return $backups;
    }
}

相关链接