<?php /* --------------------------- * Легкий шаблонизатор Miranda * Автор: Mowshon * Сайт автора: mowshon.ru * --------------------------- */ class Miranda { public $TemplateFiles = ''; public $CompiledFiles = ''; public $Extensions = array('php', 'tpl'); public $SpecialExtension = 'tpl'; public $separator = DIRECTORY_SEPARATOR; public $TemplateVariables = array(); public $Sections = array(); public function __construct() { // Полный путь до папки с файлами шаблона if(!$this->TemplateFiles) $this->TemplateFiles = dirname( dirname( __FILE__ ) ) . $this->separator . 'templates' . $this->separator; // Путь до папки с копилиными файлами if(!$this->CompiledFiles) $this->CompiledFiles = $this->TemplateFiles . 'compiled' . $this->separator; } public function __destruct() { $this->TemplateVariables = array(); $this->Sections = array(); } public function __set($name, $value) { $this->TemplateVariables[$name] = $value; } public function TemplatePath( $path ) { // Если вам нужно хранить данные шаблона по определенному пути, тогда // до вызова в скрипте метода make, выполните вызов метода TemplatePath. // указывая в нем полный путь к папке // $Miranda->TemplatePath('/var/www/templates/'); $this->TemplateFiles = $path; } public function make( $expresion, $TemplateVariables = array() ) { // Конвертируем искусственный путь до файла в полный путь $path = $this->ConvertPath( $expresion ); if(!$path) {return 'File does not exist';} // Если метод make был вызван с массивом содержащий названия // и значения будующих переменных которые будет видны только в файлах шаблона // добавляем их к ранее действующим $this->UnionVariables( $TemplateVariables ); // Проверяем если файл шаблона является особым шаблонным файлом // т.е. с расширением .tpl if(!$this->isSpecialExtension( $path )) { // Файл является PHP файлом, вызываем его как есть return $this->open( $path ); } else { // Файл является особым шаблонным, отправляем его искусственному компилятору $CompiledFilename = $this->Compile( $expresion ); // Метод Compile вернет файл из папки template / compiled с расширением PHP // Открываем его как обычного PHP файла return $this->open( $CompiledFilename ); } } public function attach( $path ) { // Подключения файлов внутри шаблона $this->make( $path, $this->TemplateVariables ); } public function inject($section, $value) { // Создание блоков(секции) вне шаблонного файла, т.е. до вызова make $this->Sections[$section] = $value; } public function section( $Content ) { // Открывает содержимое блока(секции) $RequireSections = $this->RequireSections( $Content ); if(count($RequireSections)) { return strtr($Content, $RequireSections); } else { return $Content; } } private function UnionVariables( $Variables ) { // Объединение переменных в глобальном массиве $this->TemplateVariables = array_merge($this->TemplateVariables, $Variables ); return True; } private function open( $path ) { // Открывает PHP файл содовая переменные область видимости которых // будет только в этом файле или в файлах которых он подключает if( count( $this->TemplateVariables ) ) { foreach( $this->TemplateVariables as $key => $val ) { ${$key} = $val; } } require_once( $path ); } private function ConvertPath( $path ) { // Конвертирует искусственную аббревиатуру файла в полный путь до файла шаблона // пример: templ.file => /path/to/templates/templ/file.php или .tpl зависит от приоритета // т.е. какое расширение первое в массиве $this->Extensions $InitialPath = explode('.', $path); foreach($this->Extensions as $extension) { $File = $this->TemplateFiles . implode($this->separator, $InitialPath) . '.' . $extension; if(file_exists($File)) { return $File; } } return False; } private function extension( $file ) { $File = explode('.', $file); return $File[ count($File) - 1 ]; } private function isSpecialExtension( $file ) { // Проверка если запрашиваемый файл из $file имеет особое разрешение (.tpl) return( $this->extension($file) == $this->SpecialExtension )? True : False; } /* * Искусственный компилятор TPL файлов в PHP */ private function Compile( $expresion ) { // Подготавливаем древо подключающихся файлов шаблона $ParserLayoutTree = $this->ParserLayoutTree( $expresion ); // Будущее (или уже существующее) название файла в папке templates / compiled $CompiledFilename = $this->CompiledFilename( $ParserLayoutTree ); // Если файлы участвующие в древе $ParserLayoutTree потерпели изменения // с момента последней компиляции, выполняем компиляцию повторно $WasEdited = $this->WasEdited($CompiledFilename, $ParserLayoutTree); if( file_exists( $CompiledFilename ) and $WasEdited ) { // Файл существует, изменения не присутствуют return $CompiledFilename; } else { // Объединяем подключающие друг друга файлы вместе с выполнением замен // содержимого в блоках(секциях) $SourceOfUnionLayout = $this->UnionTreeContent( $ParserLayoutTree ); // Добавляем дату последнего изменения исходного TPL файла // или суммарную дату всех файлов которые входят в древо подключения ($ParserLayoutTree) $Content = $this->addFileLastEdit( $ParserLayoutTree ); // Конвертируем TPL функции в PHP $Content .= $this->ConvertToCode( $SourceOfUnionLayout ); $SaveAsCompiledFile = $this->SaveAsCompiledFile($CompiledFilename, $Content); return $CompiledFilename; } } private function OpenFile( $filename ) { return file_get_contents($filename); } private function CompiledFilename( $LayoutTree ) { $LayoutTreeFilename = $this->LayoutTreeFilename( $LayoutTree ); return $this->CompiledFiles . $LayoutTreeFilename . '.php'; } private function ConvertToCode( $Content ) { // Конвертируем специальные искусственные функции шаблонизатора // в их PHP альтернативы $Content = $this->ConvertTags($Content); $Content = $this->ConvertConditions($Content); return $Content; } private function ConvertTags( $Content ) { // Конвертирование искусственных перемен шаблонизатора preg_match_all("#{{(.+?)}}#si", $Content, $matches); if(count($matches[0])) { foreach($matches[0] as $match) { preg_match_all("#{{(.+?)}}#si", $match, $matches); if(count($matches[1])) { $Valiable = trim($matches[1][0]); // Если в тело блоков {{}} использован знак присвоения // значения "=" значит не афишируем значение переменной if(preg_match("#=#", $Valiable)) { $Content = str_replace($match, "<?php {$Valiable}; ?>", $Content); } else { // Блок {{}} не содержит знака присвоения, афишируем ее содержимое $Content = str_replace($match, "<?php echo {$Valiable}; ?>", $Content); } } } } return $Content; } private function ConvertConditions( $Content ) { // Конвертируем функции шаблонизатора в альтернативные PHP $Content = preg_replace("#@\s?begin\s?elseif\s?\((.*?)\)#si", "<?php } elseif($1) { ?>", $Content); $Content = preg_replace("#@\s?begin\s?(.+?)\s?\((.*?)\)#si", "<?php $1($2) { ?>", $Content); $Content = preg_replace("#@\s?include\s?\((.*?)\)#si", '<?php $this->attach('."'$1'".'); ?>', $Content); $Content = str_replace("@else", "<?php } else { ?>", $Content); $Content = str_replace("@end", "<?php } ?>", $Content); return $Content; } private function SaveAsCompiledFile( $filename, $content ) { $Create = fopen($filename, 'w+'); return fwrite($Create, $content); } private function addFileLastEdit( $LayoutTree ) { $lastedit = $this->LastEditSum($LayoutTree); return "\n"; } private function WasEdited($CompiledFilePath, $LayoutTree) { if(!file_exists($CompiledFilePath)) {return False;} $CopiledFileLastedit = $this->SavedLastEditDateInCompiledFile($CompiledFilePath); $SourceFilesLastedit = $this->LastEditSum($LayoutTree); return($CopiledFileLastedit != $SourceFilesLastedit)? False : True; } private function LastEditSum($LayoutTree) { $lastedit = 0; foreach($LayoutTree as $layout) { $lastedit += filemtime( $this->ConvertPath( $layout ) ); } return $lastedit; } private function SavedLastEditDateInCompiledFile( $CompiledFilePath ) { $CompiledContent = $this->OpenFile($CompiledFilePath); preg_match("#lastedit\[([0-9]+)\]#", $CompiledContent, $match); return $match[1]; } private function ParserLayoutTree( $expresion ) { // Создание древа подключающихся шаблонных файлов $LayoutTree[] = $expresion; $tpl_file = $this->ConvertPath( $expresion ); while(True) { $OpenLayout = $this->OpenFile( $tpl_file ); // Если в содержимое файла, есть открытие блоки(секции) добавляем их // содержимое в глобальный массив хранения блоков(секции) $this->CreateSections( $OpenLayout ); // Проверяем если данный файл не является частью другого // т.е. если нет сверху вызов родительного шаблона @layout(main) $FindLayout = $this->FindLayout( $OpenLayout ); if($FindLayout) { $LayoutTree[] = trim($FindLayout); $tpl_file = $this->ConvertPath($FindLayout); } else { break; } } return $LayoutTree; } private function FindLayout( $Content ) { // Поиск в содержимое файла запрос на вывод данных в родительский файл preg_match("#@\s?layout\s?\((.+?)\)#", $Content, $matches); return @$matches[1]; } private function LayoutTreeFilename( $Tree ) { return implode('_', $Tree); } private function CreateSections( $Content ) { preg_match_all("#@\s?section\s?\((.+?)\)(.+?)@\s?section_end#siu", $Content, $matches, PREG_SET_ORDER); foreach($matches as $value) { $this->Sections[trim($value[1])] = $value[2]; } } private function UnionTreeContent( $LayoutTree=array() ) { $MainLayoutInTree = $LayoutTree[count($LayoutTree)-1]; $this->FillSectionsWithContent(); $MainLayout = $this->OpenFile( $this->ConvertPath( $MainLayoutInTree ) ); return $this->section( $MainLayout ); } private function FillSectionsWithContent() { if(count($this->Sections)) { foreach($this->Sections as $key=>$value) { $this->Sections[$key] = $this->section( $value ); } } } private function RequireSections($Content) { $SectionsToSwitch = array(); preg_match_all("#@\s?view_section\s?\((.+?)\)#siu", $Content, $matches, PREG_SET_ORDER); foreach($matches as $value) { $SectionsToSwitch[trim($value[0])] = @$this->Sections[trim($value[1])]; } return $SectionsToSwitch; } } ?>