Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/topic.php on line 737
Форумы портала PHP.SU :: Шаблонизатор страниц сайта

 PHP.SU

Программирование на PHP, MySQL и другие веб-технологии
PHP.SU Портал     На главную страницу форума Главная     Помощь Помощь     Поиск Поиск     Поиск Яндекс Поиск Яндекс     Вакансии  Пользователи Пользователи


 Страниц (1): [1]   

> Описание: интересует мнение
Prizma
Отправлено: 02 Марта, 2016 - 16:38:27
Post Id



Посетитель


Покинул форум
Сообщений всего: 463
Дата рег-ции: Июнь 2012  
Откуда: Санкт-Петербург


Помог: 5 раз(а)




Не так давно меня попросили добавить возможность на сайте интернет магазина вставлять информацию об одном товаре в описание другого (описание хранится в бд в виде текста). Я не долго думая написал класс для замены в html описании вхождений типа {%commandName?param1=x&param2=y%} на то что я хочу.

Сегодня я пошел дальше и решил написать класс который бы позволял все это использовать где угодно (для генерации шаблона, описания и всего чего только душа желает), как в шаблоны в популярных движках.

Как итог я получил класс, от которого наследуется пользовательский класс и в нем пишется только реализация самих команд. Правда пришлось использовать позднее статическое связывание(http://php.net/manual/ru/languag...tic-bindings.php ) с которым я раньше не работал и не знаю, что от него можно ожидать.

Хотел спросить ваше мнение, кто что думает по поводу реализация, применения, подводных камней?

Пример использования:
main.html - файл шаблона:
CODE (html):
скопировать код в буфер обмена
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="keywords" content="{%keywords%}">
  6.     <meta name="description" content="{%description%}">
  7.     <title></title>
  8. </head>
  9. <body>
  10. {%content%}
  11. </body>
  12. </html>

Example.php - пользовательский класс написанный на основе моего:
PHP:
скопировать код в буфер обмена
  1. class Example extends TemplateLoader
  2. {
  3.     /* Эта функция получает имя команды и параметры из шаблона и должна производить пользовательскую замену и является обязательной */
  4.     static protected function executeCommand($commandName, $params = [])
  5.     {
  6.         switch($commandName) {
  7.             case 'keywords':
  8.                 return 'my key words';
  9.             case 'description':
  10.                 return 'my key description';
  11.             case 'content':
  12.                 return self::generateContent();
  13.             default:
  14.                 return self::executeCommandNotFound($commandName);
  15.         }
  16.     }
  17.  
  18.     static private function generateContent()
  19.     {
  20.         return '<h1>content</h1>';
  21.     }
  22.     /* Тут можно сделать кучу своих методов */
  23. }

index.php - запускаемый файл:
PHP:
скопировать код в буфер обмена
  1. require_once 'TemplateLoader.php';
  2. require_once 'Example.php';
  3.  
  4. $t = __DIR__ . 'main.html';
  5. echo Example::loadTemplate($t)

Результат выглядит так:
CODE (html):
скопировать код в буфер обмена
  1.  
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5.     <meta charset="UTF-8">
  6.     <meta name="keywords" content="my key words">
  7.     <meta name="description" content="my key description">
  8.     <title></title>
  9. </head>
  10. <body>
  11. <h1>content</h1>
  12. </body>
  13. </html>
  14.  

TemplateLoader.php - класс шаблонизатора:
PHP:
скопировать код в буфер обмена
  1. class TemplateLoader
  2. {
  3.     /**
  4.      * @param $template string (if load=true then use file_get_contents for $template)
  5.      * @param bool|true $load
  6.      * @return string (ready template)
  7.      */
  8.     static public function loadTemplate($template, $load = true)
  9.     {
  10.         if($load) {
  11.             $t = $template;
  12.             $template = file_get_contents($t);
  13.             if(!$template) {
  14.                 throw new \LogicException(sprintf('Template was not found (%)', $t));
  15.             }
  16.         }
  17.         return self::executeTemplate(self::prepareCommandsInTemplate($template), $template);
  18.     }
  19.  
  20.     static protected function executeTemplate($prepareCommands, $template)
  21.     {
  22.         $search = [];
  23.         $replace = [];
  24.         foreach($prepareCommands as $commandSearchName => $prepareCommand) {
  25.             list($commandName, $params) = $prepareCommand;
  26.             $search[] = $commandSearchName;
  27.             $replace[] = static::executeCommand($commandName, $params);
  28.         }
  29.         return str_replace($search, $replace, $template);
  30.     }
  31.  
  32.     static protected function executeCommand($commandName, $params = [])
  33.     {
  34.         return '{%commnd was not found%}';
  35.     }
  36.  
  37.     static private function prepareCommandsInTemplate($template)
  38.     {
  39.         $regulars = self::searchRegular($template);
  40.         $re = [];
  41.         foreach($regulars as $templ) {
  42.             if(isset($re[$templ])) {
  43.                 continue;
  44.             }
  45.             $t = trim($templ,'%{}');
  46.             $t = explode('?',$t);
  47.             if(count($t) > 2) {
  48.                 $re[$templ] = '{syntax error}';
  49.             }else{
  50.                 $continue = false;
  51.                 $commandName = urldecode($t[0]);
  52.                 $params = [];
  53.                 if(isset($t[1]) && !empty($t[1])) {
  54.                     $t = explode('&',$t[1]);
  55.                     foreach($t as $param) {
  56.                         $p = explode('=', $param);
  57.                         if(count($p) === 2) {
  58.                             $params[urldecode($p[0])] = urldecode($p[1]);
  59.                         }else{
  60.                             $re[$templ] = '{syntax error in param}';
  61.                             $continue = true;
  62.                             break;
  63.                         }
  64.                     }
  65.                     if($continue) {
  66.                         continue;
  67.                     }
  68.                 }
  69.                 $re[$templ] = [$commandName, $params];
  70.             }
  71.         }
  72.         return $re;
  73.     }
  74.  
  75.     static private function searchRegular($template)
  76.     {
  77.         if(preg_match_all('#{%[^%\}]*%}#ui', $template, $re)) {
  78.             return $re[0];
  79.         }else{
  80.             return [];
  81.         }
  82.     }
  83. }



PS: У меня на него большие планы (в плане использования) поэтому интересуюсь вашим мнением, надеюсь камнями не забросаете). Может еще кому будет полезен.

