Порядок вызова плагинов при обработке хука

Ситуация:

Два плагина (ExpWall и Page) регистрируют функции на один и тот же хук:

$this->AddHook('template_main_menu_item', 'MenuMain', __CLASS__);


и

$this->AddHook('template_main_menu_item', 'Menu');


Как определить, в каком порядке будут вызываться обработчики хуков у каждого плагина (я полагаю, по алфавиту — ExpWall, потом Page)?

Как переопределить очередность вызова плагинов при обработке одного хука? Мне нужно, чтобы сначала отработал плагин Page, а потом ExpWall.

РЕШЕНО:
Метод Hook->AddHook() имеет 4-ым параметром int-значение — очередность обработки кука. Чем она выше — тем раньше отработает обработчик.

Многие плагины этот параметр игнорируют — он принимает значение по умолчанию 1 и тогда плагины вызываются по алфавиту.

18 комментариев

avatar
Не знаю точно, но могу предположить, что они вызываются по порядку активации плагинов.

plugins.dat
  • dex-
  • +1
avatar
Да, это работает (как ни удивительно и, несмотря на сортировку плагинов где-то в движке), но это совершенно неочевидный и непрозрачный механизм.
avatar
/**
     * Добавляет обработчик на хук
     * @see ModuleHook::AddExecHook
     *
     * @param string $sName Название хука на который вешается обработчик
     * @param string $sCallBack Название метода обработчика
     * @param null|string $sClassNameHook Название класса обработчика, по умолчанию это текущий класс хука
     * @param int $iPriority Приоритет обработчика хука, чем выше число, тем больше приоритет - хук обработчик выполнится раньше остальных
     */
    protected function AddHook($sName, $sCallBack, $sClassNameHook = null, $iPriority = 1)
    {
        if (is_null($sClassNameHook)) {
            $sClassNameHook = get_class($this);
        }
        $this->Hook_AddExecHook($sName, $sCallBack, $iPriority, array('sClassName' => $sClassNameHook));
    }
avatar
Спасибо.
avatar
Если уж разработчик в теме — не могу не задать еще один вопрос :)

Как изменить какой-то из параметров темы, не внося изменения в /template/skin//settings/config/config.php?

Сделал после загрузки конфигов dump конфига, но настройки темы в него не подгрузились.
avatar
После ModuleViewer->Init()?
Необходимо вносить изменения в конфиг шаблона(Config::Set()) после первого вызова модуля viewer.
avatar
попробуем поковырять. но почему так странно сделано?
avatar
Что-то, единственно похожее на правду нашел тут:

/engine/modules/viewer/Viewer.class.php
/**
     * Получает локальную копию модуля
     *
     * @return ModuleViewer
     */
    public function GetLocalViewer()
    {
        $sClass = $this->Plugin_GetDelegate('module', __CLASS__);

        $oViewerLocal = new $sClass(Engine::getInstance());
        $oViewerLocal->Init(true);
        $oViewerLocal->VarAssign();
        $oViewerLocal->Assign('aLang', $this->Lang_GetLangMsg());
        return $oViewerLocal;
    }


Оно?
avatar
Нет. Смотрите метод Init. Там загрузка конфигов шаблона происходит
avatar
Что за бред вообще в логике ядра???

/engine/modules/viewer/Viewer.class.php
if (!$bLocal) {
            if (file_exists($sFile = Config::Get('path.smarty.template') . '/settings/config/config.php')) {
                Config::LoadFromFile($sFile, false);
            }
        }

Загружаем конфиг, говорим НЕ ПЕРЕЗАПИСЫВАТЬ имеющийся конфиг полученным.
Все верно? Верно.
Идем дальше по цепочке вызовов…

… и приходим к
engine/lib/internal/ConfigSimple/Config.class.php

