231 lines
7.3 KiB
Markdown
231 lines
7.3 KiB
Markdown
|
---
|
|||
|
title: "📃 Простой класс шаблонизатора на PHP"
|
|||
|
date: 2023-10-05T06:12:03+03:00
|
|||
|
draft: false
|
|||
|
tags: [php, tutorial]
|
|||
|
---
|
|||
|
|
|||
|
![](splash.png)
|
|||
|
|
|||
|
> Перевод: https://codeshack.io/lightweight-template-engine-php/
|
|||
|
|
|||
|
Класс шаблонизатора является лёгким, гибким, быстрым
|
|||
|
и безопасным. Он компилирует шаблоны в оптимизированный PHP код.
|
|||
|
|
|||
|
Ниже я предоставлю полный исходный код и примеры того,
|
|||
|
как можно использовать класс шаблонизатора.
|
|||
|
|
|||
|
# Зачем мне нужен шаблонизатор?
|
|||
|
|
|||
|
The template engine keeps your design code away from your application code,
|
|||
|
this reason alone is good practice and follows many design patterns.
|
|||
|
Using a template engine is entirely up to you, if you prefer
|
|||
|
to keep your code clean and tidy then using a template engine is ideal.
|
|||
|
|
|||
|
If you're working with the MVC pattern then it is a good idea
|
|||
|
to use a template engine.
|
|||
|
|
|||
|
# Исходный код
|
|||
|
|
|||
|
Create a new file and name it `Template.php` and add:
|
|||
|
Создайте новый файл `Template.php` и вставьте в него следующий код:
|
|||
|
|
|||
|
```php
|
|||
|
<?php
|
|||
|
class Template {
|
|||
|
|
|||
|
static $blocks = array();
|
|||
|
static $cache_path = 'cache/';
|
|||
|
static $cache_enabled = FALSE;
|
|||
|
|
|||
|
static function view($file, $data = array()) {
|
|||
|
$cached_file = self::cache($file);
|
|||
|
extract($data, EXTR_SKIP);
|
|||
|
require $cached_file;
|
|||
|
}
|
|||
|
|
|||
|
static function cache($file) {
|
|||
|
if (!file_exists(self::$cache_path)) {
|
|||
|
mkdir(self::$cache_path, 0744);
|
|||
|
}
|
|||
|
$cached_file = self::$cache_path . str_replace(array('/', '.html'), array('_', ''), $file . '.php');
|
|||
|
if (!self::$cache_enabled || !file_exists($cached_file) || filemtime($cached_file) < filemtime($file)) {
|
|||
|
$code = self::includeFiles($file);
|
|||
|
$code = self::compileCode($code);
|
|||
|
file_put_contents($cached_file, '<?php class_exists(\'' . __CLASS__ . '\') or exit; ?>' . PHP_EOL . $code);
|
|||
|
}
|
|||
|
return $cached_file;
|
|||
|
}
|
|||
|
|
|||
|
static function clearCache() {
|
|||
|
foreach(glob(self::$cache_path . '*') as $file) {
|
|||
|
unlink($file);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static function compileCode($code) {
|
|||
|
$code = self::compileBlock($code);
|
|||
|
$code = self::compileYield($code);
|
|||
|
$code = self::compileEscapedEchos($code);
|
|||
|
$code = self::compileEchos($code);
|
|||
|
$code = self::compilePHP($code);
|
|||
|
return $code;
|
|||
|
}
|
|||
|
|
|||
|
static function includeFiles($file) {
|
|||
|
$code = file_get_contents($file);
|
|||
|
preg_match_all('/{% ?(extends|include) ?\'?(.*?)\'? ?%}/i', $code, $matches, PREG_SET_ORDER);
|
|||
|
foreach ($matches as $value) {
|
|||
|
$code = str_replace($value[0], self::includeFiles($value[2]), $code);
|
|||
|
}
|
|||
|
$code = preg_replace('/{% ?(extends|include) ?\'?(.*?)\'? ?%}/i', '', $code);
|
|||
|
return $code;
|
|||
|
}
|
|||
|
|
|||
|
static function compilePHP($code) {
|
|||
|
return preg_replace('~\{%\s*(.+?)\s*\%}~is', '<?php $1 ?>', $code);
|
|||
|
}
|
|||
|
|
|||
|
static function compileEchos($code) {
|
|||
|
return preg_replace('~\{{\s*(.+?)\s*\}}~is', '<?php echo $1 ?>', $code);
|
|||
|
}
|
|||
|
|
|||
|
static function compileEscapedEchos($code) {
|
|||
|
return preg_replace('~\{{{\s*(.+?)\s*\}}}~is', '<?php echo htmlentities($1, ENT_QUOTES, \'UTF-8\') ?>', $code);
|
|||
|
}
|
|||
|
|
|||
|
static function compileBlock($code) {
|
|||
|
preg_match_all('/{% ?block ?(.*?) ?%}(.*?){% ?endblock ?%}/is', $code, $matches, PREG_SET_ORDER);
|
|||
|
foreach ($matches as $value) {
|
|||
|
if (!array_key_exists($value[1], self::$blocks)) self::$blocks[$value[1]] = '';
|
|||
|
if (strpos($value[2], '@parent') === false) {
|
|||
|
self::$blocks[$value[1]] = $value[2];
|
|||
|
} else {
|
|||
|
self::$blocks[$value[1]] = str_replace('@parent', self::$blocks[$value[1]], $value[2]);
|
|||
|
}
|
|||
|
$code = str_replace($value[0], '', $code);
|
|||
|
}
|
|||
|
return $code;
|
|||
|
}
|
|||
|
|
|||
|
static function compileYield($code) {
|
|||
|
foreach(self::$blocks as $block => $value) {
|
|||
|
$code = preg_replace('/{% ?yield ?' . $block . ' ?%}/', $value, $code);
|
|||
|
}
|
|||
|
$code = preg_replace('/{% ?yield ?(.*?) ?%}/i', '', $code);
|
|||
|
return $code;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
?>
|
|||
|
```
|
|||
|
|
|||
|
Когда код проекта будет готов, не забудьте включить кеширование,
|
|||
|
обновив переменные `$cache_enabled` и `$cache_path variables`.
|
|||
|
|
|||
|
# Как использовать шаблонизатор
|
|||
|
|
|||
|
Создайте новый файл `index.php` и вставьте в него следующий код:
|
|||
|
|
|||
|
```php
|
|||
|
<?php
|
|||
|
include 'Template.php';
|
|||
|
Template::view('index.html');
|
|||
|
?>
|
|||
|
```
|
|||
|
|
|||
|
Создайте новый HTML файл `layout.html` и вставьте в него следующий код:
|
|||
|
|
|||
|
```html
|
|||
|
<!DOCTYPE html>
|
|||
|
<html>
|
|||
|
<head>
|
|||
|
<title>{% yield title %}</title>
|
|||
|
<meta charset="utf-8">
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
{% yield content %}
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
```
|
|||
|
|
|||
|
Это макет, который мы будем использовать для данного примера.
|
|||
|
|
|||
|
Создайте новый HTML файл `index.html` и вставьте в него следующий код:
|
|||
|
|
|||
|
```html
|
|||
|
{% extends layout.html %}
|
|||
|
|
|||
|
{% block title %}Home Page{% endblock %}
|
|||
|
|
|||
|
{% block content %}
|
|||
|
<h1>Home</h1>
|
|||
|
<p>Welcome to the home page!</p>
|
|||
|
{% endblock %}
|
|||
|
```
|
|||
|
|
|||
|
Теперь откройте файл `index.php` и посмотрите на результат.
|
|||
|
|
|||
|
Но что, если мы хотим использовать переменные в наших файлах шаблонов?
|
|||
|
Просто измените код шаблона в `index.php` следующим образом:
|
|||
|
|
|||
|
```php
|
|||
|
Template::view('about.html', [
|
|||
|
'title' => 'Home Page',
|
|||
|
'colors' => ['red','blue','green']
|
|||
|
]);
|
|||
|
```
|
|||
|
|
|||
|
И используйте переменные таким образом:
|
|||
|
|
|||
|
```html
|
|||
|
{% extends layout.html %}
|
|||
|
|
|||
|
{% block title %}{{ $title }}{% endblock %}
|
|||
|
|
|||
|
{% block content %}
|
|||
|
<h1>Home</h1>
|
|||
|
<p>Welcome to the home page, list of colors:</p>
|
|||
|
<ul>
|
|||
|
{% foreach($colors as $color): %}
|
|||
|
<li>{{ $color }}</li>
|
|||
|
{% endforeach; %}
|
|||
|
</ul>
|
|||
|
{% endblock %}
|
|||
|
```
|
|||
|
|
|||
|
Чтобы обезопасить результат, вместо: `{{ $output }}`
|
|||
|
необходимо использовать `{{{ $output }}}`.
|
|||
|
|
|||
|
Для таких значений, будет применяться функция
|
|||
|
[`htmlspecialchars`](https://www.php.net/manual/en/function.htmlspecialchars.php).
|
|||
|
|
|||
|
Наследование блоков:
|
|||
|
|
|||
|
```html
|
|||
|
{% block content %}
|
|||
|
@parent
|
|||
|
<p>Extends content block!</p>
|
|||
|
{% endblock %}
|
|||
|
```
|
|||
|
|
|||
|
Импортировать дополнительные файлы шаблонов:
|
|||
|
|
|||
|
```text
|
|||
|
{% include forms.html %}
|
|||
|
```
|
|||
|
|
|||
|
Чтобы очистить кеш, достаточно удалить все файлы в катоалоге `$cache_path`
|
|||
|
или вызвать функцию `Template::clearCache();``
|
|||
|
|
|||
|
# Заключение
|
|||
|
|
|||
|
Шаблонизатор очень полезен в больших проектах.
|
|||
|
Он позволит отделить дизайн-код от кода приложения.
|
|||
|
|
|||
|
Вы можете свободно использовать этот класс шаблонов в своих проектах.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
**Автор:** David Adams | **MIT License**
|