PS2: планирую на его основе переписать генерацию страниц на сайте.

(Отредактировано автором: 02 Марта, 2016 - 16:46:08)

 
My status
 Top
Мелкий Супермодератор
Отправлено: 02 Марта, 2016 - 17:20:22
Post Id



Активный участник


Покинул форум
Сообщений всего: 11926
Дата рег-ции: Июль 2009  
Откуда: Россия, Санкт-Петербург


Помог: 618 раз(а)




Prizma пишет:
описание хранится в бд в виде текста

Ок, оправдано. eval куда большее зло.

Prizma пишет:
переписать генерацию страниц на сайте.

Ни в коем случае. Это дико жрёт CPU.

В целом: не используйте static там где это не нужно. (сейчас прибежит LIME и скажет "никогда не используйте static")
Замените на фабрику (паттерн factory).
Раз ищите теги всё равно регуляркой - сразу регуляркой их и заменяйте: preg_replace_callback. Зачем их потом искать ещё раз?
Взяли за основу синтаксис query param - ну и парсите их нативными средствами: parse_str


-----
PostgreSQL DBA
 
 Top
esterio
Отправлено: 02 Марта, 2016 - 17:22:31
Post Id



Активный участник


Покинул форум
Сообщений всего: 5025
Дата рег-ции: Нояб. 2012  
Откуда: Украина, Львов


Помог: 127 раз(а)




тоже когда-то страдал той же фигней и понял что лучше использовать тот же твиг и будет мне счастье. Вот почему я бы Вам рекомендовал использовать готовые решения. Но если рука чешеться написать хоть и не такое функциональное но свое, то вперед.
 
 Top
Prizma
Отправлено: 02 Марта, 2016 - 18:47:49
Post Id



Посетитель


Покинул форум
Сообщений всего: 463
Дата рег-ции: Июнь 2012  
Откуда: Санкт-Петербург


Помог: 5 раз(а)




Мелкий пишет:
В целом: не используйте static там где это не нужно. (сейчас прибежит LIME и скажет "никогда не используйте static")

Я люблю статик за то, что код становится как мне кажется более лаконичным, без создания экземпляра класса там где не нужно хранить каких то итогов или использовать его промежуточные результаты многократно.
Мелкий пишет:
Раз ищите теги всё равно регуляркой - сразу регуляркой их и заменяйте: preg_replace_callback. Зачем их потом искать ещё раз?

Мелкий пишет:
Взяли за основу синтаксис query param - ну и парсите их нативными средствами: parse_str

согласен, исправлюсь
Мелкий пишет:
Ни в коем случае. Это дико жрёт CPU.

а вот тут не совсем понял, что именно будет давать нагрузку?
К примеру если контент динамический, есть несколько десятков типов страниц (страница товара, страница html, страница категорий, страница новостей и мало ли еще какие) для генерации я планировал использовать этот класс, сначала на уровне шаблона, а потом на уровне крупных элементов в теле страницы (сомневаюсь что их будет больше 1-5 на одной странице).

В любом случае можно задействовать кэширование, но это уже другая песня. Интересно где именно будет цп грузиться я тоже думал об этом, но недопер видимо)) регулярка шустрая тем более искать там немного потом всё по сути переходит на switch и уже по нужной ветке верстается в момент. Или не так?
(Добавление)
PS: есть еще вариант например в ленте новостей прогонять через шаблон каждую новость к примеру может тогда возрастет нагрузка? хотя не знаю
esterio пишет:
тоже когда-то страдал той же фигней и понял что лучше использовать тот же твиг и будет мне счастье. Вот почему я бы Вам рекомендовал использовать готовые решения. Но если рука чешеться написать хоть и не такое функциональное но свое, то вперед.
да функционал там широкий и часто не востребованный, я интересуюсь готовыми решениями, однако часто они дают непредсказуемую нагрузку (хотя и достаточно редко и даже когда дают бывает, что от кривых рук, а не из-за решения)
(Добавление)
Использовал советы и совсем лаконично получилось, правда от статиков не ушел) ну уж очень они мне нравятся
PHP:
скопировать код в буфер обмена
  1. class TemplateLoader
  2. {
  3.     /**
  4.      * @param $template string (if load=true then use file_get_contents for $template)
  5.      * @param bool|true $load
  6.      * @return string (ready template)
  7.      */
  8.     static $cashResults = [];
  9.  
  10.     static public function loadTemplate($template, $load = true)
  11.     {
  12.         if($load) {
  13.             $t = $template;
  14.             $template = file_get_contents($t);
  15.             if(!$template) {
  16.                 throw new \LogicException(sprintf('Template was not found (%)', $t));
  17.             }
  18.         }
  19.         $template = preg_replace_callback('#{%[^%\}]*%}#ui', 'self::re', $template);
  20.         return $template;
  21.     }
  22.  
  23.     static protected function executeCommand($commandName, $params = [])
  24.     {
  25.         return '{%commnd was not found%}';
  26.     }
  27.  
  28.     static private function re($fullCommand)
  29.     {
  30.         $fullCommand = trim($fullCommand[0], '{}%');
  31.         if(isset(self::$cashResults[$fullCommand])) {
  32.             return self::$cashResults[$fullCommand];
  33.         }else{
  34.             $t = explode('?',$fullCommand);
  35.             if(count($t) > 2) {
  36.                 return '{syntax error}';
  37.             }else{
  38.                 $commandName = urldecode($t[0]);
  39.                 parse_str($t[1], $params);
  40.                 return static::executeCommand($commandName, $params);
  41.             }
  42.         }
  43.     }
  44. }

(Отредактировано автором: 02 Марта, 2016 - 18:48:20)

 
My status
 Top
Мелкий Супермодератор
Отправлено: 02 Марта, 2016 - 20:04:20
Post Id



Активный участник


Покинул форум
Сообщений всего: 11926
Дата рег-ции: Июль 2009  
Откуда: Россия, Санкт-Петербург


Помог: 618 раз(а)




Просто сделайте рядом нормальный тупой

CODE (htmlphp):
скопировать код в буфер обмена
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="keywords" content="<?=$keywords?>">
  6.     <meta name="description" content="<?=$description?>">
  7.     <title></title>
  8. </head>
  9. <body>
  10. <?=$content?>
  11. </body>
  12. </html>


Запустите профилировщик и сравните.
Потом учтите в своём велосипеде циклы и условия и сравните ещё раз.


-----
PostgreSQL DBA
 
 Top
Prizma
Отправлено: 02 Марта, 2016 - 20:06:41
Post Id



Посетитель


Покинул форум
Сообщений всего: 463
Дата рег-ции: Июнь 2012  
Откуда: Санкт-Петербург


Помог: 5 раз(а)




Мелкий пишет:
Запустите профилировщик и сравните.
Потом учтите в своём велосипеде циклы и условия и сравните ещё раз.

У меня была мысль так сделать) я Вас услышал, спасибо за советы
 
My status
 Top
Страниц (1): [1]
Сейчас эту тему просматривают: 0 (гостей: 0, зарегистрированных: 0)
« Объектно-ориентированное программирование »


Все гости форума могут просматривать этот раздел.
Только зарегистрированные пользователи могут создавать новые темы в этом разделе.
Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.
 



Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS  RSS

 
Powered by ExBB FM 1.0 RC1. InvisionExBB