<?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;
}
}
?>