Files
laralog/app/Blog/Support/PluginManager.php
T

178 lines
5.3 KiB
PHP

<?php
namespace App\Blog\Support;
use App\Models\PluginRecord;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
class PluginManager
{
/** @var array<string, array<int, callable>> */
private array $actions = [];
/** @var array<string, array<int, callable>> */
private array $filters = [];
/** @var array<string, class-string<\App\Blog\Support\PluginServiceProvider>> */
private array $providers = [];
public function path(?string $plugin = null): string
{
return config('plugins.path').($plugin ? '/'.$plugin : '');
}
public function exists(string $plugin): bool
{
return is_dir($this->path($plugin)) && File::exists($this->path($plugin).'/plugin.json');
}
public function manifest(string $plugin): array
{
$file = $this->path($plugin).'/plugin.json';
if (! File::exists($file)) {
return [];
}
return json_decode(File::get($file), true) ?? [];
}
public function all(): array
{
$plugins = [];
foreach (File::directories(config('plugins.path')) as $dir) {
$basename = basename($dir);
$manifest = $this->manifest($basename);
if (! $manifest) {
continue;
}
[$vendor, $name] = array_pad(explode('.', $basename), 2, $basename);
$plugins[$basename] = [
'key' => $basename,
'vendor' => $vendor,
'name' => $name,
'title' => $manifest['title'] ?? $name,
'version' => $manifest['version'] ?? '0.0.0',
'description' => $manifest['description'] ?? '',
'author' => $manifest['author'] ?? '',
'enabled' => $this->isEnabled($basename),
];
}
return $plugins;
}
public function isEnabled(string $plugin): bool
{
if (\Illuminate\Support\Facades\Schema::hasTable('plugin_records')) {
[$vendor, $name] = array_pad(explode('.', $plugin), 2, $plugin);
$record = PluginRecord::query()->where('vendor', $vendor)->where('name', $name)->first();
if ($record) {
return (bool) $record->enabled;
}
}
return in_array($plugin, config('plugins.enabled', []), true);
}
public function enable(string $plugin): void
{
[$vendor, $name] = array_pad(explode('.', $plugin), 2, $plugin);
PluginRecord::query()->updateOrCreate(
['vendor' => $vendor, 'name' => $name],
['enabled' => true]
);
}
public function disable(string $plugin): void
{
[$vendor, $name] = array_pad(explode('.', $plugin), 2, $plugin);
PluginRecord::query()->updateOrCreate(
['vendor' => $vendor, 'name' => $name],
['enabled' => false]
);
}
public function uninstall(string $plugin): void
{
[$vendor, $name] = array_pad(explode('.', $plugin), 2, $plugin);
PluginRecord::query()->where('vendor', $vendor)->where('name', $name)->delete();
}
/**
* 解析插件提供者类:plugins/{vendor}.{name}/src/PluginServiceProvider.php
*
* @return class-string<\App\Blog\Support\PluginServiceProvider>|null
*/
public function resolveProvider(string $plugin): ?string
{
$class = $this->providers[$plugin] ?? null;
if ($class) {
return $class;
}
// 类名兜底:先找 src/ServiceProvider.php,再找 src/PluginServiceProvider.php
$manifest = $this->manifest($plugin);
$class = $manifest['provider'] ?? null;
if (! $class) {
$candidates = ['/src/ServiceProvider.php', '/src/PluginServiceProvider.php'];
foreach ($candidates as $candidate) {
if (File::exists($this->path($plugin).$candidate)) {
[$vendor, $name] = array_pad(explode('.', $plugin), 2, $plugin);
$class = 'Plugins\\'.Str::studly($vendor).'\\'.Str::studly($name).'\\'.basename($candidate, '.php');
break;
}
}
}
return $this->providers[$plugin] = $class;
}
public function registerProvider(string $plugin, string $class): void
{
$this->providers[$plugin] = $class;
}
/* ---------------- Hooks ---------------- */
public function addAction(string $hook, callable $callback, int $priority = 10): void
{
$this->actions[$hook][$priority][] = $callback;
ksort($this->actions[$hook]);
}
public function addFilter(string $hook, callable $callback, int $priority = 10): void
{
$this->filters[$hook][$priority][] = $callback;
ksort($this->filters[$hook]);
}
public function doAction(string $hook, mixed ...$args): void
{
foreach ($this->actions[$hook] ?? [] as $callbacks) {
foreach ($callbacks as $callback) {
$callback(...$args);
}
}
}
public function applyFilters(string $hook, mixed $value, mixed ...$args): mixed
{
foreach ($this->filters[$hook] ?? [] as $callbacks) {
foreach ($callbacks as $callback) {
$value = $callback($value, ...$args);
}
}
return $value;
}
}