所以我尝试使用侦听器在 PHP 中编写一个插件系统,就像我的世界对优先级和操作所做的那样,但我真的不知道如何做。因为如果我尝试使用事件侦听器,我必须将其赋予一个名为 $event 的值,但我无法与其他类共享该值,因为它们会告诉我可以使用 index.php 文件外部的变量.
我的项目结构就像一个 lavarel 结构,一个 public/index.php ,其余的只是 app/
这是插件事件:
<?php
namespace MythicalSystemsFramework\Plugins;
/**
* This class is used to handle plugin events.
*
* @since 1.0.0
*
* @version 1.0.0
*
* @category Plugins
*
* This class is inspired by Evenement.
*
* @see https://github.com/igorw/even ement
*
* @license MIT
*
* */
class PluginEvent
{
protected array $listeners = [];
protected array $onceListeners = [];
/**
* Adds a listener for the specified event.
*
* @param string $event the name of the event
* @param callable $listener the listener function to be added
*
* @return static returns the current instance of the PluginEvent class
*/
public function on(string $event, callable $listener): static
{
if (!isset($this->listeners[$event])) {
$this->listeners[$event] = [];
}
$this->listeners[$event][] = $listener;
return $this;
}
/**
* Adds a listener for the specified event that will be triggered only once.
*
* @param string $event the name of the event
* @param callable $listener the listener function to be added
*
* @return static returns the current instance of the PluginEvent class
*/
public function once(string $event, callable $listener): static
{
if (!isset($this->onceListeners[$event])) {
$this->onceListeners[$event] = [];
}
$this->onceListeners[$event][] = $listener;
return $this;
}
/**
* Removes a listener for the specified event.
*
* @param string $event the name of the event
* @param callable $listener the listener function to be removed
*/
public function removeListener(string $event, callable $listener): void
{
if (isset($this->listeners[$event])) {
$index = array_search($listener, $this->listeners[$event], true);
if ($index !== false) {
unset($this->listeners[$event][$index]);
if (count($this->listeners[$event]) === 0) {
unset($this->listeners[$event]);
}
}
}
if (isset($this->onceListeners[$event])) {
$index = array_search($listener, $this->onceListeners[$event], true);
if ($index !== false) {
unset($this->onceListeners[$event][$index]);
if (count($this->onceListeners[$event]) === 0) {
unset($this->onceListeners[$event]);
}
}
}
}
/**
* Removes all listeners for the specified event or all events if no event is specified.
*
* @param string|null $event the name of the event (optional)
*/
public function removeAllListeners(?string $event = null): void
{
if ($event !== null) {
unset($this->listeners[$event], $this->onceListeners[$event]);
} else {
$this->listeners = [];
$this->onceListeners = [];
}
}
/**
* Removes all listeners for the specified event or all events if no event is specified.
*
* @param string|null $event the name of the event (optional)
*
* @return void
*/
public function listeners(?string $event = null): array
{
if ($event === null) {
$events = [];
$eventNames = array_unique(
array_merge(
array_keys($this->listeners),
array_keys($this->onceListeners)
)
);
foreach ($eventNames as $eventName) {
$events[$eventName] = array_merge(
$this->listeners[$eventName] ?? [],
$this->onceListeners[$eventName] ?? []
);
}
return $events;
}
return array_merge(
$this->listeners[$event] ?? [],
$this->onceListeners[$event] ?? []
);
}
/**
* Emits the specified event and triggers all associated listeners.
*
* @param string $event the name of the event
* @param array $arguments the arguments to be passed to the listeners (optional)
*/
public function emit(string $event, array $arguments = []): void
{
$listeners = [];
if (isset($this->listeners[$event])) {
$listeners = array_values($this->listeners[$event]);
}
$onceListeners = [];
if (isset($this->onceListeners[$event])) {
$onceListeners = array_values($this->onceListeners[$event]);
}
if ($listeners !== []) {
foreach ($listeners as $listener) {
$listener(...$arguments);
}
}
if ($onceListeners !== []) {
unset($this->onceListeners[$event]);
foreach ($onceListeners as $listener) {
$listener(...$arguments);
}
}
}
/**
* Get an instance of the PluginEvent class.
*/
public static function getInstance(): PluginEvent
{
return new PluginEvent();
}
}
这是加载插件事件的内容:
<?php
namespace MythicalSystemsFramework\Plugins;
use MythicalSystemsFramework\Kernel\Config;
use MythicalSystemsFramework\Kernel\Logger;
use MythicalSystemsFramework\Kernel\LoggerTypes;
use MythicalSystemsFramework\Kernel\LoggerLevels;
class PluginsManager
{
public static string $plugins_path = __DIR__ . '/../../storage/addons';
public static function init(\Router\Router $router, \Twig\Environment $renderer, PluginEvent $eventHandler): void
{
if (!file_exists(self::$plugins_path)) {
mkdir(self::$plugins_path, 0777, true);
}
$plugins = self::getAllPlugins();
foreach ($plugins as $plugin) {
$plugin_info = self::readPluginFile($plugin);
/*
* Are all the requirements installed?
*/
if (isset($plugin_info['require'])) {
$requirements = $plugin_info['require'];
foreach ($requirements as $requirement) {
if ($requirement == 'MythicalSystemsFramework') {
continue;
}
$isInstalled = self::readPluginFile($requirement);
if ($isInstalled) {
continue;
} else {
Logger::log(LoggerLevels::CRITICAL, LoggerTypes::PLUGIN, "Plugin $plugin requires $requirement to be installed.");
}
}
}
/*
* Register the plugin in the database if it is not already registered.
*/
if (!Database::doesInfoExist('name', $plugin_info['name']) == true) {
$p = $plugin_info;
$p_homepage = $p['homepage'] ?? null;
$p_license = $p['license'] ?? null;
$p_support = $p['support'] ?? null;
$p_funding = $p['funding'] ?? null;
$p_require = $p['require'] ?? 'MythicalSystemsFramework';
Database::registerNewPlugin($p['name'], $p['description'], $p_homepage, $p_require, $p_license, $p['stability'], $p['authors'], $p_support, $p_funding, $p['version'], false);
continue;
}
/**
* Is plugin enabled?
*/
$plugin_info_db = Database::getPlugin($plugin_info['name']);
if ($plugin_info_db['enabled'] == 'true') {
$plugin_home_dir = self::$plugins_path . '/' . $plugin_info['name'];
$main_class = $plugin_home_dir . '/' . $plugin_info_db['name'] . '.php';
if (file_exists($main_class)) {
/*
* Start the plugin main class.
*/
try {
require_once $main_class;
$plugin_class = new $plugin_info_db['name']();
$plugin_class->Main();
try {
$plugin_class->Route($router, $renderer);
} catch (\Exception $e) {
Logger::log(LoggerLevels::CRITICAL, LoggerTypes::PLUGIN, 'Failed to add routes for plugin' . $plugin_info_db['name'] . '' . $e->getMessage());
}
try {
$plugin_class->Event($eventHandler);
} catch (\Exception $e) {
Logger::log(LoggerLevels::CRITICAL, LoggerTypes::PLUGIN, 'Failed to add events for plugin' . $plugin_info_db['name'] . '' . $e->getMessage());
}
} catch (\Exception $e) {
Logger::log(LoggerLevels::CRITICAL, LoggerTypes::PLUGIN, "Something failed while we tried to enable the plugin '" . $plugin_info_db['name'] . "'. " . $e->getMessage());
}
} else {
Logger::log(LoggerLevels::CRITICAL, LoggerTypes::PLUGIN, "The main class for plugin '$plugin' does not exist.");
}
}
}
}
/**
* Get all plugins.
*/
public static function getAllPlugins(): array
{
$plugins = [];
foreach (scandir(self::$plugins_path) as $plugin) {
if ($plugin == '.' || $plugin == '..') {
continue;
}
$pluginPath = self::$plugins_path . '/' . $plugin;
if (is_dir($pluginPath)) {
$json_file = $pluginPath . '/MythicalFramework.json';
if (file_exists($json_file)) {
$json = json_decode(file_get_contents($json_file), true);
if (isset($json['name']) && $json['name'] === $plugin) {
$plugins[] = $plugin;
}
}
}
}
return $plugins;
}
/**
* Get plugin info.
*/
public static function readPluginFile(string $plugin_name): array
{
if (!self::doesPluginExist($plugin_name)) {
return [];
}
if (!self::isPluginConfigValid($plugin_name)) {
return [];
}
$json_file = self::$plugins_path . '/' . $plugin_name . '/MythicalFramework.json';
return json_decode(file_get_contents($json_file), true);
}
/**
* Does a plugin exist?
*
* @param string $plugin_name The name of the plugin
*/
public static function doesPluginExist(string $plugin_name): bool
{
$plugin_folder = self::$plugins_path . '/' . $plugin_name . '/MythicalFramework.json';
return file_exists($plugin_folder);
}
/**
* Is the plugin config valid?
*
* @param string $plugin_name The name of the plugin
*/
public static function isPluginConfigValid(string $plugin_name): bool
{
if (!self::doesPluginExist($plugin_name)) {
return false;
}
$json_file = self::$plugins_path . '/' . $plugin_name . '/MythicalFramework.json';
if (file_exists($json_file)) {
$json = json_decode(file_get_contents($json_file), true);
if (isset($json['name']) && isset($json['description']) && isset($json['stability']) && isset($json['authors']) && isset($json['version']) && isset($json['require'])) {
return true;
} else {
return false;
}
} else {
return false;
}
}
}
这是 public/index.php 文件:
<?php
try {
if (file_exists('../vendor/autoload.php')) {
require '../vendor/autoload.php';
} else {
exit('Hello, it looks like you did not run: "<code>composer install --no-dev --optimize-autoloader</code>". Please run that and refresh the page');
}
} catch (Exception $e) {
exit('Hello, it looks like you did not run: <code>composer install --no-dev --optimize-autoloader</code> Please run that and refresh');
}
use MythicalSystemsFramework\App;
use MythicalSystemsFramework\Api\Api as api;
use MythicalSystemsFramework\Plugins\PluginEvent;
use MythicalSystemsFramework\Web\Template\Engine;
use MythicalSystemsFramework\Plugins\PluginsManager;
use MythicalSystemsFramework\Web\Installer\Installer;
$router = new Router\Router();
$event = new PluginEvent();
global $event;
define('$event', $event);
/*
* Check if the app is installed
*/
Installer::Installed($router);
/*
* Check if the app is healthy and all requirements are met
*/
App::checkIfAppIsHealthy();
/**
* Get the renderer :).
*/
$renderer = Engine::getRenderer();
/*
* Load the routes.
*/
api::registerApiRoutes($router);
App::registerRoutes($renderer);
$event->emit('app.onRoutesLoaded', [$router]);
/*
* Initialize the plugins manager.
*/
PluginsManager::init($router, $renderer, $event);
$router->add('/(.*)', function () {
global $renderer;
$renderer->addGlobal('page_name', '404');
http_response_code(404);
exit($renderer->render('/errors/404.twig'));
});
try {
$router->route();
} catch (Exception $e) {
exit('Failed to start app: ' . $e->getMessage());
}
现在我尝试使用定义创建一个值,这样我就可以在全局范围内使用它,但是在执行类时我没有工作,并告诉我这是未定义的,下一个问题是 PluginManager 在应用程序文件夹之前初始化,因此基本上,由于插件在事件之前启动,因此事件将不起作用!所以在 PluginManager.init() 之后新注册的事件将不起作用!
我真的不知道如何解决这些问题!
所有的解释都在上面!
全局函数是我客户的托管提供商的 PHP 配置错误!
这些事件不起作用,因为我是在之后而不是之前注册的事件!
如果有人有同样的问题,可以从我的项目中复制一些代码: