• Блог
  • Меняем механизм парсинга

С выходом MODX версии 2.7.0, в котором был принят мой PR, появилась возможность менять механизм парсинга документов. В статье я указал один из возможных примеров использования — для сайтов, использующих шаблонизатор Fenom, отключить стандартный парсинг MODX. Это позволит избежать коллизий работы двух парсеров. Давайте на простом примере разберём, как это можно сделать.

Первый вариант — это создать свой кастомный класс. Как это сделать описано в документации. Там несколько шагов — создать класс, настроить поведение в админке, создать процессоры и контроллеры. Задача достаточно трудоёмкая. Но если хочется иметь возможность выбирать тип документа в дереве ресурсов (например, в контекстном меню), то придётся заморочится. В том случае, если нужно всего лишь изменить правила парсинга на сайте для всех документов, рассмотрим упрощенный вариант.

Давайте в плагине при загрузке документа просто подменять класс. Всего несколько строчек, которые меняют всё! Но перед созданием плагина давайте создадим наш новый класс. Назовём его fenomDocument. Файл можно разместить в любом месте. В нашем случае положим его в core/classes/fenomdocument.php.

<?php
class fenomDocument extends modResource {
    /**
     * Overrides modResource::__construct to set the class key for this Resource type
     * @param xPDO $xpdo A reference to the xPDO|modX instance
     */
    function __construct(& $xpdo) {
        parent :: __construct($xpdo);
        $this->set('class_key','fenomDocument');
    }

    public function parseContent($data = array())
    {
        $oldResource = $this->xpdo->resource;
        $this->xpdo->resource = $this;
        if (!empty($data)) {
            $scope = $this->xpdo->toPlaceholders($data, '', '.', true);
        }
        $this->_output = $this->_content;
        $this->_output = $this->xpdo->getParser()->pdoTools->fenom($this->_output, $this->xpdo->placeholders);
        $this->xpdo->resource = $oldResource;
        if (isset($scope['keys'])) $this->xpdo->unsetPlaceholders($scope['keys']);
        if (isset($scope['restore'])) $this->xpdo->toPlaceholders($scope['restore']);
        $this->_processed= true;
        return $this->_output;
    }
}

/**
 * Class fenomDocument_mysql
 */
class fenomDocument_mysql extends fenomDocument {}

Для простоты сюда же добавил класс для mySql. Чтобы обойтись одним инклюдом.

Теперь нужно подключить класс. Создаём плагин fenomDocument и отмечаем для него события «OnWebPageInit» и «OnLoadWebDocument».

<?php
switch ($modx->event->name) {
    case 'OnWebPageInit':
        include_once MODX_CORE_PATH . 'classes/fenomdocument.php';
    	// Добавляем в карту классов.
    	$modx->map['fenomDocument'] = [
            'extends' => 'modResource',
            'fields' => [],
            'fieldMeta' => [],
        ];
        break;
    case 'OnLoadWebDocument':
        $data = $modx->resource->toArray();
        $modx->resource = $modx->newObject('fenomDocument');
        $modx->resource->fromArray($data, '', true, true);

        /*
        // Если нужно работать только с ресурсами класса modDocument, то используем соответствующее условие.
        if ($modx->resource instanceof modDocument) {
            ....
        }
        */
        break;
}

Пришла очередь протестировать полученное. Создайте страницу с тегами MODX и Fenom и откройте её на фронте. В результате должно получится, что первые остались нетронутыми, а вторые замечательно распарсились. И всё работает по правилам шаблонизатора — контент парсится один раз. А значит и пресловутый тег ignore будет работать на любом уровне вложенности.

Тут надо ещё отметить, что данный механизм парсинга предназначен только для контента ресурса. Т.е. шаблон MODX не учитывается. Чтобы оставить парсинг шаблона, раскомментируйте код в методе fenomDocument::prepare(). Правда в этом случае парсить шаблон будет парсер MODX. Ну а в следующей статье мы рассмотрим возможность работать с шаблонами Fenom.

Данное решение работает так:

  • Если есть шаблон, то парсятся только кэшируемые теги MODX. Если есть теги Fenom, то они будут распарсены на следующем шаге.
  • В подготовленном контенте ресурса парсятся только теги Fenom.

Заключение

Описанное выше решение является демонстрацией возможностей. Аналогично можно реализовать парсинг и для парсеров Smarty и Twig.

Кстати, не все знают, что админка использует шаблонизатор Smarty и в ней не работает парсер MODX.

Update 01.12.2018

Чтобы не путаться, оставил вариант с парсингом шаблона. Таким образом, если в шаблоне указать кэшируемые теги MODX, они будут распарсены. А следующей статье откажемся и от шаблонов MODX, а соответственно и полностью от его парсера.

Update 05.12.2018

С прискорбием сообщаю, что всё-таки специфика MODX не позволяет использовать только парсер Fenom. Если с чанками и сниппетами ещё можно как-то работать (в компилированный файл попадает результат modX::getChunk() и modX::runSnippet()), то конструкции типа $_modx->resource.content так и компилируются. И, соответственно, выводится только контент ресурса без обработки парсером. А если дополнительно парсить такие конструкции, то вернёмся опять к тому, что было. Вот такой получается результат исследований.

0   3388

Комментарии ()

  1. shock 02 декабря 2018 # 0
    Спасибо за пример. Ждём следующей статьи.
    И ещё вопрос. Как быть с документами имеющими отличный от modResource, например с ресурсами Минишопа?
    1. Сергей Шлоков 02 декабря 2018 # 0
      Пожалуйста!

      В примере я специально указал класс modDocument для демонстрации возможности фильтрации ресурсов. Для полноценного отказа от парсера MODX фильтрацию нужно убрать. Т.е. обрабатывать все документы без исключения.
    2. Alex 11 декабря 2018 # 0
      А что со скоростью? каков прирост? было бы неплохо посмотреть результаты -) потому как если даже на 50мс будет быстрее, я сразу буду разбираться как внедрить-)
      1. Кирилл 09 мая 2019 # 0
        Вопрос немного не по теме.
        Как вы используете content ресурса?
        то конструкции типа $_modx->resource.content так и компилируются.
        Даже лучше стандартного поведения, ведь контент менеджер не сломает ничего, когда решит написать фигурные скобки.

        Единственное место где я использую вызов сниппета прямо в контенте это страница sitemap, но можно просто создать новый шаблон.
        1. Сергей Шлоков 09 мая 2019 # 0
          Здесь обычные ресурсы. Вообще, в MODX нет каких-то прописанных правил о том, как работать с ресурсами. Работают кто как умеет. А во фремйворках вся логика прописывается во вьюхах (файлах), а контент подгружают из базы. Т.е. в MODX по аналогии нужно всю логику вынести в шаблоны, а контент оставить для менеджеров.
          Правда это не спасёт от ошибок фенома.
          Вообще, из MODX нужно выпиливать родной парсер. Его практически не развивали. Его функционал крайне слабенький. Сейчас есть несколько мощных шаблонизаторов, выбирай не хочу. Но выпилить его в ближайшее время вряд ли получится. А пользоваться описанными выше костылям не внушает серьёзности системе. Выкрутиться из положения — да. Но для нормальной работы этого мало.
          1. Кирилл 09 мая 2019 # 0
            А в чем может возникнуть проблема в вашем решении? Как я уже сказал выше меня полностью устраивает что не будет парситься контент ресурса.
            1. Сергей Шлоков 09 мая 2019 # 0
              Да особо никаких. Просто моё решение несколько костыльное, но зато простое. А стандартный вариант сложноватый.
              1. Кирилл 10 мая 2019 # 0
                Все таки есть одна проблема. Не работают страницы ошибок (404, 403, 503)
                1. Сергей Шлоков 11 мая 2019 # 0
                  В смысле не работают?
                  1. Кирилл 11 мая 2019 # 0
                    Fatal error: Uncaught Error: Call to a member function fromArray() on null in /var/www/vhosts/u4221466.plsk.regruhosting.ru/new.veleskotel.ru/core/cache/includes/elements/modplugin/16.include.cache.php:15 Stack trace: #0 /var/www/vhosts/u4221466.plsk.regruhosting.ru/new.veleskotel.ru/core/model/modx/modscript.class.php(76): include() #1 /var/www/vhosts/u4221466.plsk.regruhosting.ru/new.veleskotel.ru/core/model/modx/modx.class.php(1668): modScript->process(NULL) #2 /var/www/vhosts/u4221466.plsk.regruhosting.ru/new.veleskotel.ru/core/model/modx/modrequest.class.php(133): modX->invokeEvent('OnLoadWebDocume...') #3 /var/www/vhosts/u4221466.plsk.regruhosting.ru/new.veleskotel.ru/core/model/modx/modx.class.php(1200): modRequest->prepareResponse() #4 /var/www/vhosts/u4221466.plsk.regruhosting.ru/new.veleskotel.ru/core/model/modx/modx.class.php(1239): modX->sendForward('5', Array, false) #5 /var/www/vhosts/u4221466.plsk.regruhosting.ru/new.veleskotel.ru/core/model/modx/modrequest.class.php(109): modX->sendErrorPage() #6 /var/www/ in /var/www/vhosts/u4221466.plsk.regruhosting.ru/new.veleskotel.ru/core/cache/includes/elements/modplugin/16.include.cache.php on line 15
                    Вот что выходит, когда я ввожу несуществующий адрес

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

        Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.