Рендеринг нативных PHP-представлений

«devanych/view-renderer» — это маленькая и простая в использовании PHP-библиотека, предназначенная для рендеринга нативных PHP-представлений. Несмотря на простоту, данный пакет дает возможность внедрения собственной функциональности.

Особенности пакета:

  • удобная плоская система блоков;
  • поддержка использования существующих PHP-функций;
  • установка глобальных параметров для всех представлений;
  • гибкий функционал шаблонов с неограниченной вложенностью;
  • легкая расширяемость при помощи добавления собственных функций.

Простейший пример рендеринга:

use Devanych\View\Renderer;

$renderer = new Renderer('/path/to/root/directory/of/views');

$content = $renderer->render('path/to/view/file', [
    'variableName' => 'значение переменной любого типа',
]);

echo $content;

Метод render() первым аргументом принимает путь к файлу представления, который должен быть относительно директории, переданной конструктору при создании объекта. Вторым аргументом передается массив параметров name => value), которые будут преобразованы в переменные внутри представления.

$renderer->render('post/show', [
    'post' => $posts->get($id),
]);

В представлении:

<h1><?=$post->getName();?></h1>

Файл представления может быть с расширением или без него, если расширение файла не указано, то по умолчанию подставится .php. Расширение по умолчанию можно изменить, передав его в конструктор вторым аргументом.

$renderer = new Renderer('/path/to/root/directory/of/views', 'tpl');

Внутри шаблонов и представлений экземпляр рендерера доступен в $this.

<title><?=$this->renderBlock('title');?></title>

Блоки

Методы block(), beginBlock() и endBlock() позволяют создавать блоки контента в вашем представлении. Содержимое блоков сохраняется для использования в любом месте представления, а также в любом родительском шаблоне. Метод renderBlock() рендерит сохраненный контент блока.

Код шаблона:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title><?=$this->renderBlock('title');?></title>
    <?=$this->renderBlock('meta');?>
</head>
<body class="app">
    <?=$this->renderBlock('content');?>
</body>
</html>

Код представления:

<?php

declare(strict_types=1);

/** @var Devanych\View\Renderer $this */

$this->layout('layouts/main');
$this->block('title', 'Page Title');
?>

<p>Page Content</p>

<?php $this->beginBlock('meta');?>
    <meta name="description" content="Page Description">
<?php $this->endBlock();?>

Результат рендеринга:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Page Title</title>
    <meta name="description" content="Page Description">
</head>
<body class="app">
    <p>Page Content</p>
</body>
</html>

Блоки нельзя вкладывать друг в друга, но можно рендерить один блок внутри другого.

<!-- Можно -->
<?php $this->beginBlock('parent');?>
    <!--  ...  -->
    <?=$this->renderBlock('children');?>
    <!--  ...  -->
<?php $this->endBlock();?>

<!-- Нельзя -->
<?php $this->beginBlock('parent');?>
    <!--  ...  -->
    <?php $this->beginBlock('children');?>
        <!--  ...  -->
    <?php $this->endBlock();?>
    <!--  ...  -->
<?php $this->endBlock();?>

Обратите внимание, что content является зарезервированным именем блока, он рендерит контент преставления и дочерних шаблонов. Если попытаться создать блок с именем content, то будет брошено исключение \RuntimeException.

При вызове метода renderBlock() с именем несуществующего блока, метод renderBlock() вернет пустую строку, поэтому дополнительные методы проверки существования блока не нужны. Вторым аргументом в renderBlock() можно передать значение по умолчанию, которое будет подставлено, если блока с указанным именем не существует.

<!-- Вывести контент блока, если он существует  -->
<?php if ($name = $this->renderBlock('name')): ?>
    <h1><?=$name;?></h1>
<?php endif;?>

<!-- Вывести контент по умолчанию, если блок не существует  -->
<?=$this->renderBlock('title', 'Default Title');?>
<!-- или используя другой блок  -->
<?=$this->renderBlock('title', $this->renderBlock('default-title'));?>

Шаблоны

Шаблоны — это особый тип преставлений, которые очень удобно использовать для рендеринга повторяющихся частей (хедер, футер, сайдбар и т.д.). Данный пакет позволяет строить цепочки шаблонов с неограниченной вложенностью. Наследование шаблона устанавливается в файле представления или дочернего шаблона методом layout().