public function SetConfig($aConfig = array(), $bRewrite = true)
    {
        if (is_array($aConfig)) {
            if ($bRewrite) {
                $this->aConfig = $aConfig;
            } else {
                $this->aConfig = $this->ArrayEmerge($this->aConfig, $aConfig);
            }
            return true;
        }
        $this->aConfig = array();
        return false;
    }


Идем в код ArrayEmerge (точнее в func_array_merge_assoc()

… и обнаружием удивительную вещь:

Массив arr1 перезаписывается ключами массива arr2, хотя это поведение НЕ ОЖИДАЕТСЯ.

Ясно же сказано — не перезаписывать конфиги? Ясно.

А данные херятся.

Где логика?
avatar
Причем я не понимаю другого.

/config/config.php

Тут max_tree определен, допустим, как 7.
Этот конфиг загуржается с перезаписью (true) (перезаписываем пустой конфиг)

/config/config.local.php
При его загрузке стоит «НЕ ПЕРЕЗАПИСЫВАТЬ»

Тут max_tree определен как 10
… и значение успешно меняется на 10.

/templates/skin/synio/settings/config/config.php
При загрузке стоит опять «НЕ ПЕРЕЗАПИСЫВАТЬ»

Тут max_tree определен как 5
… значение в конфиге меняется на 5.
avatar
В общем, единственное решение, которое хоть как-то работает:

/engine/modules/viewer/Viewer.class.php

public function Init($bLocal = false)
    {
        $this->Hook_Run('viewer_init_start', compact('bLocal'));
        /**
         * Load template config
         */
        if (!$bLocal) {
            if (file_exists($sFile = Config::Get('path.smarty.template') . '/settings/config/config.php')) {
                Config::LoadFromFile($sFile, false);
            }

            $skin = Config::Get('view.skin');
            if (file_exists($sFile = Config::Get('path.root.server') . "/config/skins/{$skin}.config.php")) {
                Config::LoadFromFile($sFile, false);
            }
        }
...


И, к слову, зачем такая сложная функция слияния массивов?

чем не угодил
php.net/manual/ru/function.array-replace-recursive.php
?
avatar
Сразу поясню, «зачем так сложно».

Мне несложно изменить конфиг конкретно шаблона Synio.

Но у меня автоматический деплой кода и вносить изменения в репозитории плагинов — те, которые могут быть заданы в основном конфиге приложения — я не хочу.
avatar
Удивительно, что разработчики большинства плагинов (всех?) не считают нужным хранить этот приоритет в конфиге плагина — это очень многое бы упростило «искаропки».
avatar
Есть в движке какой-нибудь механизм, который позволяет получить список всех хуков и всех их обработчиков с приоритетами?

В идеале — консольный.
avatar
Идеал недостижим. Пришлось писать свой. Заодно вмешиваться в ядро. :(
avatar
Все равно глобально это не решает проблему: добавленные пункты будут в конце основного меню.
А если нужно «между», 2-м по сету, 3-м и т.п.?
Я просто добавил хуков и поправил плагины: template_main_menu_item_1, ...2 и т.д.
avatar
А я просто в начале каждого плагина написал вот так:

    const ConfigKey = 'expwall';
    const HooksArray = [
        'template_main_menu_item'       =>  'MenuMain',
        'template_profile_whois_end'    =>  'ProfileWhois'
    ];

    public function RegisterHook()
    {
        $plugin_config_key = $this::ConfigKey;
        foreach ($this::HooksArray as $hook => $callback) {
            $this->AddHook(
                $hook,
                $callback,
                __CLASS__,
                Config::Get("plugin.{$plugin_config_key}.hook_priority.{$hook}") ?? 1
            );
        }
    }


Да, это не решает проблему полностью, но я могу в локальном конфиге прописать:
$config['plugin']['page']['hook_priority']['template_main_menu_item'] = 2;
$config['plugin']['expwall']['hook_priority']['template_main_menu_item'] = -10;


Теперь плагин page отработает раньше, чем ExpWall

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