<?php
/**
* @author Morgan
* @copyright 2010
*/
#error_reporting(E_ALL);
if(!defined('SOMETEMPLATE_DEFAULT_DIR'))define('SOMETEMPLATE_DEFAULT_DIR', './');
if(!defined('SOMETEMPLATE_DEFAULT_COMPILED_DIR'))define('SOMETEMPLATE_DEFAULT_COMPILED_DIR', './compile/');
if(!defined('SOMETEMPLATE_DEFAULT_EXT'))define('SOMETEMPLATE_DEFAULT_EXT', 'tpl');
if(!defined('EOL'))define('EOL', "\r\n");
# Простенький класс для организации MVC-модели
class SomeTemplate
{
// режим отладки компилятора
public $debug_compiler = false;
// папка шаблонов
private $templates_dir = SOMETEMPLATE_DEFAULT_DIR;
// папка сгенерированных файлов
private $compiled_template_dir = SOMETEMPLATE_DEFAULT_COMPILED_DIR;
// расширение шаблонов
private $templates_ext = SOMETEMPLATE_DEFAULT_EXT;
// переменные, доступные в шаблонах
private $vars = array();
// некоторые настройки шаблона
private $cfgs = array();
/**
* __construct
*
* Установка параметров работы
*
* @param string $templates_dir папка с шаблонами
* @param string $compiled_templates_dir папка, куда сохраняются скомпилированые шаблоны
* @param string $templatess_ext расширение файлов шаблонов
**/
public function __construct($templates_dir = SOMETEMPLATE_DEFAULT_DIR, $compiled_templates_dir = SOMETEMPLATE_DEFAULT_COMPILED_DIR, $templates_ext = SOMETEMPLATE_DEFAULT_EXT)
{
if(is_dir($templates_dir))$this -> templates_dir = (substr($templates_dir, -1) != '/' ? $templates_dir.'/' : $templates_dir);
if(is_dir($compiled_templates_dir))$this -> compiled_templates_dir = (substr($compiled_templates_dir, -1) != '/' ? $compiled_templates_dir.'/' : $compiled_templates_dir);
$this -> templates_ext = $templates_ext;
}
/**
* __set
*
* Установка переменных, доступных в шаблоне
*
* @param string $var_name имя переменной
* @param string $var_value значение переменной
**/
public function __set($var_name, $var_value)
{
$this -> vars[$var_name] = $var_value;
}
/**
* __get
*
* Получения значения переменной
*
* @param string $var_name имя переменной
* @return mixed значение переменной
**/
public function __get($var_name)
{
if(isset($this -> vars[$var_name]))return $this -> vars[$var_name];
return false;
}
/**
* __set
*
* Установка переменных, доступных в шаблоне
*
* @param string $var_name имя переменной
* @param string $var_value значение переменной
**/
public function assign($var_name, $var_value = false)
{
if(is_array($var_name) && $var_value == false)
{
foreach($var_name as $name => $value)
{
$this -> assign($name, $value);
}
}
else
{
$this -> vars[$var_name] = $var_value;
}
return true;
}
/**
* debug
*
* Просмотр значений всех переменных
*
* @param bool $exit требуется ли завершение работы скрипта
**/
public function debug($exit = true)
{
echo '<b>templates_dir</b> = <b>'.$this -> templates_dir.'</b><br />';
echo '<b>templates_ext</b> = <b>'.$this -> templates_ext.'</b><br />';
print_r($this -> vars);
if($exit)exit;
return true;
}
/**
* display
*
* Обработка шаблона, и вывод на экран
*
* @param string $template_name имя файла-шаблона
* @return bool возникли ли ошибки
**/
public function display($template_name)
{
if(!file_exists($this -> templates_dir.$template_name.'.'.$this -> templates_ext))return false;
if($this -> debug_compiler || (!file_exists($this -> compiled_templates_dir.$template_name.'.'.$this -> templates_ext.'.php') || filemtime($this -> compiled_templates_dir.$template_name.'.'.$this -> templates_ext.'.php') < filemtime($this -> templates_dir.$template_name.'.'.$this -> templates_ext)))
{
if(!isset($this -> cfgs[$template_name]))$this -> cfgs[$template_name] = array();
// генерируем php файл из шаблона
$content = $this -> prepare_tpl($template_name);
// вставляем комментарий
$content = '<?php /* Сгенерировано '.date('G:i:s d.m.Y').' из файла '.$this -> compiled_templates_dir.$template_name.'.'.$this -> templates_ext.' */ ?>'.EOL.$content;
if(!file_put_contents($this -> compiled_templates_dir.$template_name.'.'.$this -> templates_ext.'.php', $content))return false;
}
#if(!isset($content))$content = file_get_contents($this -> compiled_templates_dir.$template_name.'.'.$this -> templates_ext.'.php');
require $this -> compiled_templates_dir.$template_name.'.'.$this -> templates_ext.'.php';
#if($this -> debug_compiler)unlink($this -> compiled_templates_dir.$template_name.'.'.$this -> templates_ext.'.php');
return true;
}
/**
* prepare_tpl
*
* Обработка шаблона
*
* @param string $filename имя файла-шаблона
* @return string PHP-код шаблона
**/
private function prepare_tpl($filename)
{
$code = file_get_contents($this -> templates_dir.$filename.'.'.$this -> templates_ext);
// вставка других файлов
$code = $this -> process_tpls($code);
// блоки
$code = $this -> process_blocks($code, $filename);
// циклы
$code = $this -> process_cycles($code, $filename);
// условия
$code = $this -> process_ifs($code);
// переменные
$code = $this -> process_vars($code);
#d($code);
return $code;
}
/**
* process_blocks
*
* обработка блоков в шаблоне типа
* {block index}
* ...
* {/block index}
* в switch ... case ... break ... endswtich;
*
*
* @param string $code код шаблона
* @param string $template имя файла-шаблона
* @return string обработанный код
**/
private function process_blocks($code, $template)
{
$old_code = $code;
$old_strlen = strlen($old_code);
// циклы
preg_match_all('~\{block ((\!)?([^!]*))\}(.*)\{\/block \\1\}~uUs', $code, $blocks, PREG_SET_ORDER ^ PREG_OFFSET_CAPTURE);
#d($blocks,0);
$use_blocks = false;
// код до/после/между блоками
$after_code = $before_code = $rest = null;
if($template == 'topic.page')
{
#echo '<div class="code">'.nl2br(set_eol(htmlspecialchars($code))).'</div>';
}
$count_blocks = count($blocks);
if($count_blocks > 0)
{
$use_blocks = true;
$lenght_shift = 0;
for($i = 0;$i < $count_blocks;$i ++)
{
if($i == 0 && $blocks[$i][0][1] > 0)
{
// записываем код "до"
$after_code = trim(substr($code, 0, $blocks[$i][0][1]));
#d(substr($code, $blocks[$i][0][1]));
$strlen = strlen($code);
$code = substr($code, $blocks[$i][0][1]);
$lenght_shift = $strlen - strlen($code);
#echo $lenght_shift;
#d(trim(substr($code, 0, $blocks[$i][0][1])),0);
#$result = null;
}
if($blocks[$i][2][0] == '!')
{
#d($blocks[$i],0);
$result_code = '<?php if($this -> vars[\'block\'] != \''.$blocks[$i][3][0].'\'): ?>'.EOL;
$result_code .= $blocks[$i][4][0];
$result_code .= EOL.'<?php endif; ?>'.EOL;
if($i == 0)$after_code .= $result_code;
else $before_code .= $result_code;
$result = null;
}
else
{
$result = '<?php case \''.$blocks[$i][1][0].'\': ?>'.EOL;
$result .= $blocks[$i][4][0];
$result .= EOL.'<?php break; ?>'.EOL;
#d($blocks[$i],0);
#d($result,0);
}
if(($i + 1) == $count_blocks && ($blocks[$i][0][1] + strlen($blocks[$i][0][0])) < $old_strlen)
{
#echo nl2br(set_eol(htmlspecialchars(substr($code, $blocks[$i][0][1] + strlen($blocks[$i][0][0]) + $lenght_shift))));
#echo $lenght_shift;
// записываем код "после"
#$before_code .= trim(substr($code, ($blocks[$i][0][1] + strlen($blocks[$i][0][0])) + $lenght_shift));
#$code = trim(substr($code, 0, ($blocks[$i][0][1] + strlen($blocks[$i][0][0])) + $lenght_shift));
#d(($blocks[$i][0][1] + strlen($blocks[$i][0][0])),0);
#d($blocks[$i]);
// получаем длину конца
$end_lenght = $old_strlen - ($blocks[$i][0][1] + strlen($blocks[$i][0][0]));
$before_code .= trim(substr($code, -$end_lenght));
$code = trim(substr($code, 0, -$end_lenght));
#echo $end_lenght;
#echo nl2br(set_eol(htmlspecialchars(substr($code,0, -88))));
#$result = null;
}
$code = preg_replace('~'.preg_quote($blocks[$i][0][0], '~').'~uUs', $result, $code, 1);
}
}
if($use_blocks)
{
$code = (!empty($after_code) ? $after_code.EOL : null).'<?php switch(isset($this -> vars[\'block\']) ? $this -> vars[\'block\'] : null): ?>'.EOL.$code.'<?php endswitch; ?>'.(!empty($before_code) ? EOL.$before_code : null);
/*$code = '<?php switch($this -> vars[\'block\']): ?>'.EOL.$code.'<?php endswitch; ?>';*/
}
#if($template == 'post.page')
# {
# echo '<br /><div class="code">'.nl2br(set_eol(htmlspecialchars($code))).'</div>';
# }
#d($after_code,0);
#d($before_code,0);
return $code;
}
/**
* process_ifs
*
* обработка условий в шаблоне
* {if $somevar}
* ...
* {/if $somevar}
* в if(...) ... endif;
*
*
* @param string $code код шаблона
* @return string обработанный код
**/
private function process_ifs($code)
{
// условии
preg_match_all('~\{if ((\!)?(\~)?\$((.+)(\.(.*))?)( (.*) (\$((.+)(\.(.*))?)|.+))?)\}(?:\r\n|\n)?(.*)(?:\r\n|\n)?\{\/if \\1\}~uUs', $code, $ifs, PREG_SET_ORDER);
$count_ifs = count($ifs);
if($count_ifs > 0)
{
for($i = 0;$i < $count_ifs;$i ++)
{
$var = $this -> get_var($ifs[$i][4], false);
$chapter1 = 'isset('.$var.') && ';
if(!empty($ifs[$i][8]))
{
// если это переменная
if($ifs[$i][10] == '$'.$ifs[$i][11])
{
$var2 = $this -> get_var($ifs[$i][11], false);
$chapter1 .= 'isset('.$var2.') && ';
$strlen = strlen($ifs[$i][10]);
$chapter2 = $var.substr($ifs[$i][8], 0, -$strlen).$var2;
}
else
{
$chapter2 = $var.$ifs[$i][8];
}
}
else
{
if($ifs[$i][2] == '!')
{
$chapter1 = '!isset('.$var.') || ';
if($ifs[$i][3] == '~')$chapter2 = '!(bool)'.$var;
else $chapter2 = $var.' !== true';
}
else
{
if($ifs[$i][3] == '~')$chapter2 = '(bool)'.$var;
else $chapter2 = $var.' === true';
}
#if($ifs[$i][3] == '~')$chapter2 = ($ifs[$i][2] == '!' ? '!' : null).'(bool)'.$var;
#else $chapter2 = $var.($ifs[$i][2] == '!' ? '!' : '=').'== true';
}
$result = '<?php if('.$chapter1.$chapter2.'): ?>';
$result .= $this -> process_ifs($ifs[$i][15]);
$result .= '<?php endif; ?>';
$code = preg_replace('~'.preg_quote($ifs[$i][0], '~').'~uUs', $result, $code, 1);
}
}
return $code;
}
/**
* process_cycles
*
* обработка циклов в шаблоне типа
* {foreach $a}
* ...
* {/foreach $a}
* в for(...) ... endfor;
*
* @param string $code код шаблона
* @return string обработанный код
**/
private function process_cycles($code, $template)
{
// циклы
preg_match_all('~\{foreach (\$(.*))\}(?:\r\n|\n)?(.*)(?:\r\n|\n)?\{\/foreach \\1\}~uUs', $code, $cycles, PREG_SET_ORDER);
#d($cycles);
$count_cycles = count($cycles);
if($count_cycles > 0)
{
for($i = 0;$i < $count_cycles;$i ++)
{
$result = null;
$var = $this -> get_var($cycles[$i][2], false);
#eval('$is_set = isset('.$var.');');
#eval('$array = (isset('..'))'.$var.';');
$last_value = (isset($this -> cfgs['cycles_values']['last_value'])) ? ($this -> cfgs['cycles_values']['last_value'] + 1) : 1;
$this -> cfgs['cycles_values']['last_value'] = $last_value;
$index = '_cycles_'.$last_value;
/*#$this -> vars[$index] = $array;
#d('{$'.preg_quote($cycles[$i][2], '~').'(.*)}');
#d($this -> get_recursive_values($array));
#d($index,0);
*/
$cycles[$i][3] = preg_replace('~\{\$'.preg_quote($cycles[$i][2], '~').'\.(.+)\}~uUs', '<?php if(isset($'.$index.'[\'$1\']))echo $'.$index.'[\'$1\']; ?>', $cycles[$i][3]);
$cycles[$i][3] = preg_replace('~if (\!)?(\~)?\$'.preg_quote($cycles[$i][2], '~').'\.(.*)~uUs', 'if $1$2$'.$cycles[$i][2].'.$'.$index.'_i.$3', $cycles[$i][3]);
// if ($'.$index.'[\'$2\']
#$count_values = count($array);
$result = '<?php if (!empty('.$var.')): ?>'.EOL;
$result .= ' <?php for($'.$index.'_c = count('.$var.'), $'.$index.'_i = 0; $'.$index.'_i < $'.$index.'_c; $'.$index.'_i ++): ?>'.EOL;
$result .= ' <?php $'.$index.' = '.$var.'[$'.$index.'_i]; ?>'.EOL;
$result .= $this -> process_cycles($cycles[$i][3], $template);
$result .= ' <?php endfor; ?>'.EOL;
$result .= '<?php endif; ?>';
$code = preg_replace('~'.preg_quote($cycles[$i][0], '~').'~uUs', $result, $code, 1);
}
}
return $code;
}
/**
* process_vars
*
* обработка переменных в шаблоне типа
* {$somevar}
* в echo $somevar;
*
* @param string $code код шаблона
* @return string обработанный код
**/
private function process_vars($code)
{
preg_match_all('~\{\$(.+)\}~uUs', $code, $vars, PREG_SET_ORDER);
$count_vars = count($vars);
if($count_vars > 0)
{
for($i = 0;$i < $count_vars;$i ++)
{
$var = $this -> get_var($vars[$i][1], false);
#$result = null;
#if(!empty($var))
# {
$result = '<?php if(isset('.$var.'))echo '.$var.'; ?>';
# }
$code = preg_replace('~'.preg_quote($vars[$i][0], '~').'~uUs', $result, $code, 1);
}
}
return $code;
}
/**
* process_tplss
*
* вставка содержимого друого файла через конструкцию
* {include template: some_template}
*
* @param string $code код шаблона
* @return string обработанный код
**/
private function process_tpls($code)
{
// вставка других шаблонов
preg_match_all('~\{include template\:(.*)\}~uUs', $code, $tpls, PREG_SET_ORDER);
$count_tpls = count($tpls);
if($count_tpls > 0)
{
for($i = 0;$i < $count_tpls;$i ++)
{
$result = null;
if(file_exists($this -> templates_dir.$tpls[$i][1].'.'.$this -> templates_ext))
{
$result = file_get_contents($this -> templates_dir.$tpls[$i][1].'.'.$this -> templates_ext);
}
$code = preg_replace('~'.preg_quote($tpls[$i][0]).'~u', $result, $code, 1);
}
}
return $code;
}
/**
* get_var
*
* форматирование переменной типа
* $one.two.three
* в $this -> vars['one']['two']['three']
*
* @param string $code код шаблона
* @return string обработанный код
**/
private function get_var($string_var, $isset = true, $type = false)
{
// разбиваем строку на секции
$sections = explode('.', $string_var);
$count_sections = count($sections);
if($count_sections > 0)
{
$var = null;
for($i = 0;$i < $count_sections;$i ++)
{
if(substr($sections[$i], 0, 1) == '$' || is_numeric($sections[$i]))$var .= '['.$sections[$i].']';
else $var .= '[\''.$sections[$i].'\']';
}
#eval('$var = \'[\\\'\'.implode(\'\\\'][\\\'\', $sections).\'\\\']\';');
if(!$isset)return '$this -> vars'.$var;
eval('$is_set = isset($this -> vars'.$var.');');
$is_type = true;
if($is_set && is_string($type))eval('$is_type = is_'.$type.'($this -> vars'.$var.');');
if($is_set && $is_type)
{
return '$this -> vars'.$var;
}
return 0;
}
return 0;
}
}
?>