mirror of
https://github.com/cedar2025/Xboard.git
synced 2025-02-13 03:58:13 -05:00
chore(plugin): Refactor plugin architecture and improve code quality
This commit is contained in:
parent
0f80ab8d5f
commit
ebd5c09145
@ -40,9 +40,10 @@ class PluginController extends Controller
|
|||||||
$configFile = $directory . '/config.json';
|
$configFile = $directory . '/config.json';
|
||||||
if (File::exists($configFile)) {
|
if (File::exists($configFile)) {
|
||||||
$config = json_decode(File::get($configFile), true);
|
$config = json_decode(File::get($configFile), true);
|
||||||
$installed = isset($installedPlugins[$pluginName]);
|
$code = $config['code'];
|
||||||
|
$installed = isset($installedPlugins[$code]);
|
||||||
// 使用配置服务获取配置
|
// 使用配置服务获取配置
|
||||||
$pluginConfig = $installed ? $this->configService->getConfig($pluginName) : ($config['config'] ?? []);
|
$pluginConfig = $installed ? $this->configService->getConfig($code) : ($config['config'] ?? []);
|
||||||
$plugins[] = [
|
$plugins[] = [
|
||||||
'code' => $config['code'],
|
'code' => $config['code'],
|
||||||
'name' => $config['name'],
|
'name' => $config['name'],
|
||||||
@ -50,7 +51,7 @@ class PluginController extends Controller
|
|||||||
'description' => $config['description'],
|
'description' => $config['description'],
|
||||||
'author' => $config['author'],
|
'author' => $config['author'],
|
||||||
'is_installed' => $installed,
|
'is_installed' => $installed,
|
||||||
'is_enabled' => $installed ? $installedPlugins[$pluginName]['is_enabled'] : false,
|
'is_enabled' => $installed ? $installedPlugins[$code]['is_enabled'] : false,
|
||||||
'config' => $pluginConfig,
|
'config' => $pluginConfig,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ namespace App\Services\Plugin;
|
|||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
abstract class AbstractPlugin
|
abstract class AbstractPlugin
|
||||||
@ -12,14 +13,40 @@ abstract class AbstractPlugin
|
|||||||
protected array $config = [];
|
protected array $config = [];
|
||||||
protected string $basePath;
|
protected string $basePath;
|
||||||
protected string $pluginCode;
|
protected string $pluginCode;
|
||||||
|
protected string $namespace;
|
||||||
|
|
||||||
public function __construct($pluginCode)
|
public function __construct(string $pluginCode)
|
||||||
{
|
{
|
||||||
$this->pluginCode = $pluginCode;
|
$this->pluginCode = $pluginCode;
|
||||||
|
$this->namespace = 'Plugin\\' . Str::studly($pluginCode);
|
||||||
$reflection = new \ReflectionClass($this);
|
$reflection = new \ReflectionClass($this);
|
||||||
$this->basePath = dirname($reflection->getFileName());
|
$this->basePath = dirname($reflection->getFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件代码
|
||||||
|
*/
|
||||||
|
public function getPluginCode(): string
|
||||||
|
{
|
||||||
|
return $this->pluginCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件命名空间
|
||||||
|
*/
|
||||||
|
public function getNamespace(): string
|
||||||
|
{
|
||||||
|
return $this->namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件基础路径
|
||||||
|
*/
|
||||||
|
public function getBasePath(): string
|
||||||
|
{
|
||||||
|
return $this->basePath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置配置
|
* 设置配置
|
||||||
*/
|
*/
|
||||||
@ -36,6 +63,14 @@ abstract class AbstractPlugin
|
|||||||
return $this->config;
|
return $this->config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取视图
|
||||||
|
*/
|
||||||
|
protected function view(string $view, array $data = [], array $mergeData = []): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view(Str::studly($this->pluginCode) . '::' . $view, $data, $mergeData);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册动作钩子监听器
|
* 注册动作钩子监听器
|
||||||
*/
|
*/
|
||||||
@ -70,4 +105,76 @@ abstract class AbstractPlugin
|
|||||||
{
|
{
|
||||||
HookManager::intercept($response);
|
HookManager::intercept($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件启动时调用
|
||||||
|
*/
|
||||||
|
public function boot(): void
|
||||||
|
{
|
||||||
|
// 插件启动时的初始化逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件安装时调用
|
||||||
|
*/
|
||||||
|
public function install(): void
|
||||||
|
{
|
||||||
|
// 插件安装时的初始化逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件卸载时调用
|
||||||
|
*/
|
||||||
|
public function uninstall(): void
|
||||||
|
{
|
||||||
|
// 插件卸载时的清理逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件更新时调用
|
||||||
|
*/
|
||||||
|
public function update(string $oldVersion, string $newVersion): void
|
||||||
|
{
|
||||||
|
// 插件更新时的迁移逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件资源URL
|
||||||
|
*/
|
||||||
|
protected function asset(string $path): string
|
||||||
|
{
|
||||||
|
return asset('plugins/' . $this->pluginCode . '/' . ltrim($path, '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件配置项
|
||||||
|
*/
|
||||||
|
protected function getConfigValue(string $key, $default = null)
|
||||||
|
{
|
||||||
|
return $this->config[$key] ?? $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件数据库迁移路径
|
||||||
|
*/
|
||||||
|
protected function getMigrationsPath(): string
|
||||||
|
{
|
||||||
|
return $this->basePath . '/database/migrations';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件视图路径
|
||||||
|
*/
|
||||||
|
protected function getViewsPath(): string
|
||||||
|
{
|
||||||
|
return $this->basePath . '/resources/views';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件资源路径
|
||||||
|
*/
|
||||||
|
protected function getAssetsPath(): string
|
||||||
|
{
|
||||||
|
return $this->basePath . '/resources/assets';
|
||||||
|
}
|
||||||
}
|
}
|
@ -7,6 +7,13 @@ use Illuminate\Support\Facades\File;
|
|||||||
|
|
||||||
class PluginConfigService
|
class PluginConfigService
|
||||||
{
|
{
|
||||||
|
protected $pluginManager;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pluginManager = app(PluginManager::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取插件配置
|
* 获取插件配置
|
||||||
*
|
*
|
||||||
@ -73,7 +80,7 @@ class PluginConfigService
|
|||||||
*/
|
*/
|
||||||
protected function getDefaultConfig(string $pluginCode): array
|
protected function getDefaultConfig(string $pluginCode): array
|
||||||
{
|
{
|
||||||
$configFile = base_path("plugins/{$pluginCode}/config.json");
|
$configFile = $this->pluginManager->getPluginPath($pluginCode) . '/config.json';
|
||||||
if (!File::exists($configFile)) {
|
if (!File::exists($configFile)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -88,7 +95,7 @@ class PluginConfigService
|
|||||||
* @param string $pluginCode
|
* @param string $pluginCode
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function getDbConfig(string $pluginCode): array
|
public function getDbConfig(string $pluginCode): array
|
||||||
{
|
{
|
||||||
$plugin = Plugin::query()
|
$plugin = Plugin::query()
|
||||||
->where('code', $pluginCode)
|
->where('code', $pluginCode)
|
||||||
|
@ -6,54 +6,248 @@ use App\Models\Plugin;
|
|||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Finder\Finder;
|
||||||
|
|
||||||
class PluginManager
|
class PluginManager
|
||||||
{
|
{
|
||||||
protected string $pluginPath;
|
protected string $pluginPath;
|
||||||
|
protected array $loadedPlugins = [];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->pluginPath = base_path('plugins');
|
$this->pluginPath = base_path('plugins');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件的命名空间
|
||||||
|
*/
|
||||||
|
public function getPluginNamespace(string $pluginCode): string
|
||||||
|
{
|
||||||
|
return 'Plugin\\' . Str::studly($pluginCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件的基础路径
|
||||||
|
*/
|
||||||
|
public function getPluginPath(string $pluginCode): string
|
||||||
|
{
|
||||||
|
return $this->pluginPath . '/' . Str::studly($pluginCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载插件类
|
||||||
|
*/
|
||||||
|
protected function loadPlugin(string $pluginCode)
|
||||||
|
{
|
||||||
|
if (isset($this->loadedPlugins[$pluginCode])) {
|
||||||
|
return $this->loadedPlugins[$pluginCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
$pluginClass = $this->getPluginNamespace($pluginCode) . '\\Plugin';
|
||||||
|
|
||||||
|
if (!class_exists($pluginClass)) {
|
||||||
|
$pluginFile = $this->getPluginPath($pluginCode) . '/Plugin.php';
|
||||||
|
if (!File::exists($pluginFile)) {
|
||||||
|
throw new \Exception("Plugin class file not found: {$pluginFile}");
|
||||||
|
}
|
||||||
|
require_once $pluginFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!class_exists($pluginClass)) {
|
||||||
|
throw new \Exception("Plugin class not found: {$pluginClass}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$plugin = new $pluginClass($pluginCode);
|
||||||
|
$this->loadedPlugins[$pluginCode] = $plugin;
|
||||||
|
|
||||||
|
return $plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册插件的服务提供者
|
||||||
|
*/
|
||||||
|
protected function registerServiceProvider(string $pluginCode): void
|
||||||
|
{
|
||||||
|
$providerClass = $this->getPluginNamespace($pluginCode) . '\\Providers\\PluginServiceProvider';
|
||||||
|
|
||||||
|
if (class_exists($providerClass)) {
|
||||||
|
app()->register($providerClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载插件的路由
|
||||||
|
*/
|
||||||
|
protected function loadRoutes(string $pluginCode): void
|
||||||
|
{
|
||||||
|
$routesPath = $this->getPluginPath($pluginCode) . '/routes';
|
||||||
|
if (File::exists($routesPath)) {
|
||||||
|
$files = ['web.php', 'api.php'];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$routeFile = $routesPath . '/' . $file;
|
||||||
|
if (File::exists($routeFile)) {
|
||||||
|
Route::middleware('web')
|
||||||
|
->namespace($this->getPluginNamespace($pluginCode) . '\\Controllers')
|
||||||
|
->group(function () use ($routeFile) {
|
||||||
|
require $routeFile;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载插件的视图
|
||||||
|
*/
|
||||||
|
protected function loadViews(string $pluginCode): void
|
||||||
|
{
|
||||||
|
$viewsPath = $this->getPluginPath($pluginCode) . '/resources/views';
|
||||||
|
|
||||||
|
if (File::exists($viewsPath)) {
|
||||||
|
View::addNamespace(Str::studly($pluginCode), $viewsPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 安装插件
|
* 安装插件
|
||||||
*/
|
*/
|
||||||
public function install(string $pluginCode): bool
|
public function install(string $pluginCode): bool
|
||||||
{
|
{
|
||||||
$configFile = $this->pluginPath . '/' . $pluginCode . '/config.json';
|
DB::beginTransaction();
|
||||||
|
try {
|
||||||
|
$configFile = $this->getPluginPath($pluginCode) . '/config.json';
|
||||||
|
|
||||||
if (!File::exists($configFile)) {
|
if (!File::exists($configFile)) {
|
||||||
throw new \Exception('Plugin config file not found');
|
throw new \Exception('Plugin config file not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = json_decode(File::get($configFile), true);
|
||||||
|
if (!$this->validateConfig($config)) {
|
||||||
|
throw new \Exception('Invalid plugin config');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查插件是否已安装
|
||||||
|
if (Plugin::where('code', $pluginCode)->exists()) {
|
||||||
|
throw new \Exception('Plugin already installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查依赖
|
||||||
|
if (!$this->checkDependencies($config['require'] ?? [])) {
|
||||||
|
throw new \Exception('Dependencies not satisfied');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行数据库迁移
|
||||||
|
$this->runMigrations($pluginCode);
|
||||||
|
|
||||||
|
// 提取配置默认值
|
||||||
|
$defaultValues = $this->extractDefaultConfig($config);
|
||||||
|
|
||||||
|
// 创建插件实例
|
||||||
|
$plugin = $this->loadPlugin($pluginCode);
|
||||||
|
|
||||||
|
// 注册到数据库
|
||||||
|
$dbPlugin = Plugin::create([
|
||||||
|
'code' => $pluginCode,
|
||||||
|
'name' => $config['name'],
|
||||||
|
'version' => $config['version'],
|
||||||
|
'is_enabled' => false,
|
||||||
|
'config' => json_encode($defaultValues),
|
||||||
|
'installed_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 运行插件安装方法
|
||||||
|
if (method_exists($plugin, 'install')) {
|
||||||
|
$plugin->install();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布插件资源
|
||||||
|
$this->publishAssets($pluginCode);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
return true;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$config = json_decode(File::get($configFile), true);
|
/**
|
||||||
if (!$this->validateConfig($config)) {
|
* 提取插件默认配置
|
||||||
throw new \Exception('Invalid plugin config');
|
*/
|
||||||
}
|
protected function extractDefaultConfig(array $config): array
|
||||||
|
{
|
||||||
// 检查依赖
|
|
||||||
if (!$this->checkDependencies($config['require'] ?? [])) {
|
|
||||||
throw new \Exception('Dependencies not satisfied');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提取配置默认值
|
|
||||||
$defaultValues = [];
|
$defaultValues = [];
|
||||||
if (isset($config['config']) && is_array($config['config'])) {
|
if (isset($config['config']) && is_array($config['config'])) {
|
||||||
foreach ($config['config'] as $key => $item) {
|
foreach ($config['config'] as $key => $item) {
|
||||||
$defaultValues[$key] = $item['default'] ?? null;
|
if (is_array($item)) {
|
||||||
|
$defaultValues[$key] = $item['default'] ?? null;
|
||||||
|
} else {
|
||||||
|
$defaultValues[$key] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $defaultValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运行插件数据库迁移
|
||||||
|
*/
|
||||||
|
protected function runMigrations(string $pluginCode): void
|
||||||
|
{
|
||||||
|
$migrationsPath = $this->getPluginPath($pluginCode) . '/database/migrations';
|
||||||
|
|
||||||
|
if (File::exists($migrationsPath)) {
|
||||||
|
Artisan::call('migrate', [
|
||||||
|
'--path' => "plugins/{$pluginCode}/database/migrations",
|
||||||
|
'--force' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布插件资源
|
||||||
|
*/
|
||||||
|
protected function publishAssets(string $pluginCode): void
|
||||||
|
{
|
||||||
|
$assetsPath = $this->getPluginPath($pluginCode) . '/resources/assets';
|
||||||
|
if (File::exists($assetsPath)) {
|
||||||
|
$publishPath = public_path('plugins/' . $pluginCode);
|
||||||
|
File::ensureDirectoryExists($publishPath);
|
||||||
|
File::copyDirectory($assetsPath, $publishPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证配置文件
|
||||||
|
*/
|
||||||
|
protected function validateConfig(array $config): bool
|
||||||
|
{
|
||||||
|
$requiredFields = [
|
||||||
|
'name',
|
||||||
|
'code',
|
||||||
|
'version',
|
||||||
|
'description',
|
||||||
|
'author'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($requiredFields as $field) {
|
||||||
|
if (!isset($config[$field]) || empty($config[$field])) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册到数据库
|
// 验证插件代码格式
|
||||||
Plugin::create([
|
if (!preg_match('/^[a-z0-9_]+$/', $config['code'])) {
|
||||||
'code' => $pluginCode,
|
return false;
|
||||||
'name' => $config['name'],
|
}
|
||||||
'version' => $config['version'],
|
|
||||||
'is_enabled' => false,
|
// 验证版本号格式
|
||||||
'config' => json_encode($defaultValues),
|
if (!preg_match('/^\d+\.\d+\.\d+$/', $config['version'])) {
|
||||||
'installed_at' => now(),
|
return false;
|
||||||
]);
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -64,8 +258,9 @@ class PluginManager
|
|||||||
public function enable(string $pluginCode): bool
|
public function enable(string $pluginCode): bool
|
||||||
{
|
{
|
||||||
$plugin = $this->loadPlugin($pluginCode);
|
$plugin = $this->loadPlugin($pluginCode);
|
||||||
|
|
||||||
if (!$plugin) {
|
if (!$plugin) {
|
||||||
throw new \Exception('Plugin not found');
|
throw new \Exception('Plugin not found: ' . $pluginCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取插件配置
|
// 获取插件配置
|
||||||
@ -77,6 +272,15 @@ class PluginManager
|
|||||||
$plugin->setConfig(json_decode($dbPlugin->config, true));
|
$plugin->setConfig(json_decode($dbPlugin->config, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注册服务提供者
|
||||||
|
$this->registerServiceProvider($pluginCode);
|
||||||
|
|
||||||
|
// 加载路由
|
||||||
|
$this->loadRoutes($pluginCode);
|
||||||
|
|
||||||
|
// 加载视图
|
||||||
|
$this->loadViews($pluginCode);
|
||||||
|
|
||||||
// 更新数据库状态
|
// 更新数据库状态
|
||||||
Plugin::query()
|
Plugin::query()
|
||||||
->where('code', $pluginCode)
|
->where('code', $pluginCode)
|
||||||
@ -85,22 +289,6 @@ class PluginManager
|
|||||||
'updated_at' => now(),
|
'updated_at' => now(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 加载路由
|
|
||||||
$routesFile = $this->pluginPath . '/' . $pluginCode . '/routes/web.php';
|
|
||||||
if (File::exists($routesFile)) {
|
|
||||||
require $routesFile;
|
|
||||||
}
|
|
||||||
// 注册视图
|
|
||||||
$viewsPath = $this->pluginPath . '/' . $pluginCode . '/resources/views';
|
|
||||||
if (File::exists($viewsPath)) {
|
|
||||||
View::addNamespace($pluginCode, $viewsPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化插件
|
|
||||||
if (method_exists($plugin, 'boot')) {
|
|
||||||
$plugin->boot();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,32 +357,6 @@ class PluginManager
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载插件实例
|
|
||||||
*/
|
|
||||||
protected function loadPlugin(string $pluginCode)
|
|
||||||
{
|
|
||||||
$pluginFile = $this->pluginPath . '/' . $pluginCode . '/Plugin.php';
|
|
||||||
if (!File::exists($pluginFile)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
require_once $pluginFile;
|
|
||||||
$className = "Plugin\\{$pluginCode}\\Plugin";
|
|
||||||
return new $className($pluginCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证配置文件
|
|
||||||
*/
|
|
||||||
protected function validateConfig(array $config): bool
|
|
||||||
{
|
|
||||||
return isset($config['code'])
|
|
||||||
&& isset($config['version'])
|
|
||||||
&& isset($config['description'])
|
|
||||||
&& isset($config['author']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查依赖关系
|
* 检查依赖关系
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user