Страницы

Поиск по вопросам

воскресенье, 7 июля 2019 г.

Моя система подключения плагинов: есть ли автозагрузка, что я не учел и т.д

Решил написать, а позже учитывая комменты @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.

Комментариев нет:

Отправить комментарий