Пример полноценного компилятора шаблонов на php. Функция-шаблонизатор получает содержимое шаблона, обрабатывает активные элементы и сохраняет скомпилированый файл. Нужен он, дабы отделить HTML от PHP. Код (PHP): <?php // Компилятор шаблонов VsCompiler // Vasilii B. Shpilchin (c) 2009 // Лицензия BSD /* Шаблоны имеют 5 типов активных элементов: | +Вывод переменной +-синтаксис: {=V:var} +-интерпретация: <?php echo $var?> | +Вывод константы +-синтаксис: {=D:constant} +-интерпретация: <?php echo constant?> | +Вставка статичного содержимого из файла +-синтаксис: {_FILE:file.ext} +-интерпретация: элемент заменяется на содержимое файла file.ext | +Подключаемый модуль +-синтаксис: {_MODULE:filename.ext} +-интерпретация: <php include('modules/filename.ext')?> | +Условие +-пример: {_IF:$var->_MODULE:filename.ext}%{_FILE:file.ext} +-в условии допустимо проверять переменные и константы, так же можно использовать знаки '>', '<', '='. +-в условии допустимо подключать модули, вставлять файлы, выводить переменные и константы +-знак процента (%) обзначает "else". Использование else не обязательно. */ function compileTemplate ($tplName,$compliteName,$replace=false) { //Проверка на наличие ошибок if(is_dir('templates')) { chdir('./templates'); } else { trigger_error('No such directory templates',E_USER_ERROR); return false; } if (is_file($tplName)) { $tpl=file_get_contents($tplName); } else { trigger_error('Template file '.$tplName.' not found',E_USER_NOTICE); return false; } //Если не файл уже существует, и нельзя заменить if (!$replace) { if (is_file('./complites/'.$compliteName)) { trigger_error('File '.$tplName.' too exist',E_USER_NOTICE); return false; } } //Чтобы не тратить ресурсы на замену несуществующих элементов //проверим их наличие $conditionsOn=strpos($tpl,'{_IF:')?true:false; $varsOn=strpos($tpl,'{=V:')?true:false; $definesOn=strpos($tpl,'{=D:')?true:false; $filesOn=strpos($tpl,'{_FILE:')?true:false; $modulesOn=strpos($tpl,'{_MODULE:')?true:false; //Обработка шаблона if($conditionsOn) { //Условия $tpl=preg_replace("%\{_IF:(.*)->(.*)\}%Ui","<?php if(\$1){?>{\$2}<?php }?>",$tpl); //IF $tpl=preg_replace("%\?>\%(.*)\}%Ui"," else{?>\$1}<?php }?>",$tpl); //ELSE } if($varsOn) { //Переменные $tpl=preg_replace("%\{=V:(.*)\}%Ui", "<?php echo $\$1;?>", $tpl); } if($definesOn) { //Константы $tpl=preg_replace("%\{=D:(.*)\}%Ui", "<?php echo \$1;?>", $tpl); } if($filesOn) { //Статичные файлы chdir('..'); preg_match_all("%\{_FILE:(.*)\}%Ui",$tpl,$files); foreach ($files[0] as $n=>$file) { if(!is_file($files[1][$n])) { trigger_error('Content file '.$files[1][$n].' not found',E_USER_NOTICE); return false; } $pos=strpos($tpl,$file); $len=strlen($file); $content=file_get_contents($files[1][$n]); $tpl=substr_replace($tpl,$content,$pos,$len); } chdir('./templates'); } if($modulesOn) { //Модули $tpl=preg_replace("%\{_MODULE:(.*)\}%Ui", "<?php include('modules/\$1');?>", $tpl); } //Сохранение шаблона chdir('./complites'); file_put_contents($compliteName,$tpl); return true; } ?> На входе что-то типа: Код (Text): <html> <head> <title>{=D:PAGE_TITLE} - {=D:SITE_NAME}</title> </head> <body> <div style="float: left;"> {_FILE:menu.htm} </div> <div style="float: right;"> {_MODULE:content.php} </div> <div> {_FILE:footer.htm} </div> </body> </html> И имеем на выходе: Код (PHP): <html> <head> <title><?php echo PAGE_TITLE;?> - <?php echo SITE_NAME;?></title> </head> <body> <div style="float: left;"> <ul> <li>Пункт меню <li>Пункт меню <li>Пункт меню <li>Пункт меню <li>Пункт меню </ul> </div> <div style="float: right;"> <?php is_file('modules/content.php')?include('modules/content.php'):print 'Модуль content.php РЅРµ найден';?> </div> <div> © SuperMegaSite </div> </body> </html> Этот файл уже подключаем в index.php. PS. Скрипт из собственного проекта ;-) IMHO, от шаблонизатора большего и не надо. Upd. поутру подумал, что неплохо бы сделать условия, чтобы не дергать для них нативный php. Вроде получилось юзабильно
Nemo Типа так: Код (PHP): <?php define('SITE_NAME','Новый сайт'); define('PAGE_TITLE','Главная страница'); if (is_file('./templates/complite/main.php')) { include('./templates/complite/main.php'); } else { include ('compiler.php'); compileTemplate('main.inc','main.php'); include('./templates/complite/main.php'); } ?> По задумке: вносишь изменения в шаблон, кликаешь в админ-панели "скомпилировать шаблоны" и смотришь результат. Плюс в том, что не надо собирать шаблон каждый раз. Например, чтобы не делать include содержимого, которое редко меняеся, удобен элемент _FILE. А еще ничто не мешает вставлять в шаблон нативный PHP-код.
Код (PHP): <?php // Простой наинвый шаблонизцатор Ж:-) class Spirit_View { private $templates = array(); private $vars = array(); public function __construct($path = false) { if ($path !== false) { $this->addTemplate($path); } } public function addTemplate($path) { array_push($this->templates, $path); } public function addVar($var, $val) { $this->vars[$var] = $val; } public function getVar($var) { if (!key_exists($var, $this->vars)) { return false; } if ($this->vars[$var] instanceof self) { return clone $this->vars[$var]; } return $this->vars[$var]; } public function __set($var, $val) { $this->addVar($var, $val); } public function __get($var) { return $this->getVar($var); } public function __toString() { ob_start(); extract($this->vars); foreach ($this->templates as $path) { if (Spirit_Site::templatesDir()) { $path = Spirit_Site::templatesDir().$path; } if (!file_exists($path)) { Spirit_Log::notice('View: шаблон '.$path.' не найден'); continue; } include $path; } return ob_get_clean(); } }
а как же повторное испрользование, этож сильная связность, не лучше отправлять пути в __construct? или сразу полный путь указывать да и класс с методом которые вызывать в случае ошибки или это в комплекте идет и не для потомков?)
Padaboo Предполагается, что нужные компоненты доступны. Spirit - типа фреймворк )) Здесь на мой взгляд есть только одно интересное решение передача объекта по ссылке в подобных шаблонизаторах часто игнорируется.
[vs] а, ну смотри сам, я свой Trashcan назову, если руки дойдут написать кстати, что там еше есть кроме MVC? как конфиг загружаешь? думаю, что нужно вообще в фремворке нормальном... mvc, класс для работы с базой, валидатор, генерация форм, мапперы, кешер, работа с деревьями, обработка картинок... во, крутой логотип уже нашел
Padaboo Конфиг в ini-файле. Есть там управление аккаунтами, валидатор форм, хандлер сессий, еще такая штука, которую я обозвал Scheme (viewtopic.php?t=26879 (только с тремя полями)), реестр, в котором создаются ссылки на все объекты, создаваемые фреймворком, и еще пара мелочей. Используются драйверы, к примеру к сессиям - класс, соответствующий интерфейсу, хранит данные в mysql в таблице типа Memory. Можно легко сделать драйвер для хранения в мемкэше. С Scheme та же история, тока там 2 драйвера - один для временного хранения (в памяти), другой - для постоянного. Имена классов-драйвера задаются также в ini. Класса для работы с БД как такового нет. Если в конфиге прописаны параметры, то при запросе у реестра соединения с mysql он вернет объект mysqli. MVC для пользователя не предлагается, зато предлагается в классе Site определить карту сайта (которая сохранится в Scheme). Руководствуясь картой роутер вычислит какой файл какой директории надо подключить. К примеру Код (PHP): Spirit_Site::map()->news->top = news/top.php если URL окажется длинее, например /news/top/2010/11, то невписывающаяся в карту часть url'а преобразуеся в массив $action, который будет доступен через Spirit_Site:age('action'); Всего 22 класса, 95кб кода. Пишется, что называется, для души, а не по жесткому плану ))
Padaboo Да, мне понравилась предлагаемая zend'ом структура файлов, но не понравилось что оно привязано к url'у. Вот и пришла идея...
Почему нельзя писать красивый код PHP: <?php define('SITE_NAME', 'Новый сайт'); define('PAGE_TITLE', 'Главная страница'); if (!is_file('./templates/complite/main.php')) { include ('compiler.php'); compileTemplate('main.inc','main.php'); } include('./templates/complite/main.php'); //И в конце ставить ?> не надо... [/php]