В первой части мы по всем правилам ООП создали шаблонизатор, теперь наростим его мощь, будем делать это двумя способами:
- Расширая класс Amdy_Templater
- С помощью классов-помощников.
Первый способ эллементарный:
class Amdy_Templater extends Amdy_Template_Abstract { protected function escape($var, $mode = ENT_COMPAT) { return htmlspecialchars($var, $mode); } }
а в шаблоне
1. teplater !<?=$this->escape($this->test, ENT_NOQUOTES)?>! teplater<br/>
можно и без второго параметра, но иногда очень важно чистить кавычки.
Второй способ позаковырестее.
Будем применять делегирование — когда свойства одного объекта передаются (делегируются) другому. Способ примерно таков:
class Foo { public function functionFoo() {} } class Bar { public function functionFoo() { $foo = new Foo(); $foo->functionFoo(); } }
Т.е. мы получаем нужный объект и вызываем его метод.
Интерфейс было решено не трогать, чтобы оставить возможность минимализма для извращенцев, менял абстрактный класс.
<?php require_once('Amdy/Templater/Interface.php'); abstract class Amdy_Template_Abstract implements Amdy_Templater_Interface { protected $_data = array(); protected $templateExt = '.tpl.php'; protected $_helper = array(); protected $_helperMethod = array(); public function __set($key, $value) { $this->_data[$key] = $value; } public function __get($key) { return isset($this->_data[$key]) ? $this->_data[$key] : null; } public function setTemplateExt($ext) { $this->templateExt = $ext; } public function display($tpl) { require($tpl . $this->templateExt); } public function fetch($tpl) { ob_start(); $this->display($tpl); $retVal = ob_get_contents(); ob_end_clean(); return $retVal; } public function addHelper($helper) { $method = get_class_methods($helper); foreach ($method AS $name) if (in_array($name, $this->_helperMethod)) { die("ERROR in " . __METHOD__ . " method `$name` exist in other helper"); } $this->_helperMethod = array_merge($this->_helperMethod, $method); $this->_helper[ get_class($helper) ] = $helper; } public function __call($method, $args = array()) { if (in_array($method, $this->_helperMethod)) { foreach ($this->_helper AS $helper) { if (method_exists($helper, $method)) { call_user_func_array(array($helper, $method), $args); return true; } } } else { echo "ERROR in " . __CLASS__ . " method `$name` not exist in helper's"; } } } ?>
Добавились две переменные $_helper и $_helperMethod в первой будут храниться наши помощники, а во второй набор методов из помощников. Так же два метода — addHelper и магический __call. С помощью первого мы добавляем helper, при этом проверяя, чтобы не появилось одинаковых методов. во втором с помощью магии мы вызываем метод из helper-ов, обходя их с помощью foreach и проверяя с помощью method_exist существование и когда находим нужный helper вызываем его метод передавая параметры с помощью call_user_function_array. В php есть call_user_method_array, который, судя по названию кажется более пригодный, но он считается устаревшим и использовать его не рекомендуется, вместо этого мы передаём в качестве параметра массив с объектом и методом.
Теперь проверим навый функционал. Изменим шаблон
1. teplater !<?=$this->escape($this->test, ENT_NOQUOTES)?>! teplater<br/> 2. !<?$this->say('what', 'say')?>!<br/> 3. !<?$this->say2('what', 'say')?>!<br/>
И теперь делегируем новые методы say и say2
class test { function say($say, $say2) { echo "test say - $say, $say2"; } } class test2 { function say2($say, $say2) { echo "test2 say - $say, $say2"; } } require_once('Amdy/Templater.php'); $tpl = new Amdy_Templater(); $tpl->addHelper(new test()); $tpl->addHelper(new test2()); $tpl->test = '<u style="">test</u>'; $tpl->display('test');
Создали два класса test и test2, а у них методы say и say2. И с помощью addHelper перадаём их в шаблонизатор.
Ну и архивчег с кодом навсякий. скачать исходники
почему end_clean, а не ob_end_clean, ведь буферизация продолжится?
зачем при вызове каждого метода пробегать по ним форичем, время генерации увеличим только
потому что я невнимательно читал документацию :(. спасибо, ценное замечание, исправил.
кстати, если хочется использовать php шаблоны, то это уже скоро будет хорошей идеей. мы с коллегой бились лбом о проблему более удачного синтаксиса, особенно блочные функции как в smarty и т.д. придумали один костыль, который подойдет не только для шаблонизаторов. сейчас коллега его кодирует, идею в общих чертах я выложу, возможно, частично реализацию. способ спорный, но стал для нас золотой серединой.
я сейчас пытаюсь шаблонизаторы изучать, потом будет mvc, тк. запутался как лучше строить архитектуру сайта.
это хорошо, что запутался, значит уже знаешь больше одного варианта. а вот с mvc всё плохо, практически нет примеров фреймворков, которые действительно соответствуют парадигме. вот те же шаблоны используют вместо полноценной функциональной вьюхи. mvc — это скорее попсятина, понимать которую следует — разделяй компоненты в соотетствии с функционалом. я использую model (ORM), view (Smarty+плагины), front controller, классы для списков и форм, компонентный подход или виджеты. в Seagull на котором я многому научился юзаются цепочки с пре и пост фильтрами, в Zend Framework появились ActinStack. Ищи как проще и удобнее для тебя.
спасибо 🙂
да не важно, соответствует парадигме или нет.
главное что архитектура получается явной, открытой, расширяемой.
для большого приложения это очень важно. да и самому приятно
ты на первом месте
подправил, а то урл был слишком длинным (AmdY)