PHP I Шаблонизатор своими руками.

5K
.
В последнее время, на форуме много разговоров о шаблонизторах и заметно что многие люди, до сих пор, не понимают смысл и принципы их работы.
Думаю лучшим способом разобраться - будит написать шаблонизатор самому, чем мы сейчас, собственно, и займёмся.
И так, по велосипедимс

Создадим в корне хоста файлы index.php и Tpl.php - в котором мы будим описывать класс-шаблонизатор, и папку /templates - в которую будим складывать шаблоны.
Теперь открываем Tpl.php и создадим класс:

class Tpl
{
    //...
}
.
L!MP
Что бы мы в будущем могли без лишних телодвиженний получать доступ к объекту класса-шаблонизатора реализуем в нём паттерн Singleton
(впринципе, это не обязательно и если кому не нравится, то можно легко переделать).

class Tpl
{
    protected static $instance;
    
    protected $vars   = array( );
    protected $config = array( );
    
    public static function instance( array $config = null )
    {
        return (is_null(static::$instance)
            ? static::$instance = new static($config)
            : static::$instance
        );
    }
    
    protected function __construct( array $config ) {
        $this->config = $config;
    }
    
    protected function __clone() {
        //...
    }
    
    protected function __wakeup() {
        //...
    }
}


И так, мы реализовали Singleton (защищенное статическое свойство Tpl::$instance хранит единый объект класса, доступ к которому осуществляется через статич. метод Tpl::instance(), прямая инициялизация класса (new Tpl) и его клонирование - запрещены.)
и два свойства класса: $vars - массив пременных шаблона и $config - массив с парамметрами конфигурации шаблонизатора.
.
Теперь откроем index.php и подключим шаблонизатор.

require './Tpl.php';

$tpl = Tpl::instance(array(
    // путь к папке с шаблонами
    'dir' => './templates',
    // расширение файлов шаблонов
    'ext' => 'php',
));


Настройки передаются массивом при первом вызове метода Tpl::instance() который уже инициализирует объект и сохраняет ссылку на него.
В дальнейшем, доступ к объкту можно получить через Tpl::instance() без передачи конфигов.

Едем дальше.
.
L!MP
Реализуем функционал для работы с переменными шаблонов для чего воспользуемся "магией".
Возвращаемся к Tpl.php и дополним класс:

public function __set( $key, $value ) {
        $this->vars[ $key ] = $value;
    }
    
    public function __get( $key ) {
        return (isset($this->vars[ $key ]) ? $this->vars[ $key ] : null);
    }
    
    public function __isset( $key ) {
        return isset($this->vars[ $key ]);
    }
    
    public function __unset( $key ) {
        unset($this->vars[ $key ]);
    }


Теперь мы можем манипулировать переменными шаблона как публичными свойствами класса.
Можем назанчать переменные:
$tpl->some_var = 'Some value';

... проверять
isset($tpl->some_var);

... получать
$tpl->some_var;

... и удалять
unset($tpl->some_var);


Едем дальше.
.
L!MP
Реализуем метод обработки шаблонов.

public function render( $template, array $vars = null, $key = null )
    {
        if (is_array($vars)) {
            $this->vars = $vars += $this->vars;
        }
        
        ob_start();
        
        if (is_file($this->config['dir'] . '/' . $template . '.' . $this->config['ext'])) {
            include $this->config['dir'] . '/' . $template . '.' . $this->config['ext'];
            
            if (is_null($key)) {
                return ob_get_clean();
            }
            
            $this->$key = ob_get_clean();
            
            return $this;
        }
        
        throw new Exception("Template file '{$template}' not found.");
    }


Что тут просиходит:
первым, обязательным аргументом, мы передаём имя шаблона,
второй, не обязательный аргумент - массив переменных шаблона которые мы можем передать напрямую,
и третий, не обязательный аргумент - имя переменной шаблона в которую будит "собран" результат обработки (удобство этой фичи будит продемонстрировано позже).
.
Теперь уже можно полноценно опробовать наш шаблонизатор.
Создадим в папке /templates файл welcome.php с таким сожержимым:

<center>Hello <?= $this->name ?>!</center>


... и перепишем index.php

require './Tpl.php';

$tpl = Tpl::instance(array(
    'dir' => './templates',
    'ext' => 'php',
));

$tpl->name = 'limp';
echo $tpl->render('welcome');


... запускаем скрипт что бы увидить результат.
Прикрепленные файлы:
.
Теперь усложним задачу.
Будим работать с двумя шаблонами используя один как макет и вкладывая в него второй.
Для этого создадим ещё один шаблон layout.php

<center><h1>Layout Template</h1></center>
<br>
<?= $this->content ?>


... и перепишем index.php

На этот раз будим передавать переменные напрямую в render(),
а так же воспользуемся третим аргументом что бы сразу передать результат обработки в переменную $this->content главного шаблона.

require './Tpl.php';

$tpl = Tpl::instance(array(
    'dir' => './templates', // путь к папке с шаблонами
    'ext' => 'php', // расширение файлов шаблонов
));

$tpl->render('welcome', array('name' => 'limp'), 'content');
echo $tpl->render('layout');


... запускаем скрипт что бы увидить результат.
Прикрепленные файлы:
.
L!MP
Теперь по пробуем сделать наш шаблонизатор ещё функциональнее и удобнее.
Во время разработки интерфейса сайта у нас, так или иначе, будут появлятся многократно используемые участки кода/разметки. Это может быть форматированием текста, выводом пагинации и т.д.
Часто, все эти моменты оформляются в виде отдельных вспомогательных функций или классов, мы же пойдём другим путём и заложим в наш шаблонизатор немного расширяемости с помощью макросов.

Возвращаемся к Tpl.php.
Сначала изменим свойство $config добавив в массив ключ "macros" в котором мы будим сохранять зарегистрированные макросы.

protected $config = array(
        'macros' => array( )
    );


И реализуем методы для регистрации и вызова макросов.

public function macros( $name, Closure $lambda ) {
        $this->config['macros'][ $name ] = $lambda;
    }
    
    public function __call( $name, $parameters )
    {
        if (isset($this->config['macros'][ $name ])) {
            return call_user_func_array($this->config['macros'][ $name ], $parameters);
        }
        
        throw new Exception("Method 'Tpl::{$name}' does not exist.");
    }


Откроем index.php и опробуем макросы в действии:

require './Tpl.php';

$tpl = Tpl::instance(array(
    'dir' => './templates',
    'ext' => 'php',
));


// регистрируем макрос "capitalize", который делает заглавной первую букву в переданной строке и красит её в красный цвет.
// действие, конечно, надуманное, макросы могут применяться и для более сложных задач.

$tpl->macros('capitalize', function($string) {
    return '<b style="color:red">'. ucwords($string) . '</b>';
});

$tpl->render('welcome', array('name' => 'limp'), 'content');
echo $tpl->render('layout');


... применим макрос в шаблоне welcome.php

<center>Hello <?= $this->capitalize($this->name) ?>!</center>


... запускаем скрипт что бы увидить результат.
Прикрепленные файлы:
.
На этом, собственно, всё
.
Mes que un club
Столько много всего.. Не впечатлило меня) Как-то простой php из джона ближе)
Всего: 125