Полезная ссылка для тех, кто хочет больше знать о шаблонизаторах: http://habrahabr.ru/tag/шаблонизатор/
А еще можно с кешированием PHP: <?php /* (c)2010 Vasilii B. Shpilchin Шаблонизатор */ class view { // Конфигурация кеша const CACHE_SWITCH = 0; const CACHE_DIR = './cache'; private $cfname = null; // Буфер переменных private $_vars = array(); // Шаблон (путь, файл) private $_path = null; // Сгенерированый контент protected $_content = null; // Установка шаблона public function __construct($_path) { $this -> _path = $_path; $this -> cfname = self::CACHE_DIR.'/'.md5($this -> _path); } public function cName($name) { $this -> cfname = self::CACHE_DIR.'/'.md5($name); } public function __set($var, $val) { $this -> _vars[$var] = $val; } public function __get($var) { if (isset($this -> $var)) { return $this -> $var; } return $this -> _vars[$var]; } public function flush() { if (self::CACHE_SWITCH == 1) { $cfname = $this -> cfname.'.cache'; $cached = null; $vardump = $this -> vars_dump(); if (file_exists($cfname)) { $cached = unserialize(file_get_contents($cfname)); } if ($vardump == $cached[0]) { $this -> _content = &$cached[1]; } else { $cache = array($this -> vars_dump(), $this -> getcc()); file_put_contents($cfname, serialize(&$cache)); } } echo $this; } private function getc() { if ($this -> _content === null) { extract($this -> _vars); ob_start(); include($this -> _path); $this -> _content = ob_get_clean(); } return $this -> _content; } private function getcc() { foreach ($this -> _vars as $var => $data) { if (is_object($data)) { $this -> _vars[$var] = $data -> getcc(); } } return $this -> getc(); } private function vars_dump() { $vdump = array(0 => $this -> _path, 1 => array()); foreach ($this -> _vars as $name => $val) { if (is_object($val)) { $vdump[1][$name] = $val -> vars_dump(); } else { $vdump[1][$name] = $val; } } return $vdump; } public function __toString() { if ($this -> _content !== null) { return $this -> _content; } else { return $this -> getc(); } } } по тестам - вдвое быстрее inlclde'ов кучки шаблонов, и немного медленнее, если обнаруживается неактуальность кеша (проверка происходит каждый раз). Кеширование работает при вызове метода flush. В логике (если тольк кеширование ни в коем случае не нужно) надо вызывать его. В шаблонах напротив для корректной работы надо делать echo. Если делать в логике flush и в каком-нибудь шаблоне, это приведет к дублированию данных данных шаблона в кеше, сгененированом по вызову контроллера.
PHP: <? public function __toString() { /* вот это, по-моему, лишнее if ($this -> _content !== null) { return $this -> _content; } else { */ return $this -> getc(); /*}*/ } ?> а можно примерчик с использованием метода flush() ?
Бенчмарк PHP: <?php include('view.inc.php'); $start = microtime(1); ob_start(); for ($i = 0; $i < 100; $i++) { $view = new view('./tpls/1.tpl'); $view -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 = 1; $view -> inc1 -> inc2 = 1; $view -> inc1 -> inc3 = 1; $view -> inc1 -> inc4 = 1; $view -> inc1 -> inc5 = 1; $view -> inc1 -> inc6 = 1; $view -> inc1 -> inc7 = 1; $view -> inc1 -> inc8 = 1; $view -> inc1 -> inc9 = 1; $view -> inc1 -> inc10 = 1; $view -> inc2 = 1; $view -> inc3 = 1; $view -> inc4 = 1; $view -> inc5 = 1; $view -> inc6 = 1; $view -> inc7 = 1; $view -> inc8 = 1; $view -> inc9 = 1; $view -> inc10 = 1; $view -> flush(); } ob_get_clean(); echo substr(microtime(1) - $start, 0, 6); 1.tpl - 47кб. текста + 10 переменных. Без кеширования: 0.6821 С кешированием: 0.3627 Вопреки предубеждениям, сериализация оказалась очень шустрой =) Работает просто идеально.
PHP: <?php include('view.inc.php'); $start = microtime(1); ob_start(); for ($i = 0; $i < 10; $i++) { $view = new view('./tpls/1.tpl'); $view -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = new view('./tpls/1.tpl'); $view -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 -> inc1 = 1; $view -> flush(); } ob_get_clean(); echo substr(microtime(1) - $start, 0, 6); Без кеша: 1.8635 С кешем (!!!!!!!!): 0.2879 Шаблон - 47кб текста + 1 переменная.
Кеширование говорите: PHP: <?php public function hashCode(){ $hash = 0; ksort($this->vars); foreach($this->vars as $key=>$var){ $hash = md5(md5($key).md5("".$var).$hash); } return $hash; } protected function getCashFileName(){ return basename($this->path, ".tpl")."_".$this->hashCode(); } /** * Выполнение шаблона * @return string Результат выполнения шаблона */ public function __toString(){ if ($this->needCash){ $cashFileName=$this->getCashFileName(); if (file_exists(CASHDIR.$cashFileName)){ return file_get_contents(CASHDIR.$cashFileName); } else{ $rez=$this->compile(); file_put_contents(CASHDIR.$cashFileName, $rez); return $rez; } } else{ return $this->compile(); } } public function compile(){ extract($this->vars); ob_start(); include($this->path); return ob_get_clean(); } [vs] Дай, пожалуйста, шаблон. Прогоню свой вариант...
Я это вроде и делаю... По моим тестам: Без шаблонизации оба варианта одинаковы. С шаблонизацией мой вариант немного проигрывает (В два раза он отставал, потому что я кеширую оба шаблона, а [vs] только внешний) На моем простом примере шаблонизация замедляет работу в 2 раза.
cms-lite Суть в другом, переменные идут в объекте в том порядке, в котором их добавляли, а для разных последовательностей и хеши разные. PHP: <?php class MyObject { public function __toString() { $hash = null; foreach(get_object_vars($this) as $key => $var){ $hash = md5($key.$var.$hash); } return $hash; } } $obj1 = new MyObject(); $obj1->a = 'a'; $obj1->b = 'b'; echo '$obj1 hash is: '.$obj1.'<br />'; $obj2 = new MyObject(); $obj2->b = 'b'; $obj2->a = 'a'; echo '$obj2 hash is: '.$obj2.'<br />'; Код (Text): $obj1 hash is: 5885eb82659a5df84d43c88b83305374 $obj2 hash is: b7e1a537418a25330c6b347bef330aa3 Кстати я выкинул два лишних вызова md5(), теперь оно ещё и шустрее пашет А ещё там был кусок: "".$var Народ, это не JS, где + и сложение, и конкатенация. Здесь это склеивание строк, поетому приобразование произойдёт в любом случае
Подправил hashCode: PHP: <?php public function hashCode(){ $hash = 0; ksort($this->vars); foreach($this->vars as $key=>$var){ if ($var instanceof Template){ $hash = md5($key.$var->hashCode().$hash); } else{ $hash = md5($key.$var.$hash); } } return $hash; } На простом примере отстаю. На вложенности 4-6 догоняю.
Да я это тоже увидел и сделал. =)) Этот md5 и так почти ничего не стоит... =)) Потому что он передавался в md5 и черт его знает как он его преобразует или вообще выдаст ошибку.
а вот при простом сравнении массивов неважно, в каком прядке идут элементы =) если я правильно понял, в твоем решении для каждого вложеного шаблона будет свой кэш-файл? Тогда это даст плюс в скорости, когда содержимое какого-то шаблона изменится (у меня то все в одном большом файле, он будет пересобираться заново), но должно быть медленее, когда обновление не нужно, т.к. тут дергается несколько файлов, а у меня один ))
будет примерно Не пересобируться только те шаблоны, которые не включают в себя изменившийся шаблон. Это не особо большой прирост в скорости, хотя все зависит от шаблона конечно. так и было. А с тестами у меня вообще какая-то лажа. Пока гонял кэширование было хуже. Сейчас запустил, стало лучше...
ты делаешь для каждого шаблона в отдельности, короче: вариант [vs]: - кэширование массива переменных - кэширование результата рендеринга - но делает он это сразу для всех вложенных шаблонов (один файл кэша) твой вариант: - сохранение хэша, вместо массива переменных - кэширование результата рендеринга - но делаешь ты это для каждого шаблона в отдельности (несколько файлов кэша, несколько вызовов hashCode()) нужна золотая середина: - сохранение хэша, вместо массива переменных - кэширование результата рендеринга - сделать это сразу для всех вложенных шаблонов