Код родительского шаблона (layouts/main):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title><?=$this->renderBlock('title');?></title>
</head>
<body class="app">
    <?=$this->renderBlock('content');?>
</body>
</html>

Код дочернего шаблона (layouts/columns):

<?php

declare(strict_types=1);

/** @var Devanych\View\Renderer $this */

$this->layout('layouts/main');
?>
<main class="main">
    <?=$this->renderBlock('content');?>
</main>
<aside class="sidebar">
    <!-- Sidebar Code  -->
</aside>

Код представления (site/page):

<?php

declare(strict_types=1);

/** @var Devanych\View\Renderer $this */

$this->layout('layouts/columns');
$this->block('title', 'Page Title');
?>

<p>Page Content</p>

Рендеринг представления:

$renderer->render('site/page', [/* параметры */]);

Результат рендеринга:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Page Title</title>
</head>
<body class="app">
    <main class="main">
        <p>Page Content</p>
    </main>
    <aside class="sidebar">
        <!-- Sidebar Code  -->
    </aside>
</body>
</html>

Если необходимо просто отрендерить файл дочернего шаблона из родительского, можно воспользоваться методом render().

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title><?=$this->renderBlock('title');?></title>
</head>
<body class="app">
    <?=$this->render('layouts/_header.php')?>
		
    <?=$this->renderBlock('content');?>
		
    <?=$this->render('layouts/_footer.php', [
        'parameter' => 'value'
    ])?>
</body>
</html>

Результат рендеринга:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Page Title</title>
</head>
<body class="app">
    <header>Header</header>
    <p>Page Content</p>
    <footer>Footer</footer>
</body>
</html>

Расширения

Функционал расширений позволяет добавлять собственные функции и использовать их внутри шаблонов и представлений. Чтобы добавить функцию, нужно создать собственный класс, реализующий интерфейс «Devanych\View\Extension\ExtensionInterface». Интерфейс содержит всего один метод, возвращающий массив, ключами которого являются названия добавляемых функций, а значениями могут быть любые возможные варианты типа callable.

interface ExtensionInterface
{
    /**
     * Returns an array of functions as `function name` => `function callback`.
     *
     * @return array<string, callable>
     */
    public function getFunctions(): array;
}

Данный пакет содержит одно расширение «Devanych\View\Extension\AssetExtension», которое упрощает подключения ассетов (скриптов, стилей, шрифтов и т.д.) и добавляет метку времени к файлу. Для добавления расширения нужно воспользоваться методом addExtension().

$extension = new AssetExtension('/path/to/assets', 'https://examle.com/assets', true);
$renderer->addExtension($extension);

// Результат: 'https://examle.com/assets/css/style.css?v=1601742615'
$renderer->asset('css/style.css');

Глобальные параметры

Используя метод addGlobal(), можно добавлять глобальные параметры, которые будут доступны во всех представлениях и шаблонах.

// Переменная `$variableName` теперь будет доступна внутри файлов.
$renderer->addGlobal( 'variableName', 'значение переменной любого типа');

Повторное добавление переменной с одним и тем же именем вызовет исключение \RuntimeException, но есть возможность изменить значение глобальной переменной для конкретного представления, для этого нужно передать параметр с таким же именем при рендеринге представления или просто присвоить в самом представлении.

// Для всех представлений и шаблонов значение `$author` будет равняться `Boby`
$renderer->addGlobal( 'author', 'Boby');

// Только для представления `blog/page` значение `$author` будет равняться `John`
$renderer->render('blog/page', [
    'author' => 'John',
]);
// или присвоить значение в представлении
$author = 'John';

Экранирование

Для предотвращения вывода потенциально опасного HTML-кода рендерер содержит метод esc(), который экранирует специальные символы, а точнее, преобразует их в соответствующие HTML-сущности.

// Результат: '&lt;script&gt;alert(123);&lt;/script&gt;'
$renderer->esc('<script>alert(123);</script>');
// Эквивалентно: `htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', true)`

Данный метод сделан для удобства, но можно использовать и оригинальную функцию htmlspecialchars(), так как в предтавлениях и шаблонах доступны все существующие функци PHP.

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

Добавить комментарий

 Латинские или кириллические буквы, не меньше 3 и не больше 30 символов.
 E-mail никто не увидит.