Template engine class php

Создание движка шаблонов на PHP — Рендеринг и Эхо

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

Прежде чем начнём писать код, необходимо позаботиться о самой важной части любого проекта по программированию — дать имя проекту. Я назову его Stencil

Сами шаблоны будут на простом PHP. Мы не будем создавать какой-либо специальный синтаксис, такой как Twig или Blade , мы сосредоточимся исключительно на функциональности шаблонов.

Начнём с создания основного класса.

class Stencil 

public function __construct(
protected string $path,
) >
>

Классу Stencil необходимо знать, где находятся шаблоны, чтобы они передавались через конструктор.

Чтобы на самом деле отображать шаблоны, понадобиться метод render() .

class Stencil 

// .

public function render(string $template, array $data = []): string

// ?
>
>

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

Теперь нужно сделать три вещи:

  1. Сформировать путь к запрашиваемому шаблону.
  2. Убедится, что шаблон существует.
  3. Отобразить шаблон с предоставленными данными.
class Stencil 

// .

public function render(string $template, array $data = []): string

$path = $this->path . DIRECTORY_SEPARATOR . $template . '.php';

if (! file_exists($path))
throw TemplateNotFoundException::make($template);
>

// ?
>
>

Первые два пункта списка легко сделать. Stencil будет искать только .php файлы, поэтому формирование пути — этого всего лишь случай объединения строк. Если запрошенный шаблон содержит какие-либо разделители каталогов, будут обрабатываться вложение шаблонов в каталоги.

Если файл шаблона не существует, выбрасываем исключение TemplateNotFoundException .

Чтобы охватить третий пункт в списке, фактически отображающий шаблон, нужно создать новый класс Template . В нём будут размещены все методы, доступные для шаблона, и будет обрабатываться реальная сторона рендеринга.

class Template 

public function __construct(
protected string $path,
protected array $data = [],
) >

public function render(): string

// ?
>
>
class Stencil 

// .

public function render(string $template, array $data = []): string

$path = $this->path . DIRECTORY_SEPARATOR . $template . '.php';

if (! file_exists($path))
throw TemplateNotFoundException::make($template);
>

return (new Template($path, $data))->render();
>
>

Чтобы получить отображаемый шаблон в виде строки, мы воспользуемся буфером вывода PHP. Когда вызывается ob_start() , PHP начинает захватывать всё, что приложение пытается вывести (эхо, HTMl и т.д.).

Мы можем получить это как строку, а затем прекратить захват вывода с помощью ob_get_clean() . Комбинация этих двух функций и include позволит оценить файл шаблона.

class Template 

// .

public function render(): string

ob_start();

include $this->path;

return ob_get_clean();
>
>

Это обработает рендеринг, но не даст шаблону доступ к данным переменных, хранящихся внутри $data . PHP, будучи замечательным языком, предоставляет ещё одну функцию, extract() , которая принимает массив пар ключ-значение.

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

class Template 

// .

public function render(): string

ob_start();

extract($this->data);

include $this->path;

return ob_get_clean();
>
>

Идеально! Теперь мы можем рендерить шаблон и предоставить ему доступ к предоставленным переменным. Есть одна вещь, которую мы не учли… если бы мы захотели создать несколько переменных внутри метода render() , наш шаблон также смог бы получить к ним доступ. Это не то, что мы хотим!

Для решения этой проблемы необходимо обернуть extract() и include /включить вызовы в немедленно вызываемое замыкание — таким образом, шаблон будет иметь доступ только к переменным внутри замыкания.

class Template 

// .

public function render(): string

ob_start();

(function ()
extract($this->data);

include $this->path;
>)();

return ob_get_clean();
>
>

Последняя часть головоломки — метод экранирования значений при их отображении. Замыкания наследуют $this , это означает, что наш шаблон сможет вызывать любой метод определённый в классе Template . Создадим метод e() , принимающий значение и экранирующий его с помощью htmlspecialchars() .

class Template 

// .

public function e(?string $value): string

