BiliHelper-personal/src/Plugin/Plugin.php
2024-02-13 14:38:17 +08:00

268 lines
8.0 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2024 ~ 2025
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、 。
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ヽ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Plugin;
use Bhp\Log\Log;
use Bhp\TimeLock\TimeLock;
use Bhp\Util\AsciiTable\AsciiTable;
use Bhp\Util\DesignPattern\SingleTon;
class Plugin extends SingleTon
{
/**
* 监听插件的启用/关闭|UUID下标
* @access private
* @var array
*/
protected array $_staff = [];
/**
* 保存所有插件信息
* @var array
*/
protected array $_plugins = [];
/**
* 保存插件优先级信息
* @var array
*/
protected array $_priority = [];
/**
* @return void
*/
public function init(): void
{
$this->detector();
}
/**
* @return array
*/
public static function getPlugins(): array
{
return self::getInstance()->_plugins;
}
/**
* @return array
*/
public static function getPluginsPriority(): array
{
return self::getInstance()->_priority;
}
/**
* @return array
*/
public static function getPluginsStaff(): array
{
return self::getInstance()->_staff;
}
/**
* 这个是全局使用的触发钩子动作方法
* @param string $hook
* @param mixed ...$params
* @return string
*/
public function trigger(string $hook, mixed...$params): string
{
// 首先需要判断一下$hook 存不存在
if (isset($this->_staff[$hook]) && is_array($this->_staff[$hook]) && count($this->_staff[$hook]) > 0) {
$plugin_func_result = '';
// 如果存在定义 $plugin_func_result
foreach ($this->_staff[$hook] as $staff) {
// 如果只是记录 请不要返回
$plugin_func_result = '';
$class = &$staff[0]; // 引用过来的类
$method = $staff[1]; // 类下面的方法
if (method_exists($class, $method)) {
//
if (!$this->canItRun($class)) continue;
//
$func_result = $class->$method(...$params);
if (is_numeric($func_result)) {
// 这里判断返回值是不是字符串,如果不是将不进行返回到页面上
$plugin_func_result .= $func_result;
}
}
}
}
return $plugin_func_result ?? '';
}
/**
* 这里是在插件中使用的方法 用来注册插件
* @param object $class_obj
* @param string $method
* @return void
*/
public function register(object &$class_obj, string $method): void
{
$hook = get_class($class_obj);
// 获取类名和方法名链接起来做下标
$func_class = $hook . '->' . $method;
// 将类和方法放入监听数组中 以$func_class做下标
$this->_staff[$hook][$func_class] = array(&$class_obj, $method);
// 每个插件必须实现的 getPluginInfo 获取插件信息
$this->addPluginInfo($hook, $class_obj->getPluginInfo());
}
/**
* @param string $hook
* @param array $info
* @return void
*/
protected function addPluginInfo(string $hook, array $info): void
{
$info = $this->validatePlugins($hook, $info);
//
$this->_plugins[$hook] = $info;
$this->_priority[] = $info['priority'];
}
/**
* @param string $hook
* @param array $info
* @return array
*/
protected function validatePlugins(string $hook, array $info): array
{
// 插件信息缺失
$fillable = ['hook', 'name', 'version', 'desc', 'priority', 'cycle'];
foreach ($fillable as $val) {
if (!array_key_exists($val, $info)) {
failExit("加载 $hook 插件错误,插件信息缺失,请检查修正.");
}
}
// 插件名冲突
if (array_key_exists($hook, $this->_plugins)) {
failExit("加载 $hook 插件错误,插件名冲突,请检查修正.");
}
// 插件优先级冲突
if (in_array($info['priority'], $this->_priority)) {
failExit("加载 $hook 插件错误,插件优先级冲突,请检查修正.");
}
// 插件优先级定义
if ($info['priority'] < 1000) {
failExit("加载 $hook 插件错误,插件优先级定义错误,请检查修正.");
}
//
$info['status'] = '√';
//
return $info;
}
/**
* 初始化插件(all)
* @return void
*/
protected function detector(): void
{
// 主要功能为将插件需要执行功能放入 $_staff
$plugins = $this->getActivePlugins();
foreach ($plugins as $plugin) {
// 这里将所有插件践行初始化
// 路径请自己注意
if (@file_exists($plugin['path'])) {
include_once($plugin['path']);
// 此时设定 文件夹名称 文件名称 类名 是统一的 如果想设定不统一请自己在get_active_plugins()内进行实现
$class = $plugin['name'];
if (class_exists($class)) {
// 初始化所有插件类
new $class($this);
}
}
}
$this->sortPlugins();
$this->preloadPlugins();
}
/**
* 获取插件信息(all)
* 假定了插件在根目录的/plugin
* 假定插件的入口和插件文件夹的名字是一样的
* 注意:这个执行文件我放在了根目录 以下路径请根据实际情况获取
* @return array
*/
protected function getActivePlugins(): array
{
$plugins = [];
$plugin_dir_name_arr = scandir(APP_PLUGIN_PATH);
//
foreach ($plugin_dir_name_arr as $_ => $v) {
if ($v == "." || $v == "..") {
continue;
}
// /plugin/Test/Test.php
if (is_dir(APP_PLUGIN_PATH . $v)) {
$path = APP_PLUGIN_PATH . $v . DIRECTORY_SEPARATOR . $v . '.php';
$plugins[] = ['name' => $v, 'path' => $path];
}
}
//
return $plugins;
}
/**
* 插件排序
* @param string $column_key
* @param int $sort_order
* @return void
*/
protected function sortPlugins(string $column_key = 'priority', int $sort_order = SORT_ASC): void
{
$arr = array_column($this->_plugins, $column_key);
array_multisort($arr, $sort_order, $this->_plugins);
}
/**
* @return void
*/
protected function preloadPlugins(): void
{
$th_list = AsciiTable::array2table($this->_plugins, '预加载插件列表');
foreach ($th_list as $item) {
// Log::info($item);
echo $item . PHP_EOL;
}
}
/**
* @param mixed $class
* @return bool
*/
protected function canItRun(mixed $class): bool
{
if (!isset($class->info['start']) || !isset($class->info['end'])) {
// Log::info("插件 {$class->info['name']} 全天运行");
return true;
}
if (TimeLock::isWithinTimeRange($class->info['start'], $class->info['end'])) {
// Log::info("插件 {$class->info['name']} 运行时间段");
return true;
} else {
// Log::info("插件 {$class->info['name']} 不在运行时间段");
return false;
}
}
}