Как могут быть спроектированы системы плагинов, чтобы они не тратили столько ресурсов? - PullRequest
8 голосов
/ 21 января 2012

Я пытаюсь создать базовую систему плагинов , подобную той, которую вы часто встречаете в CMS, такой как WordPress.У вас есть папка плагинов, которые связаны с работой основной системы посредством уведомлений о событиях, используя Observer или Событие шаблон проектирования.

Проблема в том, что невозможно чтобы система знала , на какие события плагин хочет воздействовать - поэтому система должна загружать каждый плагин для каждого запроса страницы, просто чтобы узнать, нужен ли этот плагин в какой-то момент.Излишне говорить, что это много потраченных впустую ресурсов - в случае WordPress, который добавляет до нескольких дополнительных МБ памяти для каждого запроса!

Есть лиальтернативные способы сделать это?

Например, есть ли способ загрузить все это один раз, а затем кэшировать результаты, чтобы ваша система знала, как загружать плагины с отложенной загрузкой?Другими словами, система загружает файл конфигурации, в котором указаны все события, с которыми плагин хочет связать, а затем сохраняет его в APC или что-то для будущих запросов?

Если это также работает плохо, то, возможно, существуетспециальная файловая структура, которая может использоваться для обоснованных предположений о том, когда определенные плагины не нужны для выполнения запроса.

Ответы [ 3 ]

3 голосов
/ 22 января 2012

У меня есть инструмент управления плагинами, но я использовал его только с преимущественно процедурными плагинами и со всеми включениями, обычно загружаемыми сразу.Но для основанного на событиях и API с отложенной загрузкой я мог бы представить использование поверхностных оболочек для управления плагином и использование автозагрузки для реальных расширений.

<?php
  /**
   * api: whatever
   * version: 0.1
   * title: plugin example
   * description: ...
   * config: <var name="cfg[pretty]" type="boolean" ...>
   * depends: otherplugin
   */

 $plugins["title_event"] = "TitleEventClass";
 $plugins["secondary"] = array("Class2", "callback");
?>

В этом примере я бы предположил, что API плагинапростой список.Этот пример сценария feature-plugin-123.php не будет делать ничего, кроме добавления в массив при загрузке.Таким образом, даже если у вас есть дюжина функциональных плагинов, это потребует дополнительных include_once каждого.

Но API основного приложения / или плагина может вместо этого просто создавать экземпляры упомянутых классов (либо new $eventcb; для необработанногоимена классов или call_user_func_array для обратных вызовов).Где, в свою очередь, это выгрузит актуальную задачу в автозагрузчик.Таким образом, у вас есть двойная система, где одна часть управляет списком, а другая находит реальный код.

Таким образом, я все еще воображаю простой config.php, который просто перечисляет плагины и настройки, подобные этому:

<?php
include_once("user/feature-plugin-123.php");
include_once("user/otherplugin2.php");
include_once("user/wrapper-for-htmlpurifier.php");
$cfg["pretty"] = 1;

Опять же, учитывая, что это всего лишь сценарии оболочки / данных, с описанием плагина для удобства управления.Можно также использовать фактический register_even() API и определить дополнительную функцию-оболочку в каждом.Но перечисление имен классов кажется самым простым вариантом.

Вышеупомянутый инструмент управления выглядит немного ржавым и уродливым: http://milki.include -once.org / genericplugins /
Но он не нужен, если вы простонужен список (таблица sql) и нет управления настройками.Эти накладные расходы предназначены только для красивой распечатки метаданных плагина и обеспечения удобочитаемости config.php.

В заключение:

spl_autoload() на include_path и простой реестр событий -> classnameпо одному сценарию-обертке, просто включенному сразу.

2 голосов
/ 24 января 2012

Wordpress и другие системы CMS - очень плохие примеры.

Что мы должны понять, так это то, что модульность почти всегда означает тяжелее.

Лучшая схема, с которой я когда-либо работал, чтобы разрешить эту ситуацию - это плагин на основе классов со строгим соглашением об именах с использованием автозагрузчика.

Итак, прежде чем использовать плагин, вам нужно будет создать экземпляр или использовать статические функции.

Вы можете даже вызвать плагин как:

<?php $thePlugin::method(); ?>

Например:

<?php

    spl_autoload_register('systemAutoload');

    function systemAutoload($class)
    {
        $parts = explode('_',$class);


        switch($parts[1])
        {
            case "Plugin":
                include("/plugins/{$parts[2]}/{$parts[2]}.php");
            break;
        }

        // ...    

    }

?>

Относительно событий:

Вы должны регистрировать эти события статически, чтобы избежать их динамического отображения.

База данных будет правильным местом для этого.Вы можете иметь таблицу событий и методы install () и uninstall () в классе плагина для добавления определенных событий или методов привязки к другим событиям.Это один запрос к базе данных, и если вы хотите получить от него больше, добавьте его в memcached или в плоский ini-файл.

Хорошо работает для меня.Таким образом, я смог получить тяжелую систему, которая потребляла 8 МБ на запрос, чтобы сбросить до 1 МБ, с точно таким же списком функций, без расширенного кэширования.Теперь мы можем добавить больше функций и поддерживать систему в чистоте

Надеюсь, что это поможет

0 голосов
/ 21 января 2012

Я бы сохранил имя класса плагина вместе с его подписанными событиями в файле конфигурации, а затем, например, сохранил проанализированный файл конфигурации в APC. Затем, когда событие инициируется, система может лениво загружать соответствующие классы плагинов по мере необходимости.

...