Простой шаблонизатор на PHP

Небольшой урок о том,  как написать простой шаблонизатор на php (аналогичный тому что используется в yii).

Для удобства напишем его используя ООП и будем использовать php7. Для начала создадим каркас класса View:

class View
{
    private $path = __DIR__ . '/templates/';

    public $data = [];

    public function setData(array $data) : View
    {
        //
    }

    public function render(string $templateName) : string
    {
        //
    }
}

Здесь $data будет содержать параметры которые будут переданы в шаблон. Единственный метод render() будет компилировать шаблон и возвращать готовый результат. Это позволит, например, рендерить страницу из множества модульных шаблонов:

echo (new View)
    ->setData([
        'cart' => 
            (new View)
            ->setData([...])
            ->render()
    ])
    ->render();

Опишем реазилацию для метода setData(). Он позволит не заменять данные а сливать уже указанные данные с теми что переданы в агрументе:

public function setData(array $data) : View
{
    $this->data = array_merge($this->data, $data);

    return $this;
}

А теперь напишем сам метод рендера. В php для этого есть функции ob_start(), ob_get_clean() для работы с буфером. На самом деле мы будем просто запускать выполнение интерпретатора и писать результат в буфер, а затем очищать буфер и возвращать результат.

public function render(string $templateName) : string
{
    $path = $this->path . $templateName;

    if (!file_exists($path)) {
        throw new Exception('Template file ' . $path . ' does not exists');
    }

    extract($this->data); 
    ob_start(); 
    include($path); 
    return ob_get_clean(); 
}

Логика работы проста: проверим существует ли файл и если он существует, то вызовем функцию extract() и скомпилируем шаблон.

На выходе у нас получается вот такой класс:

class View
{
    private $path = __DIR__ . '/templates/';

    public $data = [];

    public function setData(array $data) : View
    {
        $this->data = array_merge($this->data, $data);

        return $this;
    }

    public function render(string $templateName) : string
    {
        $path = $this->path . $templateName;

        if (!file_exists($path)) {
            throw new Exception('Template file ' . $path . ' does not exists');
        }

        extract($this->data); 
        ob_start(); 
        include($path); 
        return ob_get_clean(); 
    }
}