Решил написать, а позже учитывая комменты @Alexander Zonov переписать, самую простую систему подключения плагинов к файлу index.php
foreach(glob(__DIR__ . '/plugins/*', GLOB_ONLYDIR) as $plugin) {
if(file_exists($plugin . '/' . basename($plugin) . '.php')) {
spl_autoload_register(function($plugin){
include 'plugins/'.$plugin . '/' . basename($plugin) . '.php';
});
$pluginClass = basename($plugin);
$pluginClass::page();
}
}
require_once __DIR__ . '/tmpldir/maintemplate.php';
?>
Пример плагина например plugins/myplugin/myplugin.php
В шаблоне tmpldir/maintemplate.php данные соответственно вывожу через:
Файлы на сайт может добавлять только админ сайта например когда устанавливает какой то плагин в папку /plugins/
ВОПРОС: действительно ли классы плагинов и их функции подгружаются в данном случае при запросе или на самом деле они все сразу выполняются и spl_autoload_register здесь не имеет смысла?
И вообще что можно в целом поправить, оптимизировать, что я не учёл и т.д. И почему люди используют более сложные схемы такие как Observer pattern или тем более система плагинов WordPress?
Ответ
Начнём с того, что spl_autoload_register регистрирует обработчик автозагрузки. То есть, когда мы обращаемся к какому-то классу, описание которого ещё не загружено, выполняются обработчики автозагрузки, после чего ожидается, что класс таки будет определён. Таким образом регистрировать кучу обработчиков в цикле не надо, так как пути к файлам с определениями классов модулей у нас уже есть, то можно сразу их и подключать.
Далее. Часто модулями управляет админ через админку и в таком случае, подключать все модули из plugins не надо. Папка с модулями является только источником данных. Иными словами, админ отрывает страницу управления модулями, страница засасывает все доступные модули, админ отмечает галочками модули, которые хочет включить и для них создаются записи в базе данных. Теперь при загрузке сайта засасываются только модули, для которых существуют записи в бд.
Кроме того, модифицировать поля родительского класса не самая лучшая идея. Плагины могут оказывать влияние на самые разные аспекты работы приложения и хорошо бы это реализовывать через методы. Это позволит избежать глобальных переменных (с точки зрения потомков mainClass поля $content и $added глобальные).
interface IPlugin {
// тут мы отдаём плагину ответственность за весь респонс,
// но обычно имеет смысл давать доступ через какой-нибудь объект типа Контент или Сайт
public function handleResponse($request, $response): array;
}
class Main implements IPlugin {
public function handleResponse($request, $response): array {
$response['body'] = $this->tpl(
__DIR__ . '/tmpldir/main.phtml',
['content' => $response['body']]
);
return $response;
}
private function tpl($file, array $params = []) {
extract($params);
ob_start();
require func_get_arg(0);
return ob_get_clean();
}
}
class App {
private $plugins = [];
public function registerPlugins($pluginDir) {
// хорошо бы подключать только плагины активированные в админке
// и в указанном админом порядке
foreach (glob($pluginDir. '/*', GLOB_ONLYDIR) as $pluginDir) {
$name = basename($pluginDir);
$fileName = $pluginDir . '/' . $name . '.php';
if (is_readable($fileName)) {
require_once $fileName; // подключаем сразу
// если плагины могут быть загруженны с помощью автозагрузки,
// то можно сразу инстанцировать их без require_once
$this->addPlugin(new $name);
}
}
// плагин Main -- особый случай, оне не лежит в plugins и его подключаем последним
$this->addPlugin(new Main());
}
public function addPlugin(IPlugin $plugin) {
$this->plugins[] = $plugin;
}
public function run($request) {
$response = [
'code' => 200,
'headers' => [],
'body' => '',
];
foreach ($this->plugins as $plugin) {
try {
$response = $plugin->handleResponse($request, $response);
} catch(Throwable $err) {
$response = [
'code' => 500,
'headers' => [],
'body' => 'Всё тлен: ' . $err->getMessage(),
];
break;
}
}
$this->sendResponse($response);
}
private function sendResponse($response) {
if ( ! headers_sent()) {
http_response_code($response['code']);
foreach ($response['headers'] as $name => $value) {
if (is_string($name)) {
$header = $name.': '.$value;
} else {
$header = $value;
}
header($header, true);
}
}
echo $response['body'];
}
}
$myApp = new App();
$myApp->registerPlugins(__DIR__ . '/plugins');
$myApp->run($_SERVER['REQUEST_URI']?? '/');
tmpldir/main.phtml
Site
h1>plugins/Plugin/Plugin.php
class Plugin implements IPlugin {
public function handleResponse($request, $response): array { // если управлять не ответом, а каким-нибудь объектом контента // или сайта, то можно делать более интересные вещи $response['body'] = 'Content overrided by myplugin'; $response['body'] .= 'Text added by myplugin';
return $response; }
}
Иногда плагины взаимодействуют не только с сайтом и его содержимым, то и друг с другом. В таком случае нужны ещё более сложные системы, такие как в вордпресе или других CMS.
Комментариев нет:
Отправить комментарий