Особенность вызова событий в 1.4.x

С давних пор все привыкли писать плагины вот так:

$e = &$modx->event;
if ($e->name = '...') {
    ...
    $e->output('test');
}


Невозможно сказать, кто был первопроходцем, но так или иначе практически все разработчики следуют этому шаблону, не задумываясь, почему он так выглядит. Само по себе объявление $e = $modx->event (& здесь не нужен вообще, так как объекты в PHP и без этого передаются по ссылке) очевидно служит целью не писать каждый раз $modx->event. Так вот, в последних версиях Evo эта экономия вышла боком.

Рассмотрим плагин, который должен вывести на главной странице админки некий виджет:

$e = &$modx->Event;
switch($e->name){
  case 'OnManagerWelcomeHome':
    $a = $modx->runSnippet('DocLister', array('parents' => 0,'tpl' => '@CODE: [+title+]'));
    $widgets['shoplist'] = array(
      'menuindex' =>'1',
      'id' => 'shoplist',
      'cols' => 'col-sm-12',
      'icon' => 'fa-shopping-cart',
      'title' => 'Виджет',
      'body' => '<div class="card-body">' . $a . '</div>',
    );
    $e->output(serialize($widgets));
  break;
}


Должен, но не выводит, хотя все сделано правильно.

Другой пример из статьи про SimpleGallery, который успешно работал и внезапно перестал:

$e = &$modx->event;
if ($e->name == "OnParseProperties") {
    if ($element == "SimpleGallery") {
        if (isset($args['template']) && $args['template']==6) {
            $out = array ();
            //задаем новое значение параметра tabName
            $out["tabName"] = "Сотрудники";
            $e->_output = $out;
        }
    }
}


Проблема кроется в том, что когда запускается обработчик какого-либо события (то есть плагин), в нем может произойти вызов другого события (и не один раз).

В первом случае в плагине на событие OnManagerWelcomeHome запускается сниппет DocLister, который инициирует событие OnParseDocument, его в свою очередь обрабатывает плагин TinyMCE4.

Во втором случае плагин SimpleGallery вызывает метод DocumentParser::parseProperties, чтобы получить возможность подменить параметры SimpleGallery в зависимости от шаблона или документа. При обработке параметров в parseProperties срабатывает событие OnParseProperties и обрабатывается приведенным выше плагином.

Для корректной обработки вложенных событий Михаил kassio доработал метод парсера invokeEvent, который сохраняет событие предыдущего уровня и восстанавливает его.

Здесь и кроется проблема: когда в первом плагине для вывода виджета или в SimpleGallery для встраивания кода в страницу используется конструкция $e->output, то $modx->event к этому моменту уже не то, что было, когда мы писали в начале плагина $e = $modx->event. В итоге вывод отправляется в никуда.

Для корректной работы в проблемных плагинах необходимо заменить $e->output на $modx->event->output. И отказаться от привычки экономить на спичках.

P.S. Небольшое дополнение
В 1.4.x обращение к свойству $modx->event->_output является некорректным. Если кто не знает, то так обходилось ограничение на возврат из плагина только строк. Для работы с этим свойством следует использовать методы setOutput() и getOutput():

$out = $modx->event->getOutput(); //получили
...
$modx->event->setOutput($out); //записали


Метод setOutput полностью перезаписывает свойство $modx->event->_output.

Также не рекомендуется использовать для возврата строк метод output(). Вместо него следует использовать метод addOutput().

4 комментария

avatar
Спасибо. Много времени многим людям сэкономили :)
avatar
Кстати, а 2.х, видимо, та же история? или нет?
avatar
Да.
avatar
Ох, какая знакомая ситуация.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.