Истинная система шаблонов нужна в ситуациях, при которых возникает реальная необходимость разделения логики представления и логики приложения. Хотя далее в настоящей главе еще будет говориться о профессиональной системе шаблонов Smarty, написанной на РНР, это мало поможет вам, если вы еще не знакомы с идеей, которая лежит в основе. Чтобы помочь вам разобраться в том, как работает система шаблонов, автор продемонстрирует свою собственную систему шаблонов, которая называется QuickTemplate. Разбираясь в том, как она работает, вы не только получите представление о работе шаблонов, но и возможно, узнаете немного больше о том, как правильно писать сложный код на РНР. Прежде чем рассмотреть сценарий QuickTemplate (который в действительности является классом), давайте сначала разберемся, чего же мы хотим достичь. Чтобы полностью отделить код HTML от РНР, нам нужно будет каким-то образом отметить в документе места, куда будет помещено содержимое, за которое отвечает PHP-код. Обращаясь к классу QuickTemplate, маркеры шаблона идентифицируются строками (состоящими только из заглавных букв A-Z), заключенными с двух сторон в знаки процента (%). Например, документ, приведенный в листинге 7.1, можно определить и так, как показано в листинге 7.2.
Как видите, HTML-код в листинге 7.2 полностью свободен от PHP-кода. Им можно манипулировать, без проблем пользуясь WISIWIG-средствами ("what you see is what you get" — режим полного соответствия) любых редакторов HTML, а еще он предлагает те же возможности управления динамическим содержимым, как и аналогичный метод сегментации, продемонстрированный в листинге 7.1. В данном случае достигается свобода размещения навигационных ссылок в разных файлах, как показано в листинге 7.3.
На практике каждый из этих двух фрагментов кода в листингах 7.2 и 7.3 должен быть сохранен в своем собственном отдельном файле (предположим, что они будут называться index.html и links.html — по причинам, которые вы поймете позже). Теперь, когда шаблоны определены, используем их с классом QuickTemplate. Как и каждая из шаблонных систем, которые будут описаны в настоящей главе, класс QuickTemplate использует сложные массивы. В случае QuickTemplate массив, представленный в листинге 7.4, является типичным для шаблона, описанного в листингах 7.2 и 7.3.
Легко заметить, что этот многомерный ассоциативный массив первоначально содержит множество ключей (за исключением ключа main), отображающих маркеры шаблона из листинга 7.2. Значением каждого из них является другой массив, содержащий единственный ассоциативный элемент. Ключ (либо file, либо content) ассоциируется со значением (либо именем файла, либо строкой), представляющим данные, которые должны заменять маркер шаблона. В листинге 7.2 определяется маркер шаблона с именем %CONTENT%. Этот маркер будет заменен значением ключа content из массива $temp_data. Поскольку значением этого ключа является массив с ключом content, то используется строка "Это содержимое: %DYNAMIC%". Однако перед тем как будет подставлено значение маркера %CONTENT%, подставляемая строка также будет интерпретирована. В результате произойдет следующее:
Конечным результатом будет подстановка вместо каждого экземпляра маркера %CONTENT% строки "Это содержимое: Дополнительное содержимое". Этот процесс повторяется для каждого маркера шаблона в документе до тех пор, пока не останется ни одного маркера для подстановки. Если по какой-то причине будет обнаружен маркер, которого нет в массиве QuickTemplate (см. листинг 7.4), на месте маркера шаблона генерируется сообщение об ошибке, представляемое в виде HTML-комментария. Теперь, когда вы получили представление о поведении системы QuickTemplate, посмотрим на реальный код, заставляющий систему работать. В зависимости от того, насколько вас смутили объяснения того, как функционирует маркер шаблона, вы можете подумать или не подумать, что этот код слишком сложен для вас. Тем не менее, давайте двигаться дальше, потому что на самом деле весь код класса составляет всегонавсего 40 строк!
Листинг 7.5. Класс QuickTemplate <?php class quick_template { private $t_def; public function parse_template($subset = 'main') { $noparse = false; $content = ""; $temp_file = $this->t_def[$subset]['file']; if(isset($temp_file)) { if(strlen($temp_file) > 6) { substr($temp_file, strlen($temp_file)-6); } if(strcasecmp($ext, ".thtml") != 0) { $noparse = true; ) if(!$fr) { $content = "<!— Ошибка загрузки '$temp_file' //—>"; } else { $content » fread($fr, filesize($temp_file)); } @fclose($fr); 160 Использование РНР для разработки Web-приложений Часть I } else { :, if(isset ($this->t_def[$subset]['content'])) ( $content = $this->t_def[$subset]['content']; } else { $content = "<!— Содержимое '$subset' не определено //—>"; if(!$noparse) { $content=preg_replace("/\%([A-Z]*)\%/e", "quick_template::parse_template(strtolower('$1')) $content); } / return $content; } ' function construct($temp=*') { if(is_array($temp)) $this->t_def = $temp;
Как видите, этот класс содержит всего лишь (если игнорировать тривиальный конструктор) единственную функцию — parsetemplate (). Начнем с нее. Класс QuickTemplate функционирует, используя рекурсию (подобно большинству шаблонных механизмов). Это рекурсивное свойство позволяет шаблонной системе заменять шаблонные маркеры в контексте других шаблонов так быстро и просто.
НА ЗАМЕТКУ Не уверены, что понимаете, что такое рекурсия? В общем случае рекурсивная функция — это функция, которая вызывает саму себя в своем собственном коде. Сказанное иллюстрирует следующая функция, которая определяет наибольший общий делитель для двух чисел: <?php function gcd($a, $b) { return ($b > 0) ? gcd($b, $a % $b) : $a;
Это только один (причем довольно-таки симпатичный) пример того, насколько удобной может оказаться рекурсия. Взглянув на определение функции parse_template (), вы можете видеть, что единственный необязательный параметр $subset имеет значение по умолчанию main. Этот параметр не предназначен для использования разработчиками, применяющими класс QuickTemplate. Вместо этого он служит для определения ключа массива, который должен обрабатываться механизмом. Поскольку механизм должен где-то стартовать, этот ключ был выбран в качестве начальной точки всего процесса интерпретации шаблона. Когда начинается разбор (интерпретация), начальный шаг состоит в том, чтоб выполнить некоторую простую инициализацию трех переменных: $content, $noparse и $temp_f i l e . Первая из них — $content — сохраняет вывод, сгенерированный при разборе шаблона по отношению к определенному сегменту, который нужно разобрать. Булевская переменная Snoparse используется для определения того, должен ли текущий маркер шаблона разбираться механизмом далее. Это позволяет иметь как HTML-файл шаблона (который нужно разбирать), так и простые HTML-файлы (которые разбирать не требуется). Хотя проще не беспокоиться об этом, все же это делается из соображений эффективности — для того, чтобы исключить излишние стадии разбора. Вторая переменная — $temp_file — это просто ключ f i l e текущего поднабора. Это значение должно представлять имя файла, который нужно разбирать, если он доступен. Если ключ f i l e не представлен, предпринимается попытка найти значение ключа content, прежде чем сгенерировать ошибку. Следующая строка кода в функции проверяет с помощью функции i s s e t (), определена ли $temp_f i l e . Если переменная определена, файл затем читается в переменную из файловой системы посредством файловых функций РНР. Если же переменная $temp_file не определена, ключ content проверяется на предмет того, нет ли строки, подлежащей разбору вместо целого файла. Если ключ не существует, генерируется ошибка. До сих пор мы имели дело только с инициализацией и обработкой ошибок. Реальная работа функции parse_template () впереди. Удивительно, но вся "реальная работа" ограничена использованием единственной РНР-функции preg_replace () с применением режима /е. Вспомним из главы 3, что функция preg_replace () проверяет строку, используя регулярные выражения, которые затем заменяются другими строками. В данном случае мы просим функцию preg_replace () извлечь все экземпляры строк в верхнем регистре, заключенных между символами %, и вызываем функцию parse_template () рекурсивно. Возвращаемое значение этой функции затем используется для замены строки, которая была извлечена изначально. Эта функция — сердце всего механизма QuickTemplate. Используя функцию preg_replace (), вы рекурсивно обеспечиваете, чтобы каждый маркер шаблона, отвечающий требованиям регулярного выражения, был заменен. Результат этой замены сохраняется в переменной $content, которая затем возвращается либо в исходный сценарий, создавший экземпляр QuickTemplate, либо в другую копию функции parse_template {), которая рекурсивно вызвала его. Вот и весь класс QuickTemplate! Несмотря на то что он выглядит слишком простым, работает он достаточно хорошо. В завершение темы код в листинге 7.6 показывает класс QuickTemplate в действии/
Несложно оценить, какой объем работы позволяет сэкономить даже достаточно простой механизм шаблонов. Немного портит впечатление то, что механизм QuickTemplate не поддерживает таких действительно полезных средств, как управляющие конструкции. Однако в отличие от применения оператора include для сегментирования ваших Web-сайтов, класс QuickTemplate выполняет полезную работу для того, чтобы обеспечить полное отделение HTML-кода представления от логики приложения, управляющего им. Конечно, это разделение не обходится даром. Возможно, вы обнаружите, что написание (или использование чьих-то) механизмов шаблонов делает ваши Web-страницы менее интуитивно понятными. Прежде чем двигаться дальше, вы должны хорошо понять, что делает класс QuickTemplate (даже если вы не совсем понимаете, как он это делает). Если применение QuickTemplate смущает вас, то видимо, вы еще не готовы к освоению таких систем, как Smarty, которая описана в следующем разделе, поскольку она гораздо более сложна и развита.