Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Игра образцов. Как примирить Битрикс со сторонним шаблонизатором итога

Anna | 29.05.2014 | нет комментариев
PHP-разработкой я занимаюсь теснее достаточно давным-давно, и за это время обучился применять превосходства этого языка и чураться, по вероятности, его недостатков. Но что мне никогда не нравилось в PHP — это встроенный механизм шаблонизации. Обилие символов “<?php … ?>” и многословных языковых конструкций бьет по глазам, вероятность применения в образце произвольного PHP-кода не содействует соблюдению правила распределения логики и представления.

Следственно я признателен судьбе (и сообществу разработчиков, безусловно) за то, что существуют альтернативные движки шаблонизации, с значительно больше славным синтаксисом при тех же функциональных вероятностях. Ну, а от того что огромная часть PHP-планов у нас, в Центре Высоких Спецтехнологий, разрабатывается на Symfony2 Framework, то нашим любимым шаблонизатором стал Twig. Помимо указанных выше превосходств, он еще и беспредельно расширяемый, что дюже Зачастую помогает в работе.

Но жизнь частенько преподносит сюрпризы. Вот и на меня незадолго свалился маленький, но достаточно увлекательный план, делать тот, что необходимо было на… Битриксе! К счастью, трудиться с Битриксом мне теснее доводилось, но было это давным-давно (и выдумка), следственно я воспринял план как вероятность посмотреть на свой прошлый навык с новой точки зрения, применить собранные познания и навыки в несколько другом контексте.
И первое, что мне захотелось сделать — “прикрутить” Twig, Дабы не маяться с нативной шаблонизацией.

Вот что из этого получилось.

К счастью, Битрикс разрешает применять всякий шаблонизатор итога. Правда, только для образцов компонентов, образцы сайта все равно создаются на PHP. Для подключения шаблонизатора нужно объявить глобальную функцию (да-да, это Битрикс, детка), которая будет осуществлять рендеринг образца. Функция может выглядеть, скажем, так:

function renderTwigTemplate($templateFile, $arResult, $arParams, $arLangMessages, $templateFolder, $parentTemplateFolder, $template)
{
    echo TwigTemplateEngine::renderTemplate($templateFile, array(
        'params' => $arParams,
        'result' => $arResult,
        'langMessages' => $arLangMessages,
        'template' => $template,
        'templateFolder' => $templateFolder,
        'parentTemplateFolder' => $parentTemplateFolder,
    ));
}

Помимо того, функцию требуется зарегистрировать в глобальном массиве $arCustomTemplateEngines с указанием растяжения файла образца:

global $arCustomTemplateEngines;
$arCustomTemplateEngines["twig"] = array(
    "templateExt" => array("twig"),
    "function"    => "renderTwigTemplate"
);

В итоге, если в каталоге образца компонента находится файл с именем template.twig, будет вызвана функция рендеринга renderTwigTemplate(), на вход которой будут переданы все нужные данные: имя и путь к файлу образца, параметры вызова компонента, итог выполнения компонента, а также языковые константы для данного образца.
Как выяснилось, есть одна неприятная специфика: если в каталоге образца компонента единовременно находятся файлы template.twig и template.php, то применяться будет PHP-шный образец. Следственно, реализовать прекрасную неявную подмену типа образцов при подключении/отключении того либо другого шаблонизатора не получится.

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

class TwigTemplateEngine
{
    private static $twigEnvironment;

    public static function initialize($templateRootPath, $cacheStoragePath)
    {
        Twig_Autoloader::register();

        $debugModeOptionValue = COption::GetOptionString("htc.twigintegrationmodule", "debug_mode");
        $debugMode = ($debugModeOptionValue == "Y") ? true : false;

        $loader = new Twig_Loader_Filesystem($templateRootPath);
        self::$twigEnvironment = new Twig_Environment($loader, array(
            'autoescape' => false,
            'cache'      => $cacheStoragePath,
            'debug'      => $debugMode
        ));

        self::addExtensions();

        global $arCustomTemplateEngines;
        $arCustomTemplateEngines["twig"] = array(
            "templateExt" => array("twig"),
            "function"    => "renderTwigTemplate"
        );
    }

    private static function addExtensions()
    {
        self::$twigEnvironment->addExtension(new Twig_Extension_Debug());
        self::$twigEnvironment->addExtension(new BitrixTwigExtension());
    }

    public static function renderTemplate($templateFile, array $context)
    {
        return self::$twigEnvironment->render($templateFile, $context);
    }

    public static function clearCacheFiles()
    {
        self::$twigEnvironment->clearCacheFiles();
    }
}

Применение неподвижных способов и свойств класса в данном случае обусловлено архитектурой Битрикса: в нем нет механизма для размещения сервисных объектов, сходственного, к примеру, контейнеру сервисов из Symfony2.

