1. Приветствуем Вас на неофициальном форуме технической поддержки XenForo на русском языке. XenForo - коммерческий форумный движок от бывших создателей vBulletin, написанный на PHP.

Использование неименованных шаблонов в хуках

Тема в разделе "Вопросы и ответы по XenForo Framework", создана пользователем infis, 16.07.2011.

Загрузка
  1. infis

    infis Местный

    Регистрация:
    27.06.11
    Сообщения:
    5 966
    Симпатии:
    3 548
    Версия XF:
    1.5.9
    Есть где-нибудь вменяемая и полная документация по шаблонам XenForo?
     
  2. infis

    infis Местный

    Регистрация:
    27.06.11
    Сообщения:
    5 966
    Симпатии:
    3 548
    Версия XF:
    1.5.9
    Ладно. Как я понял, документации нет вообще.
    Тогда, возможно, кто-нибудь сможет ответить, как получить доступ к переменной из шаблона. К примеру, нужно получить значение sessionStart. Проблема в том, что я не знаю, как добраться до $template->_params['session']['sessionStart']. Переменная взята "от балды". Если бы $template был бы массив, то в шабоне без проблем можно было бы сослаться как $template.__params.session.sessionStart, но $template является объектом. Ниже пример начала массива переменных, доступных в шаблоне.
    Код:
    Array
    (
        [hookName] => post_public_controls
        [contents] =>
                    <a href="threads/%D0%A7%D0%B5%D0%B3%D0%BE-%D0%B2%D0%B0%D0%BC-%D0%BD%D0%B5-%D1%85%D0%B2%D0%B0%D1%82%D0%B0%D0%B5%D1%82.2374/" class="item muted postNumber hashPermalink" title="Постоянная ссылка">#1</a>
    
                        <a href="threads/%D0%A7%D0%B5%D0%B3%D0%BE-%D0%B2%D0%B0%D0%BC-%D0%BD%D0%B5-%D1%85%D0%B2%D0%B0%D1%82%D0%B0%D0%B5%D1%82.2374/reply?quote=108855" data-postUrl="posts/108855/quote" class="ReplyQuote item control reply" title="Ответить, цитируя это сообщение"><span></span>Ответить</a>
    
        [hookParams] => Array
            (
            )
    
        [template] => XenForo_Template_Public Object
            (
                [_templateName:protected] => thread_view
                [_params:protected] => Array
                    (
                        [session] => Array
                            (
                                [sessionStart] => 1312100974
     
  3. Yoskaldyr

    Yoskaldyr Пользователь

    Регистрация:
    27.09.10
    Сообщения:
    1 921
    Симпатии:
    1 163
    Версия XF:
    1.0.4
    В шаблоне у Вас есть доступ к переменным переданным в шаблон, т.е. в Вашем примере - $template->_params['session']['sessionStart'] - в шаблоне будет {$session.sessionStart}

    P.S. $template->_params - это как раз и есть массив переменных доступных в шаблоне
     
  4. infis

    infis Местный

    Регистрация:
    27.06.11
    Сообщения:
    5 966
    Симпатии:
    3 548
    Версия XF:
    1.5.9
    Эх. Если бы было так просто. Это работает в именованном шаблоне, но не работает в шаблоне, который я через compileParsed делаю. Собственно, оттуда и вопрос прозвучал. Кстати, далеко не всегда структура массива именно такая, как я показывал выше. Почему-то для разных хуков и шаблонов используются различные массивы переменных. Например, видно, что $hookParams в хуке post_public_controls пустой, тогда как в хуке forum_view_threads_before этот массив содержит именно _params объекта $template.
    В общем-то это выяснилось чисто случайно, когда я дал решение, как можно в конкретном разделе печатать объявление, используя мой плагин. Там без проблем можно было добраться до нужной переменной. А в другом случае, когда мне потребовалось вытащить переменные пути, это уже не работало.
    Вручную копировать массив $template->_params в $hookParams нереально, так как там по факту присутствуют рекурсии, что резко повышает требования к объему памяти php. Да и глупо это. А делать неуниверсальный вариант обработки хуков - вообще очень не правильно.
     
  5. Yoskaldyr

    Yoskaldyr Пользователь

    Регистрация:
    27.09.10
    Сообщения:
    1 921
    Симпатии:
    1 163
    Версия XF:
    1.0.4
    Вы неправильно ставите вопрос. Не синтаксис шаблонов и как передать переменные в шаблон, а помогите решить проблему, которую сам себе создал.
    Вся прелесть шаблонов ксена, что в них все необходимые переменные передаются в момент создания шаблона и только те переменные, которые необходимы шаблону, а не все подряд. Шаблон видит только то что ему передали, т.к. шаблон представление каких либо конкретных данных, а именно параметров передаваемых в него.
    Но шаблон надо создавать, а не как у Вас в хаке - все обработки в обход создания шаблона. У Вас используется прямой вызов XenForo_Template_Compiler.
    Хотя правильнее было использовать именованные шаблоны записываемые в кеш шаблонов на лету с именами , которые никак не будут пересекаться с теми что уже есть в базе.
    т.е. для примера при использовании в темплейт хуках
    PHP:
    $template->setTemplate('mySuperTemplate'$templateValueCode); //записываем в память шаблон, чтобы шаблонизатор знал о его существовании
    $contents .= $template->create('mySuperTemplate'$viewParams); //добавляем в конец контента хука результат вывода шаблона.
    И все :)

    Для любого вью класса аналогично :)
     
  6. infis

    infis Местный

    Регистрация:
    27.06.11
    Сообщения:
    5 966
    Симпатии:
    3 548
    Версия XF:
    1.5.9
    Вы предлагаете для каждого хука, вернее, всех обработчиков хука прописывать шаблоны? А смысл?
    Я писал свой менеджер как раз для того, чтобы избежать создания полноценного плагина на каждый "чих". Теперь Вы предлагаете создавать на каждый "чих" и шаблон, тогда как он без проблем компилируется из единого места. Осталось только понять, чем руководствовались разработчики, когда в разных ситуациях совершенно по-разному передаются параметры в хуки. Все должно быть унифицировано, но расширяемо.
    И еще. Почему нельзя создать шаблон, используя доступный класс? Вернее, почему Вы считаете, что создание шаблона из класса неправильно, а создание того же шаблона из админки правильнее? Кеширование шаблонов происходит в обоих случаях. Также в обоих случаях используется уникальное имя (идентификатор) именно для этого.
    Вот вы решили, что я создаю себе проблемы с тем, чтобы затем их мужественно преодолевать. А Вы не задумывались, что прямая правка шаблонов без использования хуков чревата последующими проблемами обновлений? Но на каждый случай вставки кастомного HTML писать свой полноценный обработчик хука - это, по меньшей мере, неразумно. Могу навскидку привести нужные мне случаи использования:
    1) вставка яваскриптов, ссылок и прочего для счетчиков;
    2) аналогично для постоянных банеров;
    3) простейшие расширения (в моем случае добавление кнопки "Вверх" в каждом сообщении);
    4) подгрузка нужного яваскрипта и размещение вызова, например, панели внешнего ресурса.

    Применений может быть много. К сожалению, вставка своего кода возможна либо через правку шаблона, либо через написание своего обработчика хука. И тот и другой варианты не являются простыми и беспроблемными. Вот я и упрощаю себе жизнь. Да, есть некоторые нюансы при доступе к переменным, но, уверен, их можно победить с помощью разработчиков или самостоятельно, изучив работу большинства вьюверов.

    P.S. Шаблоны видят намного больше переменных, чем те, которые явно прописаны в дефолтных шаблонах. Отсутствие документации к шаблонам как раз об этом нам и говорит, так как описание всех доступных переменных для разработчиков выглядит явно утомительным, да еще и потребует актуализацию при обновлениях. Во время обработки шаблона доступны все глобальные переменные, включая массивы и объекты. Обработка происходит только для тех переменных, которые указаны в шаблоне, а остальные переменные парсером не обрабатываются. Это благоприятно сказывается на производительности.
     
  7. Yoskaldyr

    Yoskaldyr Пользователь

    Регистрация:
    27.09.10
    Сообщения:
    1 921
    Симпатии:
    1 163
    Версия XF:
    1.0.4
    Вы внимательно прочитали что я написал? Код что я предложил как раз и создает шаблон через класс в нужном месте кода. Но именно создает шаблон, а не использует шаблон созданный в админке. Просто для шаблона нужно имя-идентификатор и все. В админке его как небыло так и не будет. Просто я говорю что надо использовать правильные классы, а в Вашем случае как раз и получаем, что сначала создаем проблемы, а потом героически их решаем.
    Насчет применения я вообще ничего не говорил. Я говорил только о коде.

    Также пару слов а видимости. При классическом ООП и большом наборе разнообразных классов глобальные переменные - зло (в большинстве случаев). Единственные переменные которые всегда будут это суперглобальные переменные по типу $_SERVER и т.д.

    Ксен глобальные переменные не использует совсем. В шаблонах ксена видны локальные переменные внутри парсера, которые получаются из входящего набора параметров + набор некоторых переменных которые используются по умолчанию (xenOptions, session и т.д.), точный список этого набора переменных по умолчанию можно посмотреть в методе preRenderView классов XenForo_Dependencies_* (список будет разный для различных классов - Public/Admin)

    P.S. И совет прежде чем спорить Вы бы сначала посмотрели и проверили что делает код что Вам дает оппонент, а потом уже начинали спорить.
    P.P.S. Скорее всего Вы очень далеки от оптимизаций в плане производительности, если хотите можем поспорить и по этому вопросу :))))
     
  8. infis

    infis Местный

    Регистрация:
    27.06.11
    Сообщения:
    5 966
    Симпатии:
    3 548
    Версия XF:
    1.5.9
    Прошу прощения за резкость. А теперь по существу.
    Код, который Вы предложили, не работает. Попробуйте. Мне очень даже интересно, получится ли у Вас в хуке таким методом создать шаблон с произвольным именем и из произвольного кода в синтаксисе шаблонов ксена.
    Глобальные переменные - практически всегда зло, да. Область видимости переменных в шаблонах ксена ограничены, как я понимаю, шаблоном. Но, так как хук вызывается из шаблона, то в хуке переменные, переданные в шаблон, также становятся видимыми. Это, думаю, не обсуждается. И мы здесь, естественно, не спорим.
    Я говорил, прежде всего, о том, что странно, когда в разных шаблонах используются разные имена массивов переменных, а в хуке вообще что-то попадает в виде стандартного массива $hookParams, а что-то туда не попадает. Я не могу найти логического объяснения такой неунифицированности.
    Если бы я не пробовал сделать аналогично Вашему пример, я бы не стал изобретать велосипед, вероятно. Сейчас попробовал опять Ваш пример - увы, сыпятся ошибки, до сути которых я не добрался. Именно поэтому я и делал это через парсер, а не через шаблонизатор.
    Кстати, о производительности. Я бы с удовольствием послушал бы Вас относительно своего кода. Если мой код действительно столь плох, возможно, есть варианты его улучшения. Я буду Вам очень признателен. Если Вы заметили, я не стесняюсь спрашивать обо всем, хотя в некоторых областях могу быть более продвинут. Данная область - написание плагинов - для меня слишком неизвестна. Поэтому позволю себе даже слегка и "потупить". Не возражаете? :)
     
  9. Yoskaldyr

    Yoskaldyr Пользователь

    Регистрация:
    27.09.10
    Сообщения:
    1 921
    Симпатии:
    1 163
    Версия XF:
    1.0.4
    Извините, но я дал только пример, т.е. показал направление куда копать. Ошибки конечно могут быть, ведь для того чтобы не вываливалось никаких ошибок весь код должен быть валидным - и код пхп и код шаблона. А т.к. я не знаю какой у Вас остальной код и что именно у Вас в шаблоне, то не могу дать точный ответ почему у вас вываливаются ошибки.
    Ведь Вы во первых не захотели докапываться до сути ошибки (да в ксене это не быстро, т.к. довольно много файлов надо просмотреть чтобы понять как работает) и даже не дали текст и описание ошибки, чтобы это сделали за Вас.

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

    Насчет производительности - темплейт хуки в текущем варианте как это есть в ксене - зло (даже разработчики с этим согласны, но они их сделали, т.к. надо что-то было сделать и быстро - и текущие темплейт хуки самое быстрое что можно было сделать на тот момент). Основное зло только потому что почти каждый хак или плагин их юзает и все в реалтайме. Т.е. например если мне надо через ajax подгрузить что-то очень небольшое и вдруг в моем шаблоне как-то всплывет хук (например в инклуде шаблона), то тогда будет обращение к файлам обработчиков хуков всех плагинов использующих темплейт хуки (которых большинство). Это хорошо если стоит что-то типа xcache и в максимальной производительности (т.е. без теста изменений файла на диске), но в большинстве случаев будет перебор довольно большого количества файлов, что совсем не есть хорошо.
    Лично я считаю, что в таком случае в плане производительности более предпочтительно использовать правку шаблонов - да гемор при обновлении, зато все работает значительно быстрее при большом количестве хаков.

    P.S. Небольшое дополнение к моему коду, который несколькими постами выше - $viewParams - это не параметры полученные на входе в хук, а произвольный набор данных который надо передать в наш шаблон
     
  10. infis

    infis Местный

    Регистрация:
    27.06.11
    Сообщения:
    5 966
    Симпатии:
    3 548
    Версия XF:
    1.5.9
    Не могу добиться выполнения Вашего примера:
    Код:
    Parse error: syntax error, unexpected $end in /var/www/xenforo/library/XenForo/Template/Abstract.php(262) : eval()'d code on line 1
    Код обработчика:
    PHP:
    $template->setTemplate('mySuperTemplate'"Получилось"); //записываем в память шаблон, чтобы шаблонизатор знал о его существовании
    $contents .= $template->create('mySuperTemplate'$hookParams); //добавляем в конец контента хука результат вывода шаблона.
    Вместо $hookParams передавал с таким же успехом и пустой массив.

    Где может быть ошибка?
     
  11. Yoskaldyr

    Yoskaldyr Пользователь

    Регистрация:
    27.09.10
    Сообщения:
    1 921
    Симпатии:
    1 163
    Версия XF:
    1.0.4
    Наконец я понял почему у нас постоянные непонимания :)
    Когда я говорил о валидном шаблонном синтаксисе, я имел ввиду синтаксис скомпилированного шаблона (т.е. это фактически php-код и глянуть его можно в таблице xf_template_compiled).
    К примеру, account_signature_preview выглядит так
    PHP:
    $__output '';
    $__output .= '<div class="section">
        <h3 class="subHeading">' 
    'Preview' '</h3>
        <div class="messageText primaryContent baseHtml">' 
    $signatureParsed '</div>
    </div>'
    ;
    Да - это моя ошибка, т.к. судя по Вашему коду понятно что Вы хотите использовать синтаксис исходного, еще нескомпилированного шаблона. Т.е. тогда конечно нужен и лексер и парсер, как в Вашем варианте. Но потом все равно лучше прогонять через setTemplate и create, чем через простой eval. Оверхед небольшой (особенно в сравнению с работой лексера и парсера), но он позволяет передать любые данные в скомпилированный код шаблона.
    Т.е. использование $template->setTemplate в принципе позволяет вставлять произвольный php код в любое место, где есть шаблонные хуки и также позволяет вставлять произвольный код в вывод во вью классе.

    Единственная проблема Вашего подхода, что лексер и парсер в ксене не очень быстры. В ксене это не вызывает проблем, т.к. отрабатывается это только в админке, а в Вашем случае это отрабатывается на каждый запрос и по несколько раз. Как вариант, решением этой проблемы может быть хранение в настройке и скомпилированного и оригинального шаблона
     
  12. infis

    infis Местный

    Регистрация:
    27.06.11
    Сообщения:
    5 966
    Симпатии:
    3 548
    Версия XF:
    1.5.9
    Ну вот теперь становится яснее :) Т.е. в setTemplate хранится уже '$__output = ...', а мне, действительно, нужно "на лету" компилировать.
    Но Вы не совсем правы в плане быстродействия. Дело в том, что в случае использования лексера и парсера данные в шаблоне могут быть любые и без явной передачи. С одним условием - в шаблоне нельзя добраться до свойств объекта. Т.е., при желании их использовать, действительно, проще будет через setTemplate прогнать с подстановкой нужного массива в параметрах. Но в разных случаях это могут быть разные массивы, что не поддается унификации.
    Спасибо за подсказку с setTemplate, так как таким образом можно и PHP-код вместо шаблона использовать, что может быть востребовано.

    Возвращаясь к производительности, хочу напомнить о том, что на самом деле парсер с лексером кешируются, поэтому на каждый запрос обработка будет происходить лишь один раз в пределах одной страницы. Чтобы убедиться в этом, я специально замерял и выводил время выполнения лексера и парсера. Вот цифры:
    Код:
    Операция                        new XenForo_Template_Compiler  lexAndParse          compileParsed        eval 
    Время выполнения первой         0,0229229927063000             0,0199320316315000   0,0657160282135000   0,0000739097595215 
    Время выполнения последующих    0,0005571842193602             0,0040739536285420   0,0004388332366942   почти не меняется 
    (усредненное) 
    Разница (кол-во раз)            41,14                          4,89                 149,75               ≈1
    
    Таким образом, производительность по сравнению с именованными шаблонами не должна сильно пострадать, которые используются в админке. При сохранении шаблона в админке, если я не ошибаюсь, также происходит лишь валидация, а для этого шаблон прогоняется через лексер и парсер, которые в автомате кешируются. Использование именованного шаблона далее в плагинах лишь на одну операцию выигрывает (первый прогон через лексер и парсер), что по сравнению с другими задачами уже не будет являться сколь нибудь существенным.
     
  13. CyberAP

    CyberAP Местный

    Регистрация:
    05.10.10
    Сообщения:
    2 604
    Симпатии:
    1 660
    Версия XF:
    1.5.10
    Предлагаю переименовать тему в "Переменные в шаблонах XenForo", т.к. к синтаксису эта тема отношения не имеет.
     
  14. infis

    infis Местный

    Регистрация:
    27.06.11
    Сообщения:
    5 966
    Симпатии:
    3 548
    Версия XF:
    1.5.9
    Тогда уж "Использование неименованных шаблонов в хуках".
     
  15. Yoskaldyr

    Yoskaldyr Пользователь

    Регистрация:
    27.09.10
    Сообщения:
    1 921
    Симпатии:
    1 163
    Версия XF:
    1.0.4
    У Вас добавляется 0.1 сек только на парсер и лексер. Учитывая что страница должна генериться меньше 0.1 сек, то это очень много.
    Насчет кеширования - это понятно. Вернее там не кеширование как таковое, а повторное использование класса - результат лексера и парсинга не кешируется.
    Хотя скорее всего при использовании пхп опкод кешей, такой проблемы почти не будет, но все равно все будет зависеть от размера шаблона который надо скомпилировать и какие конструкции там будут использоваться. Время парсинга и компиляции может значительно возрасти.

    Более логично компилировать шаблон на стадии сохранения настройки в админке.
     
  16. infis

    infis Местный

    Регистрация:
    27.06.11
    Сообщения:
    5 966
    Симпатии:
    3 548
    Версия XF:
    1.5.9
    У меня сервер - одно название. Поэтому и время такое. На нормальном хостинге или сервере, естественно, цифры будут значительно меньше. У меня страница генерируется в среднем около 0.5 секунды на голом движке без плагинов при наполненной базе :) Если прикинуть, что должно быть порядка 0.05 секунды на страницу, то это даст разницу на порядок (в 10 раз). Таким образом, добавится лишь около 0,01 секунды. Хотя да, сильно будет зависеть от сложности шаблона.
    Я уже говорил, что не всегда правка или создание нового шаблона удобнее. Хотя, можно будет предусмотреть и вариант использования именованных шаблонов в плагине.
    Завтра попробую использовать именованный шаблон из хука. Посмотрю, насколько будет разница в цифрах.
     

Поделиться этой страницей