Я довольно давно уже использую нативные шаблоны, но, почему-то, у многих людей нативные шаблоны ассоциируются с конструкциями типа:
То есть, переменную определили и приинклюдили html-файл. Я считаю, что это в корне неверный подход. Почему?
Во-первых, все переменные, переданные в шаблон, должны храниться в одном месте (свойстве класса шаблонизатора).
Во-вторых, в шаблонизаторе не должно быть доступа к переменным, которые в него не переданы, и к функциям, которые в нем не определены.
В-третьих, должен быть определен набор функций, необходимых для работы.
Таким образом, я пришел к выводу, что шаблонизатор нужен, но он не должен быть навороченным тормозом типа Smarty.
Идеология блочных шаблонизаторов (XTemplate, например) мне не импонирует потому, что в них нет ветвлений как таковых, есть только циклы.
Потому я написал свой.
Начнем с того, что нам нужно разобраться с обработкой ошибок. Я использую для этой цели исключения, потому определяем класс исключений:
Тут нам больше ничего не нужно. Переходим к самому шаблонизатору (о том, что делают методы, кратко написано в комментариях, более подробно опишу ниже):
В конструкторе мы можем указать путь к директории шаблонов (по умолчанию temlates/).
С помощью метода setParam мы можем установить параметры шаблонизатора. Их всего три (мне этого достаточно, при необходимости можно добавлять параметры). Первый параметр — xss_protection — как понятно из названия, нужен для защиты от уязвимости xss. Если значение параметра установлено как true, все переменные, которые мы используем в шаблоне, перед отдачей автоматически обрабатываются функцией htmlspecialchars (в том числе элементы массивов). Второй параметр — exit_after_display — нужен для того, чтобы, при потребности, мы могли остановить выполнение сценария после отображения шаблона. Третий параметр — endofline_to_br — обрабатывает все переменные перед отдачей (в том числе элементы массивов) функцией nl2br.
Методом setIncludeFile мы можем установить подключаемый шаблон. Очень часто используется общий шаблон index.tpl.php и в него, в зависимости от условий, подключают изменямую часть. Вот для автоматизации данного процесса и нужен этот метод. Если подключаемый файл не существует, выбрасыватся исключение.
Метод assign служит для передачи переменных в шаблон.
Метод display отображает шаблон. Если файл шаблона не существует, выбрасывается исключение. Если параметр exit_after_display установлен как true, этот метод также завершает работу сценария (практически всегда отображение шаблона является последним действием).
«Магический» метод __get возвращает значение переданной в шаблон переменной. Если переменная не определена, возвращает NULL. В зависимости от параметров, переменные перед отдачей могут обрабатываться.
Метод includeFile инклюдит файл, назначенный методом setIncludeFile и выбрасывает исключение, если этот файл не найден.
Метод morph, не совсем «шаблонизаторный», служит для формирования правильных окончание слов, относящихся к числительным. То есть, 1 комментарий, 2 комментария, 5 комментариев. В метод нужно передать само число, три разных варианта и, опционально, разделитель слов (по умолчанию неразрывный пробел).
Метод xssProtection обрабатывает данные функцией htmlspecialchars. Если на входе массив, то он рекурсивно перебирается и обрабатываются все его элементы.
Метод endoflineToBr обрабатывает данные функцией nl2br. Если на входе массив, то он, как и в предыдущем методе, рекурсивно перебирается и обрабатываются все его элементы.
Как это выглядит на практике? Предположим, нам нужно распечатать статью и комментарии к ней. Данные по статье в массиве $article, комменты — в $comments.
Контроллер:
Шаблон index.tpl.php:
Шаблон article.tpl.php:
Скачать класс.
Использование класса в личных нуждах разрешено без ограничений. При перепечатке статьи или исходного кода, в том числе, частично, ссылка на меня (на мой сайт) обязательна.
- $title = 'My title';
- include('templates/index.html');
* This source code was highlighted with Source Code Highlighter.
- <html><head><title><?php echo $title ?></title></head>
- <!-- ... -->
* This source code was highlighted with Source Code Highlighter.
То есть, переменную определили и приинклюдили html-файл. Я считаю, что это в корне неверный подход. Почему?
Во-первых, все переменные, переданные в шаблон, должны храниться в одном месте (свойстве класса шаблонизатора).
Во-вторых, в шаблонизаторе не должно быть доступа к переменным, которые в него не переданы, и к функциям, которые в нем не определены.
В-третьих, должен быть определен набор функций, необходимых для работы.
Таким образом, я пришел к выводу, что шаблонизатор нужен, но он не должен быть навороченным тормозом типа Smarty.
Идеология блочных шаблонизаторов (XTemplate, например) мне не импонирует потому, что в них нет ветвлений как таковых, есть только циклы.
Потому я написал свой.
Начнем с того, что нам нужно разобраться с обработкой ошибок. Я использую для этой цели исключения, потому определяем класс исключений:
- class STempException extends Exception {}
* This source code was highlighted with Source Code Highlighter.
Тут нам больше ничего не нужно. Переходим к самому шаблонизатору (о том, что делают методы, кратко написано в комментариях, более подробно опишу ниже):
- class STemp
- {
- /**
- * The name of the directory where templates are located.
- *
- * @var string.
- * @access private.
- */
- private $path;
-
- /**
- * Name of the template.
- *
- * @var string.
- * @access private.
- */
- private $template;
-
- /**
- * Where assigned template vars are kept.
- *
- * @var array.
- * @access private.
- */
- private $variables = array();
-
- /**
- * Parameters of the template engine.
- *
- * @var array.
- * @access private
- */
- private $params = array(
- 'xss_protection' => true,
- 'exit_after_display' => true,
- 'endofline_to_br' => false
- );
-
- /**
- * File that include in template.
- *
- * @var string.
- * @access private.
- */
- private $include_file;
-
- /**
- * The class constructor. Set name of the directory where templates are located.
- *
- * @param string $path name of the directory where templates are located, default 'templates/'.
- * @access public.
- */
- public function __construct($path = 'templates/')
- {
- $this->path = $path;
- }
-
- /**
- * Set parameters of template engine.
- *
- * @param string $param name of the parameter.
- * @param bool $value value of the parameter.
- * @return bool TRUE if parameter set, FALSE if didn't set.
- * @access public.
- */
- public function setParam($param, $value)
- {
- if (isset($this->params[$param])) {
- $this->params[$param] = $value;
- return true;
- }
-
- return false;
- }
-
- /**
- *
- * @param string $include_file path to include file.
- * @access public.
- */
- public function setIncludeFile($include_file)
- {
- $this->include_file = $this->path.$include_file;
-
- if (!file_exists($this->path.$include_file))
- throw new STempException('Include file '.$this->include_file.' not exitst');
- }
-
- /**
- * Assigns values to template variables.
- *
- * @param string $name the template variable name.
- * @param mixed $value the value to assign.
- * @access public.
- */
- public function assign($name, $value)
- {
- $this->variables[$name] = $value;
- }
-
- /**
- * Executes and displays the template results.
- *
- * @param string $template the template name.
- * @access public.
- */
- public function display($template)
- {
- $this->template = $this->path.$template;
-
- if (!file_exists($this->template))
- throw new STempException('Template file '.$template.' not exitst');
-
- require_once($this->template);
-
- if ($this->params['exit_after_display'])
- exit;
- }
-
- /**
- * Get value of template variable.
- *
- * @param string $name the template variable name.
- * @return mixed value of template variable with this name. FALSE if variable not set.
- * @access private.
- */
- private function __get($name)
- {
- if (isset($this->variables[$name])) {
- $variable = $this->variables[$name];
-
- if ($this->params['xss_protection'])
- $variable = $this->xssProtection($variable);
-
- if ($this->params['endofline_to_br'])
- $variable = $this->endoflineToBr($variable);
-
- return $variable;
- }
-
- return NULL;
- }
-
- /**
- * Include file
- *
- * @access private
- */
- private function includeFile()
- {
- if (!file_exists($this->include_file))
- throw new STempException('Include file '.$this->include_file.' not found');
-
- require_once($this->include_file);
- }
-
- /**
- * For the formation of endings of words.
- *
- * @param int $value number.
- * @param string $word0 word in the singular.
- * @param string $word1 word in the plural (2, 3).
- * @param string $word2 word in the plural.
- * @param string $separator separator, default ' '.
- * @return string formed words
- * @access private.
- */
- private function morph($value, $word0, $word1, $word2, $separator = ' ')
- {
- if (preg_match('/1\d$/', $value))
- return $value.$separator.$word2;
- elseif (preg_match('/1$/', $value))
- return $value.$separator.$word0;
- elseif (preg_match('/(2|3|4)$/', $value))
- return $value.$separator.$word1;
- else
- return $value.$separator.$word2;
- }
-
- /**
- * For protection from XSS.
- *
- * @param mixed $variable data for protection.
- * @return mixed protected data.
- * @access private.
- */
- private function xssProtection($variable)
- {
- if (is_array($variable)) {
- $protected = array();
- foreach ($variable as $key=>$value)
- $protected[$key] = $this->xssProtection($value);
- return $protected;
- }
-
- return htmlspecialchars($variable);
- }
-
- /**
- * Inserts HTML line breaks before all newlines in a string.
- *
- * @param mixed $variable data for protection.
- * @return mixed data where string with <br /> inserted before all newlines.
- * @access private.
- */
- private function endoflineToBr($variable)
- {
- if (is_array($variable)) {
- $protected = array();
- foreach ($variable as $key=>$value)
- $protected[$key] = $this->endoflineToBr($value);
- return $protected;
- }
-
- return nl2br($variable);
- }
- }
* This source code was highlighted with Source Code Highlighter.
В конструкторе мы можем указать путь к директории шаблонов (по умолчанию temlates/).
С помощью метода setParam мы можем установить параметры шаблонизатора. Их всего три (мне этого достаточно, при необходимости можно добавлять параметры). Первый параметр — xss_protection — как понятно из названия, нужен для защиты от уязвимости xss. Если значение параметра установлено как true, все переменные, которые мы используем в шаблоне, перед отдачей автоматически обрабатываются функцией htmlspecialchars (в том числе элементы массивов). Второй параметр — exit_after_display — нужен для того, чтобы, при потребности, мы могли остановить выполнение сценария после отображения шаблона. Третий параметр — endofline_to_br — обрабатывает все переменные перед отдачей (в том числе элементы массивов) функцией nl2br.
Методом setIncludeFile мы можем установить подключаемый шаблон. Очень часто используется общий шаблон index.tpl.php и в него, в зависимости от условий, подключают изменямую часть. Вот для автоматизации данного процесса и нужен этот метод. Если подключаемый файл не существует, выбрасыватся исключение.
Метод assign служит для передачи переменных в шаблон.
Метод display отображает шаблон. Если файл шаблона не существует, выбрасывается исключение. Если параметр exit_after_display установлен как true, этот метод также завершает работу сценария (практически всегда отображение шаблона является последним действием).
«Магический» метод __get возвращает значение переданной в шаблон переменной. Если переменная не определена, возвращает NULL. В зависимости от параметров, переменные перед отдачей могут обрабатываться.
Метод includeFile инклюдит файл, назначенный методом setIncludeFile и выбрасывает исключение, если этот файл не найден.
Метод morph, не совсем «шаблонизаторный», служит для формирования правильных окончание слов, относящихся к числительным. То есть, 1 комментарий, 2 комментария, 5 комментариев. В метод нужно передать само число, три разных варианта и, опционально, разделитель слов (по умолчанию неразрывный пробел).
Метод xssProtection обрабатывает данные функцией htmlspecialchars. Если на входе массив, то он рекурсивно перебирается и обрабатываются все его элементы.
Метод endoflineToBr обрабатывает данные функцией nl2br. Если на входе массив, то он, как и в предыдущем методе, рекурсивно перебирается и обрабатываются все его элементы.
Как это выглядит на практике? Предположим, нам нужно распечатать статью и комментарии к ней. Данные по статье в массиве $article, комменты — в $comments.
Контроллер:
- $stemp = new STemp();
-
- $stemp->assign("title", $article['title']);
-
- $stemp->assign("article", $article);
- $stemp->assign("comments", $comments);
-
- try {
- $stemp->setIncludeFile("article.tpl.php");
- $stemp->display("index.tpl.php");
- } catch (STempException $e) {
- die('STemp error: '.$e->getMessage());
- }
* This source code was highlighted with Source Code Highlighter.
Шаблон index.tpl.php:
- <html>
- <head>
- <title><?php echo $this->title ?></title>
- </head>
- <body>
- <?php $this->includeFile() ?>
- </body>
- </html>
* This source code was highlighted with Source Code Highlighter.
Шаблон article.tpl.php:
- <h1><?php echo $this->article['title'] ?></h1>
- <?php $this->setParam('xss_protection', false); $this->setParam('endofline_to_br', true) ?>
- <div class="content">
- <?php echo $this->article['content'] ?>
- </div>
- <p><?php echo $this->morph(count($this->comments), 'комментарий', 'комментария', 'комментариев') ?>:</p>
- <?php $this->setParam('xss_protecttion', true) ?>
- <?php foreach ($this->comments as $key=>$value) { ?>
- <p class="user"><?php echo $value['username'] ?>:</p>
- <p class="comment"><?php echo $value['text'] ?></p>
- <?php } ?>
* This source code was highlighted with Source Code Highlighter.
Скачать класс.
Использование класса в личных нуждах разрешено без ограничений. При перепечатке статьи или исходного кода, в том числе, частично, ссылка на меня (на мой сайт) обязательна.