return htmlspecialchar($value ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
>
>

Таким образом, у нас есть небольшой движок шаблонов для наших PHP проектов.

Приведённый выше шаблон можно рендерить с помощью нашего движка:

$stencil->render('hello', [ 
'name' => 'Ryan'
]);

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

Источник

Build Your Own Template Engine in PHP — Rendering & Echo

This post will focus on rendering the template and echoing out data that can be escaped with htmlspecialchars() .

Before we start writing code we need to take care of the most important part of any programming project — giving the project a name. I’m going to call it Stencil.

The templates themselves will all be plain PHP. We won’t be creating any special syntax like Twig or Blade, we’ll focus solely on templating functionality.

We’ll begin by creating the main class.

class Stencil  public function __construct( protected string $path,  ) <> > 

The Stencil class will need to know where the templates are located — so that gets passed in through the constructor.

To actually render our templates, we’ll need a render() method.

class Stencil  // .  public function render(string $template, array $data = []): string   // ?  > > 

The render() method accepts the name of the template and an array of data variables that will be accessible inside of said template.

We now need to do three things:

  1. Form a path to the requested template.
  2. Make sure the template exists.
  3. Render the template with the provided data.
class Stencil  // .  public function render(string $template, array $data = []): string    $path = $this->path . DIRECTORY_SEPARATOR . $template . '.php'; if (! file_exists($path))  throw TemplateNotFoundException::make($template);  > // ?  > > 

The first two on the list are easy to do. Stencil will only look for .php files so forming a path is just a case of concatenating some strings. If the requested template contains any directory separators, that will handle the nesting of templates inside of directories.

If the file doesn’t exist, throw a custom TemplateNotFoundException .

To cover the third point in the list, actually rendering the template, we’ll want to make a new class called Template . This will house all of the methods available to the template and handle the real rendering side of things.

class Template  public function __construct( protected string $path, protected array $data = [],  ) <> public function render(): string   // ?  > > 
class Stencil  // .  public function render(string $template, array $data = []): string    $path = $this->path . DIRECTORY_SEPARATOR . $template . '.php'; if (! file_exists($path))  throw TemplateNotFoundException::make($template);  > return (new Template($path, $data))->render();  > > 

To obtain the rendered template as a string, we’ll take advantage of PHP’s output buffers. When you call ob_start() PHP will start to capture anything that the application attempts to output ( echo , raw HTML, etc).

You can retrieve that as a string and then stop capturing the output using ob_get_clean() . A combination of these two functions and an include will let us evaluate a template file.

class Template  // .  public function render(): string   ob_start(); include $this->path; return ob_get_clean();  > > 

This will handle rendering the template, but it doesn’t do anything to let the template access those data variables stored inside of $data . PHP being the wonderful language it is provides another function called extract() that accepts an array of key value pairs.

The key for each item in the array will be used to create a new variable in the current scope using the associated value. Since include and its relatives always execute the PHP file in the current scope, the template will be able to access the extracted variables.

class Template  // .  public function render(): string   ob_start(); extract($this->data); include $this->path; return ob_get_clean();  > > 

Perfect! Now we can render a template and give it access to the data variables provided. There is one thing that we haven’t considered. if we wanted to create some variables inside of the render() method, our template would also be able to access those. That’s not what we want!

To solve that problem, we need to wrap the extract() and include calls in an immediately-invoke closure — that way, the template will only have access to the variables inside of the closure.

class Template  // .  public function render(): string   ob_start();  (function ()  extract($this->data); include $this->path;  >)(); return ob_get_clean();  > > 

The final piece of the puzzle is a method for escaping values when echoing them. Closures inherit $this which means our template will be able to call any method we define on the Template class. Let’s create an e() method that accepts a value and escapes it using htmlspecialchars() .

class Template  // .  public function e(?string $value): string   return htmlspecialchar($value ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');  > > 

And like that we have a little template engine for our PHP projects.

The template above can be rendered using our engine:

$stencil->render('hello', [ 'name' => 'Ryan' ]); 

And will output the following HTML:

In a future blog post we’ll implement support for partials, allowing us to separate out common templates and use them in multiple places.

Stencil is open-source on GitHub too if you want to look at the source code.

Источник

Читайте также:  Conda python install ubuntu
Оцените статью