За последние 24 часа нас посетили 18148 программистов и 1684 робота. Сейчас ищут 1119 программистов ...

Пример компилятора шаблонов

Тема в разделе "Решения, алгоритмы", создана пользователем [vs], 4 фев 2009.

  1. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    Пример полноценного компилятора шаблонов на php.
    Функция-шаблонизатор получает содержимое шаблона, обрабатывает активные элементы и сохраняет скомпилированый файл. Нужен он, дабы отделить HTML от PHP.

    Код (PHP):
    1. <?php
    2. // Компилятор шаблонов VsCompiler
    3. // Vasilii B. Shpilchin (c) 2009
    4. // Лицензия BSD
    5. /* Шаблоны имеют 5 типов активных элементов:
    6.     |
    7.     +Вывод переменной
    8.     +-синтаксис: {=V:var}
    9.     +-интерпретация: <?php echo $var?>
    10.     |
    11.     +Вывод константы
    12.     +-синтаксис: {=D:constant}
    13.     +-интерпретация: <?php echo constant?>
    14.     |
    15.     +Вставка статичного содержимого из файла
    16.     +-синтаксис: {_FILE:file.ext}
    17.     +-интерпретация: элемент заменяется на содержимое файла file.ext
    18.     |
    19.     +Подключаемый модуль
    20.     +-синтаксис: {_MODULE:filename.ext}
    21.     +-интерпретация: <php include('modules/filename.ext')?>
    22.     |
    23.     +Условие
    24.     +-пример: {_IF:$var->_MODULE:filename.ext}%{_FILE:file.ext}
    25.     +-в условии допустимо проверять переменные и константы, так же можно использовать знаки '>', '<', '='.
    26.     +-в условии допустимо подключать модули, вставлять файлы, выводить переменные и константы
    27.     +-знак процента (%) обзначает "else". Использование else не обязательно.
    28. */
    29. function compileTemplate ($tplName,$compliteName,$replace=false) {
    30.  
    31.     //Проверка на наличие ошибок
    32.     if(is_dir('templates')) {
    33.         chdir('./templates');
    34.     } else {
    35.         trigger_error('No such directory templates',E_USER_ERROR);
    36.         return false;
    37.     }
    38.     if (is_file($tplName)) {
    39.         $tpl=file_get_contents($tplName);
    40.     } else {
    41.         trigger_error('Template file '.$tplName.' not found',E_USER_NOTICE);
    42.         return false;
    43.     }
    44.     //Если не файл уже существует, и нельзя заменить
    45.     if (!$replace) {
    46.         if (is_file('./complites/'.$compliteName)) {
    47.             trigger_error('File '.$tplName.' too exist',E_USER_NOTICE);
    48.             return false;
    49.         }
    50.     }
    51.     
    52.     //Чтобы не тратить ресурсы на замену несуществующих элементов
    53.     //проверим их наличие
    54.     $conditionsOn=strpos($tpl,'{_IF:')?true:false;
    55.     $varsOn=strpos($tpl,'{=V:')?true:false;
    56.     $definesOn=strpos($tpl,'{=D:')?true:false;
    57.     $filesOn=strpos($tpl,'{_FILE:')?true:false;
    58.     $modulesOn=strpos($tpl,'{_MODULE:')?true:false;
    59.     
    60.     //Обработка шаблона
    61.     if($conditionsOn) { //Условия
    62.         $tpl=preg_replace("%\{_IF:(.*)->(.*)\}%Ui","<?php if(\$1){?>{\$2}<?php }?>",$tpl); //IF
    63.         $tpl=preg_replace("%\?>\%(.*)\}%Ui"," else{?>\$1}<?php }?>",$tpl); //ELSE
    64.     }
    65.     if($varsOn) { //Переменные
    66.         $tpl=preg_replace("%\{=V:(.*)\}%Ui", "<?php echo $\$1;?>", $tpl);
    67.     }
    68.     if($definesOn) { //Константы
    69.         $tpl=preg_replace("%\{=D:(.*)\}%Ui", "<?php echo \$1;?>", $tpl);
    70.     }
    71.     if($filesOn) { //Статичные файлы
    72.         chdir('..');
    73.         preg_match_all("%\{_FILE:(.*)\}%Ui",$tpl,$files);
    74.         foreach ($files[0] as $n=>$file) {
    75.             if(!is_file($files[1][$n])) {
    76.                 trigger_error('Content file '.$files[1][$n].' not found',E_USER_NOTICE);
    77.                 return false;
    78.             }
    79.             $pos=strpos($tpl,$file);
    80.             $len=strlen($file);
    81.             $content=file_get_contents($files[1][$n]);
    82.             $tpl=substr_replace($tpl,$content,$pos,$len);
    83.         }
    84.         chdir('./templates');
    85.     }
    86.     if($modulesOn) { //Модули
    87.         $tpl=preg_replace("%\{_MODULE:(.*)\}%Ui", "<?php include('modules/\$1');?>", $tpl);
    88.     }
    89.     
    90.     //Сохранение шаблона
    91.     chdir('./complites');
    92.     file_put_contents($compliteName,$tpl);
    93.     return true;
    94. }
    95. ?>
    На входе что-то типа:
    Код (Text):
    1. <html>
    2. <head>
    3. <title>{=D:PAGE_TITLE} - {=D:SITE_NAME}</title>
    4. </head>
    5. <body>
    6. <div style="float: left;">
    7. {_FILE:menu.htm}
    8. </div>
    9. <div style="float: right;">
    10. {_MODULE:content.php}
    11. </div>
    12. <div>
    13. {_FILE:footer.htm}
    14. </div>
    15. </body>
    16. </html>
    И имеем на выходе:
    Код (PHP):
    1. <html>
    2. <head>
    3. <title><?php echo PAGE_TITLE;?> - <?php echo SITE_NAME;?></title>
    4. </head>
    5. <body>
    6. <div style="float: left;">
    7. <ul>
    8. <li>Пункт меню
    9. <li>Пункт меню
    10. <li>Пункт меню
    11. <li>Пункт меню
    12. <li>Пункт меню
    13. </ul>
    14. </div>
    15. <div style="float: right;">
    16. <?php is_file('modules/content.php')?include('modules/content.php'):print 'Модуль content.php РЅРµ РЅР°Р№РґРµРЅ';?>
    17. </div>
    18. <div>
    19. © SuperMegaSite
    20. </div>
    21. </body>
    22. </html>
    Этот файл уже подключаем в index.php.

    PS. Скрипт из собственного проекта ;-) IMHO, от шаблонизатора большего и не надо.

    Upd. поутру подумал, что неплохо бы сделать условия, чтобы не дергать для них нативный php. Вроде получилось юзабильно :)
     
  2. EvelRus

    EvelRus Активный пользователь

    С нами с:
    16 ноя 2006
    Сообщения:
    2.168
    Симпатии:
    0
    Адрес:
    Москва
    Прости, красиво, но я дурак... а где задавать {=D:pAGE_TITLE} - в смысле чему оно равно?:))
     
  3. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    Nemo
    Типа так:
    Код (PHP):
    1. <?php
    2. define('SITE_NAME','Новый сайт');
    3. define('PAGE_TITLE','Главная страница');
    4. if (is_file('./templates/complite/main.php')) {
    5.         include('./templates/complite/main.php');
    6. } else {
    7.         include ('compiler.php');
    8.         compileTemplate('main.inc','main.php');
    9.         include('./templates/complite/main.php');
    10. }
    11. ?>
    По задумке: вносишь изменения в шаблон, кликаешь в админ-панели "скомпилировать шаблоны" и смотришь результат. Плюс в том, что не надо собирать шаблон каждый раз. Например, чтобы не делать include содержимого, которое редко меняеся, удобен элемент _FILE. А еще ничто не мешает вставлять в шаблон нативный PHP-код.
     
  4. phpdude

    phpdude Активный пользователь

    С нами с:
    9 июл 2010
    Сообщения:
    697
    Симпатии:
    0
    смайлоблонизатор :D
     
  5. Gromo

    Gromo Активный пользователь

    С нами с:
    24 май 2010
    Сообщения:
    2.786
    Симпатии:
    2
    Адрес:
    Ташкент
    цикла нет =/
     
  6. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    да фтопку этот шаблонизатор =D
     
  7. Gromo

    Gromo Активный пользователь

    С нами с:
    24 май 2010
    Сообщения:
    2.786
    Симпатии:
    2
    Адрес:
    Ташкент
    а чем пользуешься?
     
  8. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    Код (PHP):
    1. <?php
    2. // Простой наинвый шаблонизцатор Ж:-)
    3. class Spirit_View
    4. {
    5.     private $templates = array();
    6.     private $vars = array();
    7.  
    8.     public function  __construct($path = false) {
    9.         if ($path !== false) {
    10.             $this->addTemplate($path);
    11.         }
    12.     }
    13.  
    14.     public function addTemplate($path) {
    15.         array_push($this->templates, $path);
    16.     }
    17.  
    18.     public function addVar($var, $val) {
    19.         $this->vars[$var] = $val;
    20.     }
    21.  
    22.     public function getVar($var) {
    23.         if (!key_exists($var, $this->vars)) {
    24.             return false;
    25.         }
    26.         if ($this->vars[$var] instanceof self) {
    27.             return clone $this->vars[$var];
    28.         }
    29.         return $this->vars[$var];
    30.     }
    31.  
    32.     public function __set($var, $val) {
    33.         $this->addVar($var, $val);
    34.     }
    35.  
    36.     public function __get($var) {
    37.         return $this->getVar($var);
    38.     }
    39.  
    40.     public function __toString() {
    41.         ob_start();
    42.         extract($this->vars);
    43.         foreach ($this->templates as $path) {
    44.             if (Spirit_Site::templatesDir()) {
    45.                 $path = Spirit_Site::templatesDir().$path;
    46.             }
    47.             if (!file_exists($path)) {
    48.                 Spirit_Log::notice('View: шаблон '.$path.' не найден');
    49.                 continue;
    50.             }
    51.             include $path;
    52.         }
    53.         return ob_get_clean();
    54.     }
    55. } 
     
  9. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    а как же повторное испрользование, этож сильная связность, не лучше отправлять пути в __construct? или сразу полный путь указывать
    да и класс с методом которые вызывать в случае ошибки
    или это в комплекте идет и не для потомков?)
     
  10. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    Padaboo
    Предполагается, что нужные компоненты доступны. Spirit - типа фреймворк )) Здесь на мой взгляд есть только одно интересное решение
    передача объекта по ссылке в подобных шаблонизаторах часто игнорируется.
     
  11. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    [vs]
    а, ну смотри сам, я свой Trashcan назову, если руки дойдут написать :D
    кстати, что там еше есть кроме MVC?
    как конфиг загружаешь?
    думаю, что нужно вообще в фремворке нормальном...
    mvc, класс для работы с базой, валидатор, генерация форм, мапперы, кешер, работа с деревьями, обработка картинок...
    во, крутой логотип уже нашел
    [​IMG]
     
  12. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    Padaboo
    Конфиг в ini-файле. Есть там управление аккаунтами, валидатор форм, хандлер сессий, еще такая штука, которую я обозвал Scheme (viewtopic.php?t=26879 (только с тремя полями)), реестр, в котором создаются ссылки на все объекты, создаваемые фреймворком, и еще пара мелочей.
    Используются драйверы, к примеру к сессиям - класс, соответствующий интерфейсу, хранит данные в mysql в таблице типа Memory. Можно легко сделать драйвер для хранения в мемкэше.
    С Scheme та же история, тока там 2 драйвера - один для временного хранения (в памяти), другой - для постоянного.
    Имена классов-драйвера задаются также в ini.
    Класса для работы с БД как такового нет. Если в конфиге прописаны параметры, то при запросе у реестра соединения с mysql он вернет объект mysqli.
    MVC для пользователя не предлагается, зато предлагается в классе Site определить карту сайта (которая сохранится в Scheme). Руководствуясь картой роутер вычислит какой файл какой директории надо подключить. К примеру
    Код (PHP):
    1. Spirit_Site::map()->news->top = news/top.php
    если URL окажется длинее, например /news/top/2010/11, то невписывающаяся в карту часть url'а преобразуеся в массив $action, который будет доступен через Spirit_Site::page('action');
    Всего 22 класса, 95кб кода. Пишется, что называется, для души, а не по жесткому плану ))
     
  13. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    [vs]
    c бд и реестром у меня так же, а идею с картой сайта сам придумал?
     
  14. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    Padaboo
    Да, мне понравилась предлагаемая zend'ом структура файлов, но не понравилось что оно привязано к url'у. Вот и пришла идея...
     
  15. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    отделение HTML от PHP в полной жопе, версталы только и делают, что верстают шаблоны...
     
  16. Dagdamor

    Dagdamor Активный пользователь

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    [vs]
    Spirit - название хорошее :)
     
  17. sDevAlex

    sDevAlex Активный пользователь

    С нами с:
    28 окт 2010
    Сообщения:
    53
    Симпатии:
    0
    Почему нельзя писать красивый код :D
    PHP:
    1. <?php
    2. define('SITE_NAME', 'Новый сайт');
    3. define('PAGE_TITLE', 'Главная страница');
    4.  
    5. if (!is_file('./templates/complite/main.php'))
    6. {
    7.      include ('compiler.php');
    8.      compileTemplate('main.inc','main.php');
    9. }
    10.  
    11. include('./templates/complite/main.php');
    12.  
    13. //И в конце ставить ?> не надо...
    [/php]