Работа по инициализации шаблонизатора выполняется в способе initialize(). Подмечу, что в нашем случае подключение Twig инкапсулировано в отдельном модуле Битрикса. Это, во-первых, дало нам вероятность комфортного применения функционала на различных планах, а во-вторых, дозволило задавать некоторые конфигурационные параметры через административный интерфейс CMS. В частности, отладочный режим включается в зависимости от значения опции debug_mode, управление которой вынесено на страницу настроек модуля в админке Битрикса.
От того что речь зашла о конфигурационных параметрах, то дозволю себе сделать малое лирическое отхождение. Правило работы Twig заключается в дальнейшем: при первом обращении к образцу он компилируется в PHP-код, тот, что после этого исполняется при всех последующих обращениях. Файлы со сгенерированным кодом именуются кэшем образцов и помещаются в каталог, указанный в опции cache. При изменении начального кода образца, безусловно, кэш необходимо инвалидировать. Самый примитивный метод, тот, что традиционно используется при релизе нового функционала — это полная чистка каталога кэша, которая реалиуется вызовом способа Twig_Environment::clearCacheFiles() (в нашем модуле реализована обертка для этого способа, дозволяющая очищать кэш по нажатию кнопки в административном интерфейсе). Помимо того, Twig может механически пересоздавать кэш определенного образца при изменении его начального кода: для этого нужно установить опцию auto_reload в значение true. Но традиционно такой подход требуется только в режиме разработки, следственно взамен auto_reload дозволено установить опциюdebug, что даст такой же результат при работе с кэшем, а также дозволит применять отладочные вероятности Twig.
Кстати, кэш образцов Twig никак не связан и не конфликтует с кэшем образцов Битрикса, от того что в первом случае кэшируется PHP-код, а во втором — данные, полученные в итоге работы компонента и HTML-разметка.
В контексте Битрикса также оказалось значимым установить опцию autoescape в значение false, так как в функцию рендеринга передаются теснее экранированные данные.

Вызов способа инициализации выполняется в файле подключения модуля:

CModule::AddAutoloadClasses(
    'htc.twigintegrationmodule',
    array(
        'TwigTemplateEngine' => 'classes/general/templating/TwigTemplateEngine.php',
        'BitrixTwigExtension' => 'classes/general/templating/BitrixTwigExtension.php',
        'Twig_Autoloader' => 'vendor/Twig/Autoloader.php',
    )
);

// Initialize Twig template engine
$documentRoot = $_SERVER['DOCUMENT_ROOT'];
$cacheStoragePathOption = COption::GetOptionString("htc.twigintegrationmodule", "cache_storage_path");

if ($cacheStoragePathOption == "") {
    $cacheStoragePath = $documentRoot . BX_PERSONAL_ROOT . "/cache/twig";
} else {
    $cacheStoragePath = $documentRoot . $cacheStoragePathOption;
}

TwigTemplateEngine::initialize($documentRoot, $cacheStoragePath);

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

Выходит, шаблонизатор зарегистрирован и настроен, самое время начинать им пользоваться. И тут, как традиционно, не обошлось без подводных камней.
Во-первых, нередко в образцах компонентов Битрикса доводится применять некоторые битриксовые функции, а также всеобщии объекты (что сделать, издержки архитектуры CMS). К счастью, Twig, как я теснее подмечал, разрешает создавать личные растяжения, в которых дозволено описывать добавочные теги, фильтры, функции и т.д. Следственно было разработано малое расширение BitrixTwigExtension, предоставляющее доступ к API Битрикса в образцах. При этом мы постарались оставить доступным наименьший комплект API, Дабы оградить разработчиков от мечты реализовывать бизнес-логику в образцах.
После этого, позже длинных попыток осознать, отчего же в образец не передаются языковые константы, и дальнейшего постижения кода ядра CMS, стало ясно, что языковой файл образца должен иметь верно такое же имя, что и сам образец, включая растяжение. Это обозначает, что языковой файл образца template.twigдолжен также иметь имя template.twig, оставаясь при этом PHP-файлом! Что ж, необычное поведение, но, как выяснилось, от разработчиков Битрикса дозволено еще и не такого ждать.
Самым неприятным стало то, что при применении Twig-образцов не отрабатывал component_epilog(завершающий этап рендеринга образца в Битриксе, дозволяющий исполнить какие-либо действия самостоятельно от того, закеширован образец либо нет). Вновь постижение кода ядра — и очередное удивление: component_epilog подключается только к нативным образцам! Больше спорного решения в Битриксе, я еще, вероятно, не встречал. Исключительный доступный метод исправления данной обстановки — вручную вызывать component_epilog позже рендеринга образца:

function renderTwigTemplate($templateFile, $arResult, $arParams, $arLangMessages, $templateFolder, $parentTemplateFolder, $template)
{
    echo TwigTemplateEngine::renderTemplate($templateFile, array(
        'params' => $arParams,
        'result' => $arResult,
        'langMessages' => $arLangMessages,
        'template' => $template,
        'templateFolder' => $templateFolder,
        'parentTemplateFolder' => $parentTemplateFolder,
    ));

    $component_epilog = $templateFolder . "/component_epilog.php";
    if(file_exists($_SERVER["DOCUMENT_ROOT"].$component_epilog))
    {
        $component = $template->__component;
        $component->SetTemplateEpilog(array(
            "epilogFile" => $component_epilog,
            "templateName" => $template->__name,
            "templateFile" => $template->__file,
            "templateFolder" => $template->__folder,
            "templateData" => false,
        ));
    }
}

Позже проведенных доработок мы, наконец, получили подлинно пригодное к применению решение, которое упростило жизнь и мне (тот план, с которого все и началось, был удачно реализован), и моим коллегам, которым тоже понравилась простота и лаконичность Twig.
И, безусловно, мы не могли не поделиться итогом своих трудов. Модуль помещен в Bitrix Marketplace под комичным именем Твигрикс, он безусловно бесплатен и доступен для скачивания каждому интересующимся. А начальный код дозволено посмотреть на гитхабе. Мы от каждой души верим, что Твигрикс немножко украсит грозные будни грозных Битрикс-разработчиков.

 Источник: programmingmaster.ru
Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB