Примечание переводчика: данная статья является переводом поста из блога компании Prosopo, разработчика Procaptcha - privacy-friendly альтернативы Google reCaptcha.
Мы рады представить вам наш новый пакет — PHP Views, Composer библиотеку, созданную в процессе работы над WordPress интеграцией для Procaptcha.
1. Что такое PHP Views?
PHP Views — это небольшой Composer пакет без зависимостей, созданный для того чтобы упростить работу с шаблонами в PHP. Он использует модельно-ориентированный подход, включает поддержку пространств имён и собственную реализацию Blade в качестве шаблонизатора по умолчанию.
Большинство PHP фреймворков имеет свои решения для шаблонов, но существует огромное количество PHP проектов, включая CMS вроде WordPress, где коробочные решения отсутствуют.
Обычные PHP шаблоны довольно многословны и подвержены ошибкам. PHP Views призван сделать работу с шаблонами в чистом PHP более простой, гибкой и надежной.
1.1) Несколько слов о Twig
Вы можете спросить: «А как же Twig от Symfony? Он ведь доступен как отдельный пакет!»
Это действительно так, и Twig — отличный инструмент. Однако он специализируется исключительно на шаблонах и не поддерживает модели. Это означает, что для передачи данных приходится использовать обычные массивы и строковые ключи.
Модельный подход в PHP Views решает эту проблему, добавляя в шаблоны преимущества объектно-ориентированного программирования.
Модели позволяют:
Использовать типизированные переменные — что гораздо надёжнее.
Инкапсулировать логику в методах моделей.
Избежать опечаток в ключах, которые сложно отследить при использовании строковых ключей в массивах.
Ещё один момент — в отличие от Blade, Twig не поддерживает нативный PHP-код и функции. Вместо этого приходится изучать его собственные фильтры и синтаксис, что может быть дополнительным препятствием.
О вкусах не спорят (no holy wars – just options)
У каждого инструмента есть плюсы и минусы. Мы использовали Twig в течение многих лет и ценим его сильные стороны. Но также видим и возможности для улучшения. И модельный подход из PHP Views может сделать ваш рабочий опыт с Twig ещё лучше.
1.2) Свобода выбора шаблонизатора
Возвращаясь к PHP Views: пакет включает собственный компилятор Blade и использует его по умолчанию.
Однако ключевой принцип пакета — гибкость. Вы можете подключить Twig или любой другой шаблонизатор всего за несколько строк кода — при этом сохранив преимущества модельного подхода и все функции, которые предлагает PHP Views.
1.3) Особенности автономной реализации Blade
Blade — это элегантный и мощный шаблонизатор, изначально созданный для Laravel. В отличие от Twig, он не ограничивает использование PHP, а наоборот — расширяет его возможности с помощью удобного синтаксиса, делая шаблоны проще и понятнее.
Blade предоставляет специальные сокращения, которые упрощают громоздкие конструкции, но при этом остаётся полноценным PHP с доступом ко всем его функциям.
К сожалению, Blade недоступен как отдельный пакет. Поэтому PHP Views включает собственную реализацию компилятора, которая поддерживает все ключевые функции Blade, при этом оставаясь полностью независимой от Laravel.
Возможно, вы встречали другие реализации, например jenssegers/blade, которые создают "заглушки" для Laravel зависимостей. Мы отказались от подобного подхода по нескольким причинам:
Требуется PHP 8.2 или выше.
Добавляются внешние зависимости.
Может ломаться интеграция при обновлениях Laravel (что уже случалось).
Нехватка гибкости — изначально реализация не создавалась как автономный компонент.
Использует глобальные функции Laravel, что мешает полной изоляции кода.
Благодаря отличной концепции Blade, наша реализация компилятора составила менее чем 200 строк кода.
2. Преимущества пакета
Прежде чем перейти к концепции и реализации, давайте посмотрим, что предлагает данный пакет:
Потрясающая производительность: Быстрее оригинального Blade из Laravel (см. бенчмарк ниже).
Отсутствие зависимостей: Лёгкая и простая интеграция в любой проект.
Широкая совместимость: PHP 7.4+, 8.0+
Надежная архитектура: Гибкая и модульная — можно легко переопределять компоненты под нужды проекта.
Поддержка пространств имён: Удобное управление различными шаблонизаторами через единый, структурированный подход.
Надёжность: каждый релиз проверен Pest тестами и PHPStan анализатором.
Несмотря на то, что список выше выглядит довольно обычно, настоящая сила PHP Views в его гибкости. Вы можете использовать пакет так, как необходимо именно вам:
В качестве поставщика шаблонов: Сочетайте модельно-ориентированный подход со встроенным движком Blade для создания простых и динамичных шаблонов.
Как самостоятельную Blade реализацию: Используйте нашу Blade реализацию для отображения своих Blade шаблонов.
Как модельный слой: Используйте модельно-ориентированный подход с любым шаблонизатором, например Twig или даже чистым PHP.
Как связующее звено: Объединяйте шаблоны, использующие разные движки, в единую систему.
2.1) Бенчмарк
Мы провели тест производительности PHP, чтобы сравнить PHP Views с Blade из Laravel (эмулируется через jenssegers/blade) и Twig. Ниже результаты для 1000 отображений:
Участник |
Первый рендеринг, мс |
Кешированный рендеринг, мс |
---|---|---|
prosopo/views (без моделей) |
19.75 |
19.75 (нет кеширования) |
prosopo/views (с моделями) |
43.78 |
43.78 (нет кеширования) |
illuminate/view (Blade из Laravel) |
181.24 |
56.77 |
twig/twig |
441.13 |
9.47 |
Цифры говорят сами за себя. Даже без кеша, с учётом дополнительных расходов на модели, реализация PHP Views значительно обгоняет конкурентов. Несмотря на мощный кеш в Twig, PHP Views быстрее, чем Blade из Laravel даже при кешировании.
Примечание: использовались следующие версии пакетов:
illuminate/view — 11.7.0
twig/twig — 3.17.1
jenssegers/blade — 2.0.1
Поскольку бенчмарк включён в репозиторий, вы можете легко запустить его локально:
git clone https://github.com/prosopo/php-views.git
composer install; cd benchmark; composer install
php benchmark {1000} — укажите количество отображений
3. Установка и минимальное использование
3.1) Установка
Поскольку PHP Views распространяется как Composer пакет, его установка просходит как обычно:
composer require prosopo/views
После установки убедитесь, что ваш проект подключает автозагрузчик Composer (если это ещё не сделано):
require __DIR__ . '/vendor/autoload.php';
3.2) Минимальная настройка
Для начала вам нужно создать три экземпляра: ViewTemplateRenderer, ViewNamespaceConfig и ViewsManager.
Основная конфигурация задаётся в ViewNamespaceConfig, где вы определяете папку с шаблонами и корневое пространство имён для моделей.
use Prosopo\Views\View\ViewNamespaceConfig;
use Prosopo\Views\View\ViewTemplateRenderer;
use Prosopo\Views\ViewsManager;
require __DIR__ . '/vendor/autoload.php';
// 1. Создаём рендерер шаблонов (по умолчанию используется встроенный Blade)
$viewTemplateRenderer = new ViewTemplateRenderer();
// 2. Конфигурация пространства имён
$namespaceConfig = (new ViewNamespaceConfig($viewTemplateRenderer))
->setTemplatesRootPath(__DIR__ . './templates')
->setTemplateFileExtension('.blade.php');
// 3. Создаём экземпляр менеджера представлений
$viewsManager = new ViewsManager();
// 4. Регистрируем пространство имён моделей
$viewsManager->registerNamespace('MyPackage\\Views', $namespaceConfig);
3.3) Определение модели
Теперь мы готовы создать первую модель. Как и во многих фреймворках (например, Laravel), здесь используется модельно-ориентированный подход к шаблонам.
Каждому шаблону соответствует своя модель. Публичные свойства и методы модели становятся доступными внутри шаблона как аргументы.
Класс модели должен расширять BaseTemplateModel или реализовывать интерфейс TemplateModelInterface:
namespace MyPackage\Views;
use Prosopo\Views\BaseTemplateModel;
class EmployeeTemplateModel extends BaseTemplateModel
{
public int $salary;
public int $bonus;
public CompanyTemplateModel $company;
public function total(): int
{
return $this->salary + $this->bonus;
}
}
Пример шаблона модели (используется Blade):
<p>
Ваш доход за месяц — {{ $total() }},
из них {{ $salary }} — зарплата, и {{ $bonus }} — бонус.
Расчёт налогов: {{ $company->calcTaxes($salary) }}
</p>
<p>Информация о компании:</p>
{!! $company !!}
Как видите, все публичные свойства модели доступны в шаблоне, включая вложенные модели, такие как $company. Вы также можете вызывать их публичные методы напрямую в шаблоне.
Класс BaseTemplateModel, от которого мы наследуемся, переопределяет метод __toString(), что позволяет отображать вложенные модели как строки через Blade вывод с поддержкой HTML.
Например: {!! $innerModel !!} отобразит модель как HTML-фрагмент.
Примечание: пакет не требует использовать суффикс Model в названиях классов. В этом примере он используется исключительно для наглядности.
3.4) Автоматическое сопоставление шаблонов
Встроенный ModelTemplateResolver автоматически сопоставляет шаблоны с моделями на основе имён классов и их пространств имён. Это избавляет от необходимости вручную указывать путь к шаблону.
Пример структуры:
-
src/
-
Views/
Homepage.php
-
Settings/
GeneralSettings.php
-
templates/
homepage.blade.php
-
settings/
general-settings.blade.php
-
Мы указываем корневую папку шаблонов и корневое пространство имён моделей в ViewNamespaceConfig (как делали выше):
$namespaceConfig = (new ViewNamespaceConfig($viewTemplateRenderer))
->setTemplatesRootPath(__DIR__ . './templates')
->setTemplateFileExtension('.blade.php');
$viewsManager = new ViewsManager();
$viewsManager->registerNamespace('MyPackage\Views', $namespaceConfig);
Примечание: имена шаблонов следует писать через дефисы. Имена моделей в стиле camelCase будут автоматически преобразованы в шаблоне в дефисы.
3.5) Использование
Экземпляр ViewsManager (который мы создали ранее) предоставляет методы createModel и renderModel.
Вы можете создать, настроить и отобразить модель за один шаг, указав callback-функцию в методе renderModel:
echo $viewsManager->renderModel(
EmployeeTemplateModel::class,
function (EmployeeTemplateModel $employee) use ($salary, $bonus) {
$employee->salary = $salary;
$employee->bonus = $bonus;
}
);
Пошаговое создание и отображение
Если вы хотите создать модель отдельно, используйте метод createModel, а отображение выполните позже:
$employee = $viewsManager->createModel(EmployeeTemplateModel::class);
$employee->salary = $salary;
$employee->bonus = $bonus;
echo $views->renderModel($employee);
Подсказка: вы также можете передавать callback как второй аргумент в методы createModel() и renderModel() — это позволяет настраивать модель перед возвратом или отображением.
Совет касательно типов аргументов
Класс ViewsManager реализует три интерфейса:
ViewNamespaceManagerInterface — для метода registerNamespace
ModelFactoryInterface — для метода createModel
ModelRendererInterface — для метода renderModel
Мы рекомендуем использовать эти интерфейсы как типы переменных в функциях, принимающих ViewsManager — это повышает читаемость и упрощает сопровождение кода.
Вот и всё! Сейчас вы уже готовы использовать данный пакет.
4. Заключение
Это был краткий обзор пакета PHP Views, созданного для упрощения работы с шаблонами в PHP. Если вас интересуют расширенные настройки, переопределение модулей и дополнительные возможности —загляните в официальный репозиторий.
Спасибо, что дочитали до конца! Надеемся, что данный пакет сделает вашу разработку проще и приятнее.
P.S. Если вам понравился PHP Views — поставьте ⭐️ его репозиторию на GitHub и поделитесь ссылкой с коллегами.
Комментарии (8)
FanatPHP
02.06.2025 13:49Чисто по подаче выглядит, как инфоцыганство.
Потрясающая производительность. Абсолютно непонятно, за счёт чего, если там везде чистый пхп. Я добавил в эти копеечные шаблоны HTML с главной страницы php.net, просто чтобы немного приблизить их к реал лайф. Результаты:
39.46 ms: Twig (with cache)
142.79 ms: Blade from Laravel (with cache)
205.84 ms: PHP Views without Models (using built-in Blade)
внезапно потрясающая производительность вся куда-то улетучилась.Отсутствие зависимостей. Ну да, а другие пакеты ставятся с трудом и страданиями.
Широкая совместимость на самом деле абсолютно стандартная
Надежная архитектура: баззворд
Поддержка пространств имён какая-то внутренняя фишка, которая в других шаблонизаторах не нужна.
Плюс какие-то прибитые гвоздями к шаблонам модели
Ну и
подписывайтесь на наш телеграм каналпоставьте нам звездочку, а то их там всего три штуки, то есть весь этот матёрый проект видело человек 5.
thekingoftheworld
02.06.2025 13:49Добрый день! Давно хотел узнать, в чем вообще смысл php-шаблонизаторов, как таковых.
Чем код шаблона лучше собственно кода php. Например, какие преимущества имеет вот это
<a href="{{ item.href }}">{{ item.caption }}</a>
против вот этого:
<a href="<?=item['href']?>"><?=item['caption']?></a>
Раньше я слышал версию, что шаблонизатор нужен, чтобы слабо подготовленный контентник не имел доступ к коду php, т.к. во первых, можно вынести шаблоны в отдельные файлы, во вторых, как будто, забытая "}}" не разрушит всю страницу в отличии от забытой "?>". Но теперь, когда в шаблоны протекает логика, циклы, условия и прочее наследования, кажется, что это обмен шила на мыло.
FanatPHP
02.06.2025 13:49Спасибо за хороший вопрос. Основных причин три
-
Автоматическое экранирование. Преимущество конкретно этого малюсенького кусочка кода в том, что он не идентичен примеру ниже, который для соответствия должен выглядеть как
<a href="<?=htmlspecialchars($item['href'])?>"><?=htmlspecialchars($item['caption'])?></a>
здесь мы убиваем сразу двух зайцев - самых беспечных (которые либо не знают, либо забывают про обязательное форматирование вывода в зависимости от контекста) и самых умных (которые считают, что некоторые переменные и так безопасные и их форматировать не обязательно.
И хотя автоматическое форматирование не панацея, и имеет уже свои недостатки, это огромный шаг вперёд по сравнению с вашим<?=item['caption']?>
. Наследование и блоки. В реальной жизни шаблоны оказываются чуть сложнее, чем ваш пример. Особенно - шаблон основного дизайна сайта, в котором бывает множество мелких исключений. Например, на определённой странице надо подгрузить специальный js или css. Или есть сайдбар, который в каждом разделе сайта отображает свою информацию. В чистом пхп такой сайдбар очень быстро превращается в кучу говнокода. А при использовании шаблонизатора мы в основном шаблоне заполняем этот блок дефолтной информацией, а в шаблоне конкретной страницы мы можем заменить или дополнить её.
Разделение ответственности. Увы, не каждый шаблонизатор может похвастаться следованием этому правилу - как, например, рассматриваемый в этой статье (что также снижает его привлекательность). Разделение это нужно не "контентнику" а самому программисту, который, если дать ему возможность писать в шаблоне на РНР, тут же перетянет половину бизнес-логики в шаблон. А потом кто-то другой будет мучаться, переводя сайт на новый дизайн или на SPA.
censor2005
02.06.2025 13:49Ну и не забываем про вкусный синтаксический сахар вроде
<div @class([ 'alert', 'alert-danger' => $is_error ])> </div>
или
<option value="{{ $version }}" @selected(old('version') == $version)>
-
leon-mbs
02.06.2025 13:49нет ничего проще Mustashe
FanatPHP
02.06.2025 13:49Наоборот.
Mustashe, к сожалению - это другая крайность, попытка спрятать в голову в песок и заявить, что логики отображения не существует. Очень хороший пример ситуации, когда противоречия между идеологией и реальностью решаются попытками подогнать реальность под идеологию. С закономерно анекдотическим результатом.Как в Mustashe выглядит цикл?
{{#block}}...{{/block}}
. А условный переход?...{{#block}}...{{/block}}
. Гениально, Ватсон! Буквально как в анекдоте, "В теории мы миллионеры", а в реальности логика в шаблоне есть, но при этом чудовищно нечитабельная: вместо явного цикла или явного условия - совершенно одинаковый синтаксис, суть которого можно понять только посмотрев тип переменной (и который гвоздями прибит к именам переменных). А дальше начинаются совсем уже танцы вприсядку со специальными именами блоков, типаblock_has_items
.Для любого непредвзятого наблюдателя все эти "logicless" шаблонизаторы - просто анекдот.
alexhu
02.06.2025 13:49Шаблонизатор позволяет подставлять значения в код php. При рендеренге код шаблонизатора преобразуется в php, который трансформируется в html и отдаётся клиенту. Ещё иногда шаблонизатор экранирует данные и можно немного их преобразовать, что тоже можно сделать чистым кодом php без всяких шаблонизаторов. Циклы, условия, очистка данных - наверное вообще не задача шаблонизатора , только библиотеки популярных шаблонизаторов раздуты до совсем уже экзотических методов, которые почти никем не используются. Но разработчики хотят развивать свои разработки и добавляют такое, что уже и понять зачем это и когда применять, бывает трудно.
Вес библиотеки шаблонизатора наверное под мегабайт или больше и популярные имеют десятки классов и сотню методов, из которых используются до десятка. Всё это требует время и вычислительных ресурсов. А мы же боремся за скорость работы скриптов?
В opencart, Laravel предусмотрены шаблонизаторы и лучше и нам их использовать. В wordpress шаблонизатора нет и wordpress прекрасно работает.
На форумах постоянно возникают споры, а нужен ли вообще шаблонизатор, если он используется в шаблоне и туда уже должны отдаваться подготовленные данные. То есть шаблонизатор мало что добавляет и в разы (по моим замерам - иногда до 30 раз) уменьшает скорость рендеренга страницы.
fso
Это конечно хорошо) Но много лет в разных проектах актуальна проблема шаблонизации docx, в основном из-за того, что просто распаковать архив и пропустить contents.xml через шаблонизатор - недостаточно. Ибо тэги шаблонов офисный редактор оборачивает в блоки, которые после "компиляции" грозятся стать не парными, невалидными или вовсе возникнет путаница со вложенностью. Таки документы офисом открываются с ошибками, с дикой текучкой памяти или не открываются вовсе... Сорри за оффтопик, но наболело)