Функция render template php
Как написать простейший шаблонизатор на php
Если углубиться в историю php (а он был написан как шаблонизатор в языке perl), то будет ясно, лучшего шаблонизатора, чем сам язык на нем не написать. Smarty и т.п. библиотеки, ограничены своим API. И работа с ними напоминает история про Active Record, которую я описал в предыдущей статье.
Итак, как можно написать простейший шаблонизатор, с тем же функционалом, что и в Yii.
Однажды, на страницах этого блога, уже была статья, про такой шаблонизатор. Однако, там был обзор готовой библиотеки. Сейчас, я расскажу подробнее о том, как оно работает.
Создадим класс, и сразу опишем методы, которыми он будет владеть.
Основа его работы простое подключение требуемого файла методом include, а чтобы переменные были доступны в нем самостоятельно, а не как части массива, используется подзабытая функция extract
Чтобы загнать содержимое файла в переменную, используется известный прием с ob_start() ob_get_clean(). Это такие функции, которые загоняют стандартный поток вывода в другие рельсы. Говоря другим языком, они буферизируют вывод. Подобно тому, как в windows данные помещаются в буфер обмена. Подробнее про это можно почитать здесь
Вот и вся магия. Теперь опишем метод render
Но оставлю это для вашей фантазии.
В рамках статей о написании идеального велосипеда фреймворка добавим в класс контроллера, эти два метода. Отличать их будет только параметр static, перед названием метода. Он нам больше не понадобится. Так как для каждого контроллера будет создан свой экземпляр.
Вы все еще не форкнули этот фреймворк? Чего же вы ждете? Давайте сделаем идеальный фреймворк вместе.
Как ко мне пришла идея
В vfsStream обработчики потоков используются разными способами. Изучая их, я заметил две интересные:
Синтаксис
Чтобы шаблонизатор соответствовал второму и третьему требованию, нужно использовать комментарии HTML.
Недостаток заключается в том, что здесь больше кода. Но он может быть быстро написан, используя горячие клавиши комментариев в редакторе или IDE ( обычно Ctrl + / или Cmd + / ).
Структура проекта
Звучит запутанно? Но с помощью vfsStream это становится тривиальной задачей по сравнению с традиционным методом создания лексеров/парсеров, токенизаторов и разработкой некоторой формы AST ( абстрактного синтаксического дерева ).
Далее создадим в корне проекта папку под названием src. Внутри нее разместим два файла классов ( Engine.php и Syntax.php ). Структура папок должна выглядеть следующим образом:
Чтобы получить рабочую версию шаблонизатора, нужно следующее:
Сначала создадим наше представление с простым PHP кодом, чтобы убедиться, что переменные передаются в представление:
Теперь можно сфокусироваться на простейшей реализации класса шаблонизатора на PHP ( Engine.php ).
Так мы получим рабочую версию метода render, хотя и без желаемой функциональности.
Теперь требуется повторно сгенерировать файлы автозагрузки и файл composer.lock :
Реализуем логику получения представления для отображения с параметрами, которые мы передали.
Вы увидите сообщение о том, что сервер запущен. Откройте браузер, перейдите по адресу http://localhost/ и вы должны увидеть:
Шаблонизация в PHP
Шаблонизация — это работа по интеграции готовой, статичной вёрстки на сайт.
Ведя работу над сайтом, написанном на PHP, мы разрабатываем логику, которая управляет представлением.
Логика — это PHP-сценарии, где происходит объявление переменных, функций, использование массивов и циклов, получение и обработка информации.
Представление — это внешний вид сайта. Сюда входят его страницы, которые содержат общие элементы оформления (шапка, меню, подвал) и контент (страницы, статьи и тому подобное).
Имея на руках готовую вёрстку, надо правильно её внедрить в работающий сайт, чтобы в определённых местах страниц информация выводилась динамически, под контролем PHP-сценариев.
Что такое «шаблон» и причём тут рыба?
Шаблон — это готовая вёрстка страницы или блока, которая состоит только из оформления, и не содержит никакого контента (полезной информации).
Шаблон по своей сути — это обычный PHP-сценарий, который на 90% состоит из HTML-кода и только на 10% из PHP-конструкций. Основная задача программиста в процессе работы над сайтом — это превратить статичные HTML-страницы в динамичные PHP-шаблоны, которые будут использоваться для показа итоговых страниц.
Но если статичная вёрстка страниц не содержит динамического контента, то что будет на его месте до начала внедрения этих страниц? Когда дизайнер или верстальщик хотят показать как будет выглядеть страница на сайте, то вместо реального контента используется так называемая «рыба».
Рыба — это заполнитель. Бессмысленный текст, который используется в вёрстке, чтобы показать как будет выглядеть страница, наполненная контентом.
Проблемы типичного процесса вёрстки
Когда верстальщик работает над несколькими страницами, ему приходится копировать бол́ьшую часть HTML-кода между страницами, так как эти страницы содержат много повторяющихся элементов: подключение стилей, шапка сайта с логотипов, футер сайта, различные меню и тому подобное.
В результате, когда дело доходит до правок, то при необходимости поменять что-нибудь в той же шапке, это изменение нужно будет сделать во всех HTML-файлах, чтобы они выглядели одинаково. Конечно, такой мартышкин труд никому не понравится.
Общие фрагменты страниц сайта
Взглянув на страницы практически любого сайта, можно заметить их сходство друг с другом. Ведь все страницы состоят из общих частей, которые не меняются, а также из областей с уникальным содержимым. И как было бы удобно редактировать общие блоки, вроде шапки сайта, отдельно, а затем вкладывать их во все страницы. Тогда при смене логотипа потребуется внести изменение только в одном месте, а все страницы сайта обновятся автоматически.
Шаблонизация — это и есть деление всей вёрстки на независимые шаблоны и дальнейшее их подключение и вкладывание друг в друга.
Сбор страницы из частей
Если поделить всю вёрстку сайта на отдельные, небольшие шаблоны мы получим сразу несколько преимуществ. Станет легче следить за единообразием интерфейса. Выделив представление сайта в шаблоны, мы также существенно упростим PHP-сценарии, ведь в них останется только PHP-код. Можно даже доверить верстальщику редактировать непосредственно шаблоны, так как в них почти нет программного кода, а тот, что есть, очень простой.
Потренируйтесь собирать страницы из фрагментов, выполнив это задание.
Термины шаблонизации
В разговоре о шаблонизации очень важно придерживаться определённых терминов, чтобы всегда было понимание, о чём идёт речь. Договоримся использовать следующую терминологию:
Лейаут — это шаблон, который содержит HTML-код, общий для всех страниц сайта. В нём могут содержаться подключение стилей, мета-теги, шапка, подвал. Также лейаут содержит область для вставки уникального содержимого каждой страницы.
Шаблон страницы — это шаблон с уникальным для одной страницы HTML-кодом. Например, для главной страницы там может быть список новостей. Также шаблон страницы может включать в себя блоки.
Блок — это шаблон очень небольшого блока страницы. Например, это может быть один элемент списка новостей. Удобство блоков в том, один блок могут включать разные страницы.
Содержимое шаблона
Что же находится внутри шаблона? Вы уже знаете, что шаблон почти полностью состоит из HTML-кода. Но помимо HTML-тегов, там также лежат данные и простая логика.
Данные в шаблоне
Шаблон показывает динамическую информацию. Прилагательное «динамическая» означает, что эти данные могут меняться и показываться в зависимости от различных условий. Сама информация, как правило, хранится в базе данных, а PHP-сценарий извлекает её оттуда и передаёт в шаблон.
Вне зависимости от источника информации есть правило, которое никогда нельзя нарушать: любой шаблон (лейаут, шаблон страницы, блок) должен иметь доступ только к тем данным, которые ему явно передали.
Такую изоляцию данных обеспечивает специальная функция-шаблонизатор, о которой пойдёт речь далее.
Логика шаблона
В шаблоне должна находиться только несложная логика. Иными словами, шаблоны не содержат «тяжёлого» PHP-кода, а только простые конструкции.
Так, в шаблоне можно показывать переменные, использовать условия, циклы, обходить массивы, вызывать функции и подключать файлы. Всё другое запрещено. Оставшаяся бизнес-логика остаётся в PHP-сценариях, которые вызывают шаблоны и передают в них информацию.
Функция-шаблонизатор
Шаблонизатор — это функция, которая подключает файл шаблона, передаёт ему данные и возвращает сгенерированный HTML.
Именно шаблонизатор является тем клеем, что скрепляет воедино отдельные шаблоны в итоговую страницу. Работает он следующим образом: PHP-сценарий страницы выполяет все действия для подготовки необходимой информации, к примеру, запрашивает записи из базы данных. Эти записи в виде массива отправляются шаблонизатору вместе с именем шаблона страницы.
Шаблонизатор подключает указанный файл шаблона и передаёт туда всю информацию. Но, вместо вывода на экран содержимого этого шаблона, он захватывает получившийся HTML-код и возвращает его.
Затем сценарий вызывает шалбонизатор ещё раз, но теперь с его помощью подключает общий лейаут, куда отправляется общая информация, а также содержимое страницы, полученное из предыдущего шага. Весь результат работы выводится на экран.
Пример использования
Посмотрим на примере, как это всё работает. Начнём с того, что определим три шаблона: лейаут, шаблон страницы и какой нибудь блок.
Напоминаю, что в лейаут выносим общий HTML-код.
[layout.php]
Теперь очередь за шаблоном страницы:
[main.php]
Здесь обрати внимание, что шаблон страницы помимо интерации по массиву, для каждого его элемента вызывает функцию-шаблонизатор. Шаблонизатор получает контент из шаблона блока и показывает его внутри списка.
Как сделать шаблонизатор на php?
Придерживаюсь мнения что лучщий шаблонизатор для PHP это сайм PHP но есть вопрос: как его правильно реализовать.
Ну т.е. условно страница у нас отрабатывает и есть набор данных в аккаунте которые нужно показать пользователю:
title страницы, имя юзера, баланс, таблица с данными.
Вот где почитать как архитекрутно правильно это сделать?
Да понятно MVC, но. у меня нет контроллера. Запрос приходит на роутер, который определяет какой вызывать класс обработчик (это жирная модель по сути), класс всё готовит и должен выплюнуть результат в шаблон по идее. Передавать шаблонизатору он через массив вероятно всё будет, но как это лучше делать, как определить для этого набора данных можно использовать сущствующий шаблон или надо создавть новый? Как таблицы шаблонизаирова если в одной 3 столбца, в другой 5? Или 4 и 4, но у первой первый столбец должен быть 70% а у второй последний?
Проблема решается, если вывод HTML делать после вывода заголовков. Например, использовать буфер
Однако так придётся писать в каждом месте, где формируется HTML. Можно ли сократить?
Простейшее представление через буфер
Не правда ли, с представлениями код становится значительно изящнее. А если логика для реализации представлений хранится в отдельном файле-библиотеке, то код становится короче и понятнее.
В фреймворках вместо функции render может использоваться объектная реализация
Как таблицы шаблонизаирова если в одной 3 столбца, в другой 5?
Никто в представлениях не запрещает использовать языковые конструкции. Это могут быть конструкции языка PHP или какой-нибудь другой язык, специально написанный для шаблонизатора. Например, в Smarty, Blade, Pug свои языки. Передай в представление количество колонок и сделай цикл 🙂
у первой первый столбец должен быть 70% а у второй последний?
В представлениях можно подключать стили CSS, через которые меняется отображение (колонка 70%).
У каждого представления может быть свои стили.
Если нужно, чтобы были некоторые общие стили и дополнительные, нужные только для этой страницы, используют что-то типа буферизирования вывода блока стилей.
Перевод: Шаблонизаторы в PHP
Первод статьи, автором которой является Fabien Potencier, ведущий разработчик и идеолог Symfony. Статья поднимает обсуждение о шаблонизаторах PHP в целом и представляет Twig — быстрый и функциональный шаблонизатор.
————————————————————————
Итак, вы считаете что PHP это уже шаблонизатор? Так думал и я… и очень долго. Но недавно я передумал. Несмотря на то что PHP может использоваться как шаблонизатор, его синтаксис ужасен для этих целей.
На протяжении нескольких лет я продвигал наиболее успешные концепции веб-разработки, одной из которых является практика отделения отображения от логики приложения. Под моим влиянием, как ведущего разработчика Symfony, все проекты в Sensio спроектированы с учетом MVC архитектуры. Несомненно, это упрощает работу нескольких человек над одним проектом. Программисты работают над кодом (контроллеры и модель) и веб-дизайнеры (верстальщики) над отображением.
Шаблонизатор должен способствовать выполнению концепции разделения. Язык шаблонизатора должен быть достаточно гибким и мощным чтобы упростить внедрение логики отображения, не дав возможности травмировать логику приложения расширенными возможностями.
И когда я несколько дней назад спросил о лучших и популярных шаблонизаторах на PHP в Твиттере, некоторые, естественно, ответили «сам PHP». Это было вполне предсказуемо, ведь несколько недель назад я бы ответил так же.
Так почему же PHP (уже) не является удобным шаблонизатором?
Почему люди до сих пор думают что PHP это шаблонизатор? По-большей части, потому что PHP начал свое существование как язык-шаблонизатор, но в последнее годы он развивался другим путем. Если вы считаете что PHP до сих пор является шаблонизатором, расскажите мне хотя бы об одном изменении в языке, которое улучшило его как шаблонизатор? Я не могу вспомнить ни одого.
Шаблонизаторы стремительно развивались начиная с 1995 года, когда вышел первый релиз PHP/FI:
И значимым фактом является то, что PHP не поддерживает множество возможностей современных шалонизаторов.
Я буду использовать Django как пример современного шаблонизатора в своих примерах по причинам которые вы поймете позже.
Ниже описано то что я хочу видеть в современном шаблонизаторе:
Краткость
PHP многословен. Только для того чтобы просто вывести переменную необходимо не менее 14 символов (нет, использование более компактного непреемлемо):
И PHP становиться нелепо многословен при необходимости экранировать вывод (да, экранирование данных полученных из небезопасного источника является обязательным)
Сравнити это с двумя примерами написанными на Django:
Синтаксис ориентированный на шаблоны
По больше части это дело вкуса, но шаблонизаторы должны иметь красивые решения частых случаев. Для примера предположим, что необходимо отобразить массив и вывести текст по умолчанию, если этот массив пуст. Это очень распространенный случай, но версия на PHP не очень читаема:
Версия на Django гораздо лучше — спасибо конструкции else для тега for :
Повторное использование
PHP развивался с учетом возможности повторного использования кода. Начиная с 5-й версии реализация объектов сделала шаг вперед и, после того как в следующей версии будут поддерживаться типажи, мы получим язык программирования удовлетворяющий наши требования. Я действительно рад всем этим изменения т.к. это помогает мне писать программы лучше, но это не имеет значения когда все что тебе нужно — это написать шаблон.
В Django ввели наследование шаблонов несколько лет назад, пытаясь повторить наследование классов в шаблонах:
Это элегантно, просто для восприятия и действительно мощно. Настолько мощно, что многие шаблонизаторы сейчас поддерживают эту возможность «из-коробки».
Безопасность
Я не говорю что PHP небезопасен. Но я хочу сказать что экранирование переменных в шаблоне это просто кошмар, как и продемонстрировал ранее:
Конечно, вы можете написать свою функции и сделать ее котороче, но это не то о чем я говорю:
Я считаю что безопасность должа быть на уровне по умолчанию, особенно для шаблонов, которые зачастую пишут не программисты, которые не обращают внимания на возможные XSS и CSRF уязвимости.
Насколько я знаю, Symfony был самым первым фреймворком поддерживающим автоматическое экранирование в шаблонах (2006); и все основные фреймворки пошли тем же путем: в Django по умолчанию включено экранирование начиная с версии 1.0, и в Ruby on Rails это тоже появится с версии 3.
Включенное автоматическое экранирование так же означает что аудировать(?) приложение гораздо проще. Взгляните на примеры с отключенным экранированием:
Конечно, я в курсе возможных проблем с автоматическим экранированием. Вам все так же нужно быть внимательными при экранировании переменных в JavaScript, но это гораздо проще чем экранировать абсолютно все вручную.
Режим «Песочницы»
Эта возможность особенно необходима в том случае когда пользователи могу сами редактировать шаблоны сайта (например, из системы управления сайтом). Это не является необходимость, однако бывает важно. Исполнение шаблонов в «песочнице» означает возможность ограничить то что может быть выполенно: методы/функции которые можно вызывать, теги которые можно использовать и т. д.
Ни у PHP, ни у Django нет такого режима, так что продолжайте читать эту статью для дальнейшего объяснения этой секции.
Альтернативные шаблонизаторы для PHP
Итак я приступил к поиску шаблонизатора который имел бы все возможности упомянутые выше. Я нашел много разных шаблонизаторов, но ни один из них не удовлетворил все мои нужды. Следующий раздел о некоторых из них, среди которых есть и рекомендованные мне ответами в Твиттер.
На PHP написан миллион шаблонизаторов, я тестировал только самые «популярные». И т. к. я их не использую мной могут быть допущены ошибки использования того или иного шаблонизатора.
Smarty и Smarty 3
Это то что первым пришло в голову. И сам язык шаблонов Django был «навеян» Smarty. Smarty это стандарт де-факто для PHP.
PHPTAL
PHPTAL очень приятный проект, использующий синтаксис шаблонов Zope. Очень хорошо спроектирован, имеет большое количество возможностей, но не умеет работать с не-HTML шаблонами, что может стать проблемой, если вы хотите использовать один язык шаблонов также для писем и RSS лент.
Еще я думаю что синтаксис может оказаться непонятным веб-дизайнерам, особенно при использовании расширенных возможностей, таких как наследование шаблонов:
Одним из важных достоинств можно назвать автодополнение в среде разработки и гарантировано «правильно сформированный» HTML код.
eZ Components Templates
Компонент шаблонов в составе eZ Components так же предоставляет очень удачный языком шаблонизатора. Здесь присутвуют почти все возможности шаблонизаторов… даже чресчур, если вы хотите знать мое мнение:
Наследование шаблонов не поддерживается, и меня беспокоит скорость работы. Это самая медленная библиотека что я тестировал, и сильно медленне остальных.
Dwoo — это, прежде всего, интересный проект. Он позиционирует себя как альтернативу Smarty. И их работа стоит внимания:
Dwoo подражает Smarty, но также вносит и свои дополнения, например, наследование шаблонов, и работает гораздо быстрее Smarty.
К сожалению, у Dwoo нет «песочницы» и ядро библиотеки недостаточно гибкое.
Calypso
Calypso это реализация шаблонизатора Django на PHP. Я упоминаю о нем т.к. он клон Django и на него ссылались в Твиттере. Но сам автор считает что его реализация неудачна.
Когда я приступил к поиску PHP шаблонизатора, я сосредоточился на библиотеках копирующих поведение Django. Спустя несколько часов «гугления» был найден Twig. Его автором является Armin Ronacher, известный по проекту of Jinja (шаблонизатор для Python). Несомненно я испытываю крайнее уважение к Армину за его замечательную работу над Jinja. Twig скорее похож на Jinja, чем на Django, как описано в реализации.
Он написал Twig в 2008 году для платформы блогов Chypr. Но больше не возвращался к разработке и больше занимался разработкой на Python.
Когда я взглянул на код, я сразу понял что это то что я ищу. Главное отличие от Calypso в том что Twig компилирует шаблоны в обычный PHP-код. Я начал использовать эту библиотеку и в конце этой недели спросил у Армина не желает ли он дать своему проекту новую жизнь. Его ответ был полон энтузиазма, и я приступил к изучению кода. Моя версия сильно отличается от версии Армина, но «лексер» и «парсер» практически оригинальные.
| Библиотека | Время (сек) | Память (Кб) | Шаблонов в секунду |
|---|---|---|---|
| Twig | 3 | 1 190 | 3 333 |
| PHPTAL | 3.8 | 2 100 | 2 632 |
| Dwoo | 6.9 | 1 870 | 1 449 |
| Smarty 2 | 12.9 | 2 350 | 775 |
| Smarty 3 | 14.9 | 3 230 | 671 |
| Calypso | 34.3 | 620 | 292 |
| eZ Templates | 53 | 5 850 | 189 |
Я тестировал простые шаблоны содержащие 1 вывод и итерацию трех элементов, декорируя простым макетом. Время — среднее для 10 запусков; состоящее из 1 компиляции и 10 000 отображений (рендеринга). Для библиотек, не поддерживающих наследование был использован шаблон подключающий шапку (header) и подвал (footer) отдельно и, в библиотеках не поддерживающих автоматического экранирования, экранирование было выполнено вручную.
В случае запуска с уже скомпилированными шаблонами потребление памяти, конечно, гораздо меньше для всех шаблонизаторов, и Twig использует меньше всех памяти.
| Библиотека | Память без компиляции (Кб) |
|---|---|
| Twig | 383 |
| PHPTAL | 598 |
| Dwoo | 1 645 |
| Smarty 2 | 1 634 |
| Smarty 3 | 1 790 |
| Calypso | 614 |
| eZ Templates | 2 783 |
Если вы хотите узнать больше, то посетите сайт, также можете принять участие в обсуждении или стать одним из разработчиков подписавшись на список рассылок для разработчиков.
————————————————————————
Это мой первый перевод. Не только для хабра, а вообще. Поэтому приветствуются комментарии не только про шаблонизаторы и Twig, но и про сам перевод.
