Файл: ycheb/php_teach/12.php
Строк: 361
<?php
include '../../config.php';
$title='Учебник PHP';
aut();
head();
?>
<html><head>
<title>Учебник PHP, Глава 12</title>
</head>
<DIV class=infotxt>
<LI><A href="#a">Глава 12. Шаблоны </A>
<UL>
<LI><A href="#b">О чем говорилось выше
</A>
<LI><A href="#c">Нетривиальная система
шаблонов </A>
<UL>
<LI><A href="#d">Регистрация файлов</A>
<LI><A href="#e">Регистрация
переменных</A>
<LI><A href="#f">Обработка файла</A>
<LI><A href="#g">Вывод файла</A>
<LI><A href="#h">Расширение класса
template</A>
<LI><A href="#i">Недостатки системы
шаблонов</A>
<LI><A href="#j">Необоснованные надежды
на «идеальное решение»</A>
<LI><A href="#k">Снижение
быстродействия</A>
<LI><A href="#l">Ориентация дизайна на
PHP</A> </LI></UL>
<LI><A href="#m">Проект: адресная
книга</A>
<LI><A href="#n">Итоги</A> </LI></UL>
<UL></UL><A name=a></A>
<P> </P>
<P>ГЛАВА 12</P>
<P>Шаблоны</P>
<P>Шаблоны можно рассматривать как «расширение» программного кода. Шаблоны
не только автоматизируют утомительный процесс кодирования, но и
обеспечивают структурное деление проекта в рабочих группах. Роль такого
деления возрастает с увеличением объемов проекта и численности групп, а
также с усложнением архитектуры проекта, причем не только на стадии
программирования, но и при последующем сопровождении программы.</P>
<P>Сказанное стоит пояснить на конкретном примере. Допустим, у нас имеется
команда разработчиков, состоящая из web-дизайнеров и программистов. В
идеале группа web-дизайнеров трудится над созданием привлекательного и
удобного сайта, а группа программистов в это время работает над
эффективностью и широтой возможностей web-приложения. К счастью, шаблоны
заметно упрощают подобное структурирование процесса. Настоящая глава
посвящена созданию системы шаблонов, обеспечивающих подобное «разделение
труда».</P>
<P><A href="http.html://doks.gorodok.net/0" name=b></A>О чем говорилось
выше</P>
<P>До настоящего момента я упоминал о двух разных подходах к созданию
шаблонов РНР:</P>
<UL>
<LI>внедрение HTML в код РНР;
<LI>включение файлов в страницу. </LI></UL>
<P>Хотя первая схема более понятна и проще реализуется, она также в
большей степени ограничивает вашу свободу действий. Главная проблема
заключается в том, что код РНР смешивается с компонентами HTML,
образующими макет страницы. Возникающие при этом проблемы связаны не
только с необходимостью потенциальной поддержки одновременного доступа к
странице и ее модификации, но и с повышенной вероятностью ошибок при
непосредственном просмотре и редактировании страниц.</P>
<P>Вторая схема во многих ситуациях оказывается гораздо удобнее первой.
Тем не менее, хотя структура «заголовок — основная часть — колонтитул»
(см. главу 9)</P>
<P>хорошо подходит для структурирования относительно малых сайтов с четко
определенным форматом, с увеличением объемов и сложности проекта эти
ограничения проявляются все заметнее. Попытки решения этих проблем привели
к разработке новой схемы применения шаблонов, более сложной по сравнению с
двумя первыми, но и обладающей существенно большей гибкостью. В этой схеме
разделяются два главных компонента web-приложения: дизайн и
программирование. Подобное деление обеспечивает возможность параллельной
разработки (web-дизайн и программирование) без необходимости постоянной
координации на протяжении всего рабочего цикла. Более того, оно позволяет
в будущем модифицировать один компонент, не влияя на работу другого. В
следующем разделе я покажу, как устроена одна из таких схем «нетривиальных
шаблонов». Следует помнить, что эта схема существует не только в РНР.
Более того, она появилась задолго до РНР и в настоящее время используется
в нескольких языках, включая РНР, Perl и Java Server Pages. To, что
описано в этой главе, — не более чем адаптация этой схемы применительно к
РНР.</P>
<P><A name=c></A>Нетривиальная система шаблонов</P>
<P>Как говорилось ранее, главной целью при разработке подобных систем
шаблонов является фактическое отделение дизайна от функциональных
возможностей. Собственно, эта система и создается для того, чтобы
программисты и дизайнеры могли независимо трудиться над своими аспектами
приложения, не мешая работе другой группы.</P>
<P>К счастью, сделать это проще, чем кажется на первый взгляд, — при
условии, что до начала разработки было проведено некоторое предварительное
планирование. В листинге 12.1 представлен некий базовый шаблон, созданный
на основе материала этой главы.</P>
<P>Листинг 12.1. Пример шаблона</P>
<P><html></P>
<P><head></P>
<P><title>:::::{page_title}:::::</title></P>
<P></head></P>
<P><body bgcolor="{bg_color}"></P>
<P>Welcome to your default home page. {user_name}!<br></P>
<P>You have 5 MB and 3 email addresses at your disposal.<br></P>
<P>Have fun!</P>
<P></body></P>
<P></html></P>
<P>Обратите внимание на три строки (page_title, bg_color и userjiame),
заключенные в фигурные скобки ({ }). Фигурные скобки имеют особый смысл
при обработке шаблонов — заключенная в них строка интерпретируется как имя
переменной, вместо которого подставляется ее значение. Дизайнер строит
страницу по своему усмотрению; все, что от него потребуется, — включать в
соответствующие места документа эти ключевые строки. Конечно, программисты
и дизайнеры должны заранее согласовать имена всех переменных!</P>
<P>Итак, как же работает эта схема? Прежде всего, возможно, нам придется
одновременно работать с несколькими шаблонами, обладающими одними и теми
же общими атрибутами. В таких ситуациях применение технологии
объектно-ориентированного программирования (ООП) оказывается особенно
эффективным. По этой причине все функции построения и выполнения операций
с шаблонами будут оформлены в виде методов класса. Определение класса
начинается так:</P>
<P>class template {</P>
<P>VAR $files = array( );</P>
<P>VAR $variables = array( );</P>
<P>VAR $openi ng_escape = '{';</P>
<P>VAR $closing_escape = '}';</P>
<P>В массиве $files хранятся идентификаторы файлов и содержимое каждого
файла. Атрибут $variables представляет собой двухмерный массив для
хранения файлового идентификатора (ключа) и всех соответствующих
переменных, обрабатываемых в схеме шаблонов. Наконец, атрибуты
$opening_escape и $closing_escape задают ограничители для частей шаблона,
которые должны заменяться системой. Как было показано в листинге 12.1, в
наших примерах в качестве ограничителей будут использоваться фигурные
скобки ({ }). Впрочем, вы можете изменить два последних атрибута и выбрать
ограничители по своему усмотрению. Главное — проследите за тем, чтобы эти
символы не использовались для других целей.</P>
<P>Каждый метод класса решает конкретную задачу, соответствующую той или
иной операции в процессе обработки шаблона. На простейшем уровне этот
процесс можно разделить на четыре стадии.</P>
<UL>
<LI>Регистрация файлов — регистрация всех файлов, обрабатываемых
сценариями шаблонов.
<LI>Регистрация переменных — регистрация всех переменных, которые должны
заменяться своими значениями в зарегистрированных файлах.
<LI>Обработка файлов — замена всех переменных, находящихся между
ограничителями, в зарегистрированных файлах.
<LI>Вывод файла — вывод обработанных зарегистрированных файлов в
браузере. </LI></UL>
<P>Применение концепций ООП в РНР рассматривалось в главе 6. Если вы не
знакомы с ООП, я рекомендую бегло просмотреть главу 6 перед тем, как
читать дальше.</P>
<P><A name=d></A>Регистрация файлов</P>
<P>В процессе регистрации содержимое файла сохраняется в массиве с ключом,
однозначно идентифицирующим этот файл. Метод register_file( ) открывает и
читает содержимое файла, имя которого передается в качестве параметра. Код
этого метода приведен в листинге 12.2.</P>
<P>Листинг 12.2. Метод регистрации файла</P>
<P>function register_file($file_id, $file_name) {</P>
<P>// Открыть $file_name для чтения или завершить программу</P>
<P>// с выдачей сообщения об ошибке.</P>
<P>$fh = fopen($file_name, "r") or die("Couldn't open $file_name!");</P>
<P>// Прочитать все содержимое файла $file_name в переменную.</P>
<P>$file_contents = fread($fh, filesize($file_name));</P>
<P>// Присвоить содержимое элементу массива</P>
<P>// с ключом $file_id. $this->files[$file_id] = $file_contents;</P>
<P>// Работа с файлом завершена, закрыть его.</P>
<P>fclose($fh);</P>
<P>}</P>
<P>Параметр $file_id содержит идентификатор — «псевдоним» для последующих
операций с файлом, упрощающий последующие вызовы метода. Идентификатор
используется в качестве ключа для индексирования массива $files. Пример
регистрации файла:</P>
<P>// Включить класс шаблона</P>
<P>include("tempiate.class"):</P>
<P>// Создать новый экземпляр класса</P>
<P>$template = new template:</P>
<P>// Зарегистрировать файл "homepage.html",</P>
<P>// присвоив ему псевдоним "home"</P>
<P>$template->register_file("home", "homepage.html");</P>
<P><A name=e></A>Регистрация переменных</P>
<P>После регистрации файлов необходимо зарегистрировать все переменные,
которые будут интерпретироваться особым образом. Метод register_variables(
) (листинг 12.3) работает по тому же принципу, что и register_file( ), —
он читает имена переменных и сохраняет их в массиве $variables.</P>
<P>Листинг 12.3. Метод регистрации переменнных</P>
<P>function register_vanables($file_id, $variable_name) {</P>
<P>// Попытаться создать массив,</P>
<P>// содержащий переданные имена переменных</P>
<P>$input_variables - explode(".", $variable_name);</P>
<P>// Перебрать имена переменных</P>
<P>while (Iist($value) = each($input_variables)) :</P>
<P>// Присвоить значение очередному элементу массива</P>
<P>$this->variables $this->variables[$file_id][] = $value:</P>
<P>endwhile;</P>
<P>}</P>
<P>В параметре $file_id передается ранее присвоенный псевдоним файла.
Например, в предыдущем примере файлу homepage.html был присвоен псевдоним
home. Обратите внимание — при регистрации имен переменных, которые должны
особым образом обрабатываться в файле homepage.html, вы должны ссылаться
на файл по псевдониму! В параметре $variable_name передаются имена одной
или нескольких переменных, регистрируемых для указанного псевдонима.
Пример:</P>
<P>// Включить класс шаблона include("tempiate.class");</P>
<P>// Создать новый экземпляр класса $template = new template;</P>
<P>// Зарегистрировать файл "homepage.html",</P>
<P>// присвоив ему псевдоним "home" $template->register_file("home",
"homepage.html");</P>
<P>// Зарегистрировать несколько переменных</P>
<P>$template->register_variablest"home",
"page_title.bg_color,user_name");</P>
<P><A name=f></A>Обработка файла</P>
<P>После того как файлы и переменные будут зарегистрированы в системе
шаблонов, можно переходить к обработке зарегистрированных файлов и замене
всех ссылок на переменные с соответствующими значениями. Метод
file_parser( ) приведен в листинге 12.4.</P>
<P>Листинг 12.4. Метод обработки файла</P>
<P>function file_parser($file_id) {</P>
<P>// Сколько переменных зарегистрировано для данного файла?</P>
<P>$varcount = count($this->variables[$file_id]);</P>
<P>// Сколько файлов зарегистрировано?</P>
<P>$keys = array_keys($this->files):</P>
<P>// Если файл $file_id существует в массиве</P>
<P>$this->files</P>
<P>// и с ним связаны зарегистрированные переменные</P>
<P>If ( (in_array($file_id. $keys)) && ($varcount > 0) ) :</P>
<P>// Сбросить $x $x = 0:</P>
<P>// Пока остаются переменные для обработки...</P>
<P>while ($x < sizeof($this->variables[$file_id])) :</P>
<P>// Получить имя очередной переменной $string =
$this->variables[$file_id][$x];</P>
<P>// Получить значение переменной. Обратите внимание:</P>
<P>// для получения значения используется конструкция $$.</P>
<P>// Полученное значение подставляется в файл вместо</P>
<P>// указанного имени переменной.GLOBAL $$string:</P>
<P>// Построить точный текст замены вместе с ограничителями</P>
<P>$needle =
$this->opening_escape.$string.$this->closing_escape;</P>
<P>// Выполнить замену.</P>
<P>$this->files[$file_id] = str_replace( $needle.</P>
<P>$$string.</P>
<P>$this->files[$file_id]);</P>
<P>// Увеличить $х $x++;</P>
<P>endwhile;</P>
<P>endif;</P>
<P>}</P>
<P>Сначала мы проверяем, присутствует ли указанное имя файла в массиве
$this->files. Если файл был зарегистрирован, мы также проверяем, были
ли для него зарегистрированы переменные, и если были — значения этих
переменных подставляются в содержимое $file_id. Пример:</P>
<P>// Включить класс шаблона include("template. class") ;</P>
<P>$page_title = "Welcome to your homepage!";</P>
<P>$bg_color = "white"; $user_name = "Chef Jacques";</P>
<P>// Создать новый экземпляр класса</P>
<P>$template = new template;</P>
<P>// Зарегистрировать файл "homepage.html",</P>
<P>II присвоив ему псевдоним "home"</P>
<P>$template->register_file( "home", "homepage.html");</P>
<P>// Зарегистрировать несолько переменных</P>
<P>$template->register_variables("home", "page_titie, bg_color,
user_name");</P>
<P>$template->file_parser("home");</P>
<P>Поскольку переменные page_title, bg_color и user_name были
зарегистрированы, значения каждой переменной (присвоенные в начале
сценария) подставляются в страницу homepage.html, хранящуюся в массиве
files (атрибуте объекта-шаблона). На этом предварительная подготовка
завершается, остается лишь вывести полученный шаблон в браузере. Эта
операция рассматривается в следующем разделе.</P>
<P><A name=g></A>Вывод файла</P>
<P>Вероятно, после обработки файла вы захотите отправить его в браузер,
чтобы пользователь увидел результат обработки шаблона. В нашем примере для
вывода</P>
<P>файла создается отдельный метод, приведенный в листинге 12.5, однако в
зависимости от ситуации вывод также может интегрироваться с методом f i I
e_parser().</P>
<P>Листинг 12.5. Метод вывода файла в браузере</P>
<P>function pnnt_file($file_id) {</P>
<P>// Вывести содержимое файла с идентификатором</P>
<P>$file_id print $this->files[$file id];</P>
<P>}</P>
<P>Все очень просто — при вызове print_file( ) содержимое файла,
представленного ключом $file_id, передается в браузер.</P>
<P>В листинге 12.6 приведен пример использования класса template.</P>
<P>Листинг 12.6. Пример использования класса template</P>
<P>// Включить класс шаблона, include("tempiate.class");</P>
<P>// Присвоить значения переменным</P>
<P>$page_title = "Welcome to your homepage!";</P>
<P>$bg_color = "white"; $user_name = "Chef Jacques":</P>
<P>// Создать новый экземпляр класса $template= new template;</P>
<P>// Зарегистрировать файл "homepage.html" с псевдонимом "home"</P>
<P>$template->register_file("home", "homepage.html");</P>
<P>// Зарегистрировать переменные</P>
<P>$template->register_variables("home", "page_title,
bg_color.user_name");</P>
<P>$template->file_parser("home");</P>
<P>// Передать результат в браузер</P>
<P>$template->print_file("home");</P>
<P>Если бы шаблон, приведенный в листинге 12.1, хранился в файле
homepage.html в одном каталоге со сценарием из листинга 12.6, то в браузер
был бы направлен следующий код HTML:</P>
<P><html></P>
<P><head></P>
<P><title>:::::Welcome to your homepage!:::::</title></P>
<P></head></P>
<P><body bgcolor=white></P>
<P>Welcome to your default home page, Chef Jacques!<br></P>
<P>You have 5 MB and 3 email addresses at your disposal.<br></P>
<P>Have fun!</P>
<P></body></P>
<P></html></P>
<P>Как видно из приведенного примера, все зарегистрированные переменные
были заменены соответствующими значениями. При всей своей простоте класс
tempi ate</P>
<P>обеспечивает стопроцентное разделение уровней программирования и
дизайна. Полный код класса template приведен в листинге 12.7.</P>
<P>Листинг 12.7. Полный код класса template</P>
<P>class template {</P>
<P>VAR $files = array( );</P>
<P>VAR $variables = array( );</P>
<P>VAR $opening_escape = '{';</P>
<P>VAR $closing_escape = '}' ;</P>
<P>// Функция: register_file( )</P>
<P>// Назначение: сохранение в массиве содержимого файла.</P>
<P>// определяемого идентификатором $file_id</P>
<P>function register_file($file_id. $file_name) {</P>
<P>// Открыть $file_name для чтения или завершить программу</P>
<P>// с выдачей сообщения об ошибке.</P>
<P>$fh = fopen($file_name, "r") or die("Couldn't open $file_name!");</P>
<P>// Прочитать все содержимое файла $file_name в переменную.</P>
<P>$file_contents = fread($fh, filesize($file_name));</P>
<P>// Присвоить содержимое элементу массива</P>
<P>// с ключом $file_id. $this->files[$file_id] = $file_contents;</P>
<P>// Работа с файлом завершена, закрыть его.</P>
<P>fclose($fh):</P>
<P>} // Функция: register_variables( )</P>
<P>// Назначение: сохранение переменных, переданных</P>
<P>// в параметре $variable_name. в массиве с ключом $file_id.</P>
<P>function register_variables($file_id, $variable_name) {</P>
<P>// Попытаться создать массив.</P>
<P>// содержащий переданные имена переменных</P>
<P>$input_variables = explode(".", $vahable_name);</P>
<P>// Перебрать имена переменных</P>
<P>while (list(, $value) = each($input_variables)) :</P>
<P>// Присвоить значение очередному элементу массива $this->variables
$this->variables[$file_id][] = $value:</P>
<P>endwhile;</P>
<P>} // Функция: file_parser( )</P>
<P>// Назначение: замена всех зарегистрированных переменных</P>
<P>// в файле с идентификатором $file_id</P>
<P>function file_parser($file_id) {</P>
<P>// Сколько переменных зарегистрировано для данного файла?</P>
<P>$varcount = count($this->variables[$file_id]):</P>
<P>// Сколько файлов зарегистрировано?</P>
<P>$keys = array_keys($this->files):</P>
<P>// Если файл $file_id существует в массиве $this->files</P>
<P>// и с ним связаны зарегистрированные переменные</P>
<P>if ( (in_array($file_id. $keys)) && ($varcount > 0) ) :</P>
<P>// Сбросить $х $x - 0;</P>
<P>// Пока остаются переменные для обработки...</P>
<P>while ($x < sizeof($this->variables[$file_id])) :</P>
<P>// Получить имя очередной переменной</P>
<P>$string = $this->variables[$file_id][$x];</P>
<P>// Получить значение переменной. Обратите внимание:</P>
<P>// для получения значения используется конструкция $$.</P>
<P>// Полученное значение подставляется в файл вместо</P>
<P>// указанного имени переменной.</P>
<P>GLOBAL $$string;</P>
<P>// Построить точный текст замены вместе с ограничителями</P>
<P>$needle = $this->opemng_escape.$string.$this->closing_escape;</P>
<P>// Выполнить замену.</P>
<P>$this->files[$file_id] = str_replace( $needle, $$string,</P>
<P>$this->files[$file_idj);</P>
<P>// Увеличить $х $x++;</P>
<P>endwhile;</P>
<P>endif;</P>
<P>}</P>
<P>// Функция: print_file()</P>
<P>// Назначение: вывод содержимого файла,</P>
<P>// определяемого параметром $file_id</P>
<P>function print_file($file_id) {</P>
<P>// Вывести содержимое файла с идентификатором $file_id</P>
<P>print $this->files[$file_id];</P>
<P>}</P>
<P>} //END template.class</P>
<P><A href="http.html://doks.gorodok.net/996" name=h></A>Расширения класса
template</P>
<P>Конечно, класс tempi ate обладает весьма ограниченными возможностями,
хотя для проектов, создаваемых на скорую руку, он вполне подходит.
Объектно-ориентированные схемы хороши тем, что они позволяют легко
наращивать функциональность, не беспокоясь о возможных нарушениях работы
существующего кода. Допустим, вы решили создать новый метод, который будет
загружать значения для последующей замены из базы данных. Хотя такой метод
устроен чуть сложнее, чем метод file_parser( ), производящий простую
замену глобальных переменных, его реализация на базе SQL состоит из
нескольких строк и легко инкапсулируется в отдельном методе. Более того,
мы создадим нечто подобное в проекте адресной книги, завершающем эту
главу.</P>
<P>В класс tempi ate можно внести несколько очевидных усовершенствований.
Первое — объединение функций register_file( ) и register_variables( ),
обеспечивающее автоматическую регистрацию переменных для каждого
регистрируемого файла. Конечно, при этом также необходимо реализовать
проверку ошибок, чтобы предотвратить регистрацию неверных файлов и
переменных.</P>
<P>Однако на этом возможности усовершенствования далеко не исчерпаны.
Подумайте, как бы вы реализовали методы, работающие с целыми массивами? На
самом деле это проще, чем кажется на первый взгляд. Проанализируйте
решение, использованное в проекте адресной книги в конце главы. Общие
принципы легко трансформируются под любую конкретную реализацию.</P>
<P>Общие схемы работы с шаблонами были реализованы на нескольких языках и
ни в коем случае не являются чем-то принципиально новым. В Web можно найти
немало информации о реализации шаблонов. Рекомендую два особенно
интересных ресурса — сборники статей, написанных с ориентацией на
JavaScript:</P>
<UL>
<LI><A
href="http.html://www.netscape.com/viewsource/long_ssjs/long_ssjs.html">http://www.netscape.com/viewsource/long_ssjs/long_ssjs.html</A>;
<LI><A
href="http.html://www.netscape.com/viewsource/schroder_template/schroder_template.html">http://www.netscape.com/viewsource/schroder_template/schroder_template.html</A>.
</LI></UL>
<P>В следующей статье затронута тема использования шаблонов применительно
к Java Server Pages:</P>
<UL>
<LI><A
href="http.html://www-4.ibm.com/software/webservers/appserv/doc/guide/asgdwp.html">http://www-4.ibm.com/software/webservers/appserv/doc/guide/asgdwp.html</A>.
</LI></UL>
<P>Кроме того, описанная схема построения шаблонов используется в
нескольких библиотеках РНР, среди которых наибольший интерес представляют
следующие:</P>
<UL>
<LI>PHPLib Base Library: <A
href="http.html://phplib.netuse.de/">http://phplib.netuse.de/</A>;
<LI>Richard Hayes's Template Class: <A
href="http.html://www.heyes-computing.net/">http://www.heyes-computing.net/</A>;
<LI>Fast Template: <A
href="http.html://www.thewebmasters.net/php">http://www.thewebmasters.net/php</A>.
</LI></UL>
<P>На сайте ресурсов РНР, PHPBuilder (<A
href="http.html://www.phpbuilder.com/">http://www.phpbuilder.com/</A>), также
имеется несколько интересных учебников, посвященных обработке шаблонов.
Кроме того, загляните на сайт РНР Classes Repository (<A
href="http.html://phpclasses.upperdesign.com/">http://phpclasses.upperdesign.com/</A>),
здесь также можно найти несколько реализаций.</P>
<P><A name=i></A>Недостатки системы шаблонов</P>
<P>Хотя рассмотренная система шаблонов справляется со своей главной
задачей — полным разделением дизайна и программирования, она не лишена
недостатков. Некоторые из этих недостатков перечислены ниже.</P>
<P><A name=j></A>Необоснованные надежды на «идеальное решение»</P>
<P>Шаблоны помогают четко выделить в проекте аспекты программирования и
дизайна, но они не заменяют нормального взаимодействия между этими
аспектами. Более того, правильность их работы зависит от предварительного
согласования списка переменных, заменяемых в процессе обработки шаблона.
Как и в любом успешном проекте, переходить к написанию кода РНР следует
лишь после тщательной проработки спецификации всего приложения. Это
значительно уменьшает вероятность ошибок при последующей обработке,
приводящих к непредвиденным последствиям при использовании шаблонов.</P>
<P><A name=k></A>Снижение быстродействия</P>
<P>Затраты на обработку файлов приводят к некоторому замедлению работы
программы. В какой мере замедляется работа, зависит от ряда факторов, в
том числе от размера страницы, размера запроса SQL (если они задействован)
и аппаратной конфигурации компьютера. Как правило, эти потери настолько
малы, что ими можно пренебречь, но в некоторых ситуациях они оказываются
довольно значительными (например, при одновременной обработке нескольких
шаблонов в условиях высокого трафика).</P>
<P><A name=l></A>Ориентация дизайна на РНР</P>
<P>Одна из главных целей создания шаблонов заключается в том, чтобы по
возможности изолировать дизайнера от программного кода при редактировании
внешнего вида и поведения страницы. В идеальном случае дизайнер должен
обладать некоторыми навыками программирования или, по крайней мере, быть
знакомым с общими концепциями — переменными, циклами и условными
командами. Дизайнеру, абсолютно не разбирающемуся в них, применение
шаблонов практически ничего не даст, кроме относительно бесполезных
сведений из области синтаксиса. В общем, независимо от того, захотите вы
пользоваться этим типом шаблонов или нет, я настоятельно рекомендую
потратить немного времени и обучить дизайнера азам языка РНР... а еще
лучше — купить ему эту книгу! От этого выиграют обе стороны, поскольку
дизайнер приобретет дополнительные навыки и станет более ценным членом
рабочей группы, а у программиста появится новый источник идей. Может,
дизайнер и не изобретет ничего выдающегося, но зато он взглянет на
ситуацию под новым углом зрения, обычно недоступным для программиста.</P>
<P><A name=m></A>Проект: адресная книга</P>
<P>Хотя системы шаблонов хорошо подходят для многих типов web-приложений,
они приносят особенную пользу в приложениях, ориентированных на выборку и
вывод данных, в которых особенно важно обеспечить правильное
форматирование.</P>
<P>Примером такого приложения является адресная книга. Представьте себе
обычную (бумажную) адресную книгу: все страницы выглядят практически
одинаково, различаются разве что буквы, с которых начинаются имена на
конкретной странице. Аналогичный подход можно применить и к адресной книге
на базе Web. Форматирование в данном случае играет еще более важную роль,
поскольку не исключено, что данные придется экспортировать в другое
приложение в каком-нибудь специфическом формате. Подобные приложения
прекрасно работают на базе шаблонов, поскольку дизайнеру остается лишь
создать единый формат страницы, который будет использоваться для всех 26
букв алфавита.</P>
<P>Прежде всего, необходимо решить, какие данные и в каком формате будут
храниться в адресной книге. Конечно, оптимальным носителем информации в
данном случае является база данных, поскольку это упростит такие полезные
операции, как поиск и сортировка данных. В своем примере я воспользуюсь
СУБД MySQL. Определение таблицы выглядит следующим образом:</P>
<P>mysql>CREATE table addressbook (</P>
<P>last_name char(35) NOT NULL,</P>
<P>first_name char(20) MOT NULL,</P>
<P>tel char(20) NOT NULL,</P>
<P>email char(55) NOT NULL );</P>
<P>Разумеется, вы можете самостоятельно добавить поля для хранения адреса,
города и т. д. Для наглядности я буду использовать сокращенную таблицу,
приведенную ранее.</P>
<P>Теперь я возьму на себя роль дизайнера и займусь созданием шаблонов.
Для этого проекта нужны два шаблона. Код первого, «родительского» шаблона
book.html приведен в листинге 12.8.</P>
<P>Листинг 12.8. Основной шаблон адресной книги book.html</P>
<P><html></P>
<P><head></P>
<P><title>:::::{page_title}:::::</title></P>
<P></head></P>
<P><body bgcolor="white"></P>
<P><table cellpadding=2 cellspacing=2 width=600></P>
<P><h1>Address Book: {letter}</h1> <tr><td></P>
<P><a href="index.html.php?letter=a">A</a> | </P>
<P><a href="index.html.php?letter=b">B</a> | </P>
<P><a href="index.html.php?letter=c">C</a> | </P>
<P><a href="index.html.php?letter=d">D</a> | </P>
<P><a href="index.html.php?letter=e">E</a> | </P>
<P><a href="index.html.php?letter=f">F</a> | </P>
<P><a href="index.html,php?letter=g">G</a> | </P>
<P><a href="index.html.php?letter=h">H</a> | </P>
<P><a href="index.html.php?letter=i">I</a> | </P>
<P><a href="index.html.php?letter=j">J</a> | </P>
<P><a href="index.html.php?letter=k">K</a> | </P>
<P><a href="index.html.php?letter=l">L</a> | </P>
<P><a href="index.html.php?letter=m">M</a> | </P>
<P><a href="index.html.php?letter=n">N</a> | </P>
<P><a href="index.html.php?letter=o">O</a> | </P>
<P><a href="index.html.php?letter=p">P</a> | </P>
<P><a href="index.html.php?letter=q">Q</a> | </P>
<P><a href="index.html.php?letter=r">R</a> | </P>
<P><a href="index.html.php?letter=s">S</a> | </P>
<P><a href="index.html.php?letter=t">T</a> | </P>
<P><a href="index.html.php?letter=u">U</a> | </P>
<P><a href="index.html.php?letter=v">V</a> | </P>
<P><a href="index.html.php?letter=w">W</a> | </P>
<P><a href="index.html.php?letter=x">X</a> | </P>
<P><a href="index.html.php?letter=y">Y</a> | </P>
<P><a href="index.html.php?letter=z">Z</a></P>
<P></td></tr></P>
<P>{rows.addresses}</P>
<P></table></P>
<P></body></P>
<P></html></P>
<P>Как видите, файл в основном состоит из ссылок с разными буквами
алфавита. Если щелкнуть на букве, в браузере отображается информация обо
всех контактах в адресной книге, фамилии которых начинаются с указанной
буквы.</P>
<P>В странице встречаются три имени переменных, заключенных в
ограничители: page_title, letter и rows_addresses. Смысл первых двух
переменных очевиден: текст в заголовке страницы и буква адресной книги,
использованная для выборки текущих адресных данных. Третья переменная
относится к дополнительному шаблону (листинг 12.9) и определяет файл
конфигурации таблицы, включаемый в основной шаблон. Файлы конфигурации
таблиц используются в связи с тем, что в сложных страницах может быть
одновременно задействовано несколько шаблонов, в каждом из которых данные
форматируются в виде таблиц HTML. Шаблон rows.addresses (листинг 12.9)
выполняет вспомогательные функции и вставляется в основной шаблон
book.html. Вскоре вы поймете, почему это необходимо.</P>
<P>Листинг 12.9. Вспомогательный шаблон rows.addresses</P>
<P><tr><td bgcolor="#c0c0c0"></P>
<P><b>{last_name},{first_name}</b></P>
<P></td></tr></P>
<P><tr><td></P>
<P><b>{telephone}</b></P>
<P></td></tr></P>
<P><tr><td></P>
<P><b><a href =
"mailto:{email}">{email}</a></b></P>
<P></td></tr></P>
<P>В листинге 12.9 встречаются четыре переменных, заключенных в
ограничители: last_name, first_name, telephone и emal. Смысл этих
переменных очевиден (см. определение таблицы addressbook). Следует
заметить, что этот файл состоит только из табличных тегов строк
(<tr>...</tr>) и ячеек (<td>...</td>). Дело в том,
что этот файл вставляется в шаблон многократно, по одному разу для каждого
адреса, прочитанного из базы данных. Поскольку имя переменной
rows.addresses в листинге 12.8 включается внутрь тегов
<table>...</table>, форматирование HTML будет обработано
правильно. Чтобы вы лучше поняли, как работает этот шаблон, взгляните на
рис. 12.1 — на нем изображена копия страницы адресной книги. Затем
проанализируйте листинг 12.10, содержащий исходный текст этой страницы. Вы
увидите, что содержимое файла rows.addresses многократно встречается в
странице.</P>
<P>Листинг 12.10. Исходный текст страницы, изображенной на рис. 12.1</P>
<P><html></P>
<P><head></P>
<P><title>:::::Address Book:::::</title></P>
<P></head></P>
<P><body bgcolor="white"></P>
<P><table cellpadd1ng=2 cellspacing=2 width=600></P>
<P><hl>Address Book: f</hl></P>
<P><tr><td></P>
<P><a href="index.html.php?letter=a">A</a> | </P>
<P><a href="index.html.php?letter=b">B</a> | </P>
<P><a href="index.html.php?letter=c">C</a> | </P>
<P><a href="index.html.php?letter=d">D</a> | </P>
<P><a href="index.html.php?letter=e">E</a> | </P>
<P><a href="index.html.php?letter=f">F</a> | </P>
<P><a href="index.html.php?letter=g">G</a> | </P>
<P><a href="index.html.php?letter=h">H</a> | </P>
<P><a href="index.html.php?letter=i">I</a> | </P>
<P><a href="index.html.php?letter=j">J</a> | </P>
<P><a href="index.html.php?letter=k">K</a> | </P>
<P><a href="index.html.php?letter=l">L</a> | </P>
<P><a href="index.html.php?letter=m">M</a> | </P>
<P><a href="index.html.php?1etter=n">N</a> | </P>
<P><a href="index.html.php?letter=o">0</a> | </P>
<P><a href="index.html.php?letter=p">P</a> | </P>
<P><a href="index.html.php?letter=q">Q</a> | </P>
<P><a href="index.html.php?letter=r">R</a> | </P>
<P><a href="index.html.php?letter=s">S</a> | </P>
<P><a href="index.html.php?letter=t">T</a> | </P>
<P><a href="index.html.php?letter=u">U</a> | </P>
<P><a href="index.html.php?letter=v">V</a> | </P>
<P><a href="index.html.php?letter=w">W</a> | </P>
<P><a href="index.html.php?letter=x">X</a> | </P>
<P><a href="index.html.php?letter=y">Y</a> | </P>
<P><a href="index.html.php?letter=z">Z</a></P>
<P></td></tr></P>
<P><tr><t</P>
<P>bgcolor="#c0c0c0"></P>
<P><b>Fries.Bobby</b></P>
<P></td></tr></P>
<P><tr><td></P>
<P><b>(212) 563-5678</b></P>
<P></td></tr></P>
<P><tr><td> "</P>
<P><b></P>
<P><a href="mailto.html:bobby@fries.com">bobby@fries.com</a></P>
<P></b></P>
<P></td></tr></P>
<P><tr><td bgcolor="#c0c0c0"></P>
<P><b>Frenchy.Pierre</b></P>
<P></td></tr></P>
<P><tr><td></P>
<P><b>002-(30)-09-7654321</b></P>
<P></td></tr></P>
<P><tr><td></P>
<P><b><a href =
"mailto:frenchy@frenchtv.com"><BR>frenchy@frenchtv.com</a></b></P>
<P></td></tr></P>
<P></table></P>
<P></body></P>
<P></html></P>
<P>Как видно из приведенного листинга, в адресной книге хранятся записи
двух лиц, фамилии которых начинаются с буквы F: Bobby Fries и Pierre
Frenchy. Соответственно в таблицу вставляются данные двух записей.</P>
<P>Дизайнерская часть проекта адресной книги завершена, и я перехожу к
роли программиста. Возможно, вас удивит тот факт, что класс tempiate.
class (см. листинг 12.7) практически не изменился, если не считать
появления одного нового метода — address_sql( ). Код этого метода приведен
в листинге 12.11.</P>
<P>Листинг 12.11. Обработка данных, полученных в результате запроса</P>
<P>class template {</P>
<P>VAR $files = array( );</P>
<P>VAR $variab!es = array( ):</P>
<P>VAR $sql = array();</P>
<P>VAR $opening_escape - '{';</P>
<P>VAR $closing_escape = '}';</P>
<P>VAR $host = "localhost";</P>
<P>VAR $user = "root";</P>
<P>VAR $pswd = "";</P>
<P>VAR $db = "book";</P>
<P>VAR $address table = "addressbook";</P>
<P>function address_sql($file_id, $vanable_name, $letter) {</P>
<P>// Подключиться к серверу MySQL и выбрать базу данных </P>
<P>mysql_connect($this->host, $this->user, $this->pswd)</P>
<P>or die("Couldn't connect to MySQL server!");</P>
<P>mysql_select_db($this->db) or die('Couldn't select MySQL
database!");</P>
<P>// Обратиться с запросом к базе данных</P>
<P>$query = "SELECT last_name, first_name, tel, email</P>
<P>FROM $this->address_table WHERE lastjiame LIKE '$letter%' ";</P>
<P>$result = mysql_query($query);</P>
<P>// Открыть файл "rows.addresses"</P>
<P>// и прочитать его содержимое в переменную</P>
<P>$fh - fopen("$variable_name", "r");</P>
<P>$file_contents = fread($fh, filesize("rows.addresses") ):</P>
<P>// Заменить имена переменных в ограничителях</P>
<P>// данными из базы.</P>
<P>while ($row = mysql_fetch_array($result)) :</P>
<P>$new_row = $file_contents;</P>
<P>$new_row=str_replace($this->opening_escape.<BR>"last_name".$this->closing_escape. </P>
<P>$row["last_name"]. $new_row);</P>
<P>$new_row=</P>
<P>str_replace($th1s->opening_escape.<BR>"first_name".$this->closing_escape.</P>
<P>$row["first_name"], $new_row);</P>
<P>$new_row=str_replace($this->opening_escape.<BR>"telephone".$this->closing_escape.</P>
<P> $row["tel"], $new_row);</P>
<P>$new_row =
str_replace($this->opening_escape.<BR>"email".$this->closing_escape,</P>
<P> $row["email"],</P>
<P>$new_row);</P>
<P>// Присоединить запись к итоговой строке замены</P>
<P>$complete_table .= $new_row;</P>
<P>endwhile;</P>
<P>$sql_array_key = $variable_name;</P>
<P>$this->sql[$sql_array_key] = $complete_table;</P>
<P>// Включить ключ в массив variables для последующего поиска</P>
<P>$this->variables[$file_id][ ] = $variable_name;</P>
<P>// Закрыть файловый манипулятор fclose(lfh);</P>
<P>Комментариев, приведенных в листинге 12.11, вполне достаточно для того,
чтобы вы разобрались в происходящем, однако я должен сделать несколько
важных замечаний. Во-первых, обратите внимание на то, что файл
rows.addresses открывается только один раз. Возможен и другой вариант —
многократно открывать и закрывать файл rows.addresses, каждый раз
производя замену и присоединяя его содержимое к переменной
$complete_table. Впрочем, такое решение будет крайне неэффективным.
Потратьте немного времени и разберитесь в том, как новые данные таблицы в
цикле присоединяются к переменной $complete_table.</P>
<P>Второе, на что следует обратить внимание при просмотре листинга 12.11,
— появление пяти новых атрибутов класса: $host, $user, $pswd, $db и
$address_table. В этих атрибутах хранится информация, необходимая для
сервера SQL. Полагаю, смысл каждого атрибута понятен без объяснений, а
если нет — вернитесь и повторите материал главы 11.</P>
<P> <IMG src="2_1_12.1Rus.jpg"></P>
<P>Рис. 12.1. Страница адресной книги</P>
<P>Все, что осталось сделать — написать файл index.php, инициирующий
обработку шаблонов, Код этого файла приведен в листинге 12.12. Если
щелкнуть на одной из ссылок (index.php?letter=буква) на странице book.html
(см. листинг 12.8), загружается страница index.php, которая, в свою
очередь, заново строит book.html с включением новой информации.</P>
<P>Листинг 12.12. Обработчик шаблонов index.php</P>
<P>include("Listing12-11.php"); $page_title = "Address Book";</P>
<P>// По умолчанию загружается страница с фамилиями,</P>
<P>// начинающимися с буквы 'а' if (! isset($letter) ) :</P>
<P>$letter = "а";</P>
<P>endif ;</P>
<P>$tpl = new template;</P>
<P>$tpl->register_file("book", "book.html");</P>
<P>$tpl->register_variables("book", "page_title.letter");</P>
<P>$tpl ->address_sql("book", "rows.addresses", "$letter");</P>
<P>$tpl ->file_parser("book");</P>
<P>$tpl->phnt_fil("book");</P>
<P>Перед вами практический пример, показывающий, как при помощи шаблонов
организовать эффективное разделение труда между программистом и
дизайнером. Подумайте, как бы вы использовали шаблоны для организации
своих разработок. Готов поспорить, что вы найдете им полезное
применение.</P>
<P><A name=n></A>Итоги</P>
<P>В этой главе была представлена концепция, особенно важная как для РНР,
так и для web-программирования в целом, — применение шаблонов. Глава
началась с обзора двух схем; упоминавшихся ранее, — простой замены
переменных средствами РНР и логическим делением страницы при помощи
включаемых файлов. Затем мы познакомились с третьей схемой применения
шаблонов, позволяющей полностью отделить программирование от дизайна
страницы. Оставшаяся часть главы была посвящена анализу класса,
построенного для реализации шаблонов такого рода. Главу завершает пример
практического использования шаблонов в адресной книге на базе Web. В
частности, в этой главе рассматривались следующие темы:</P>
<UL>
<LI>для чего нужны шаблоны;
<LI>простой шаблон № 1: внедрение РНР в HTML;
<LI>простой шаблон № 2: разделение компонентов страницы при помощи
включаемых файлов;
<LI>нетривиальное использование шаблонов для полного разделения
программирования и дизайна;
<LI>класс для работы с шаблонами;
<LI>регистрация файлов;
<LI>регистрация переменных;
<LI>подстановка значений переменных в файл;
<LI>вывод файла в браузере;
<LI>недостатки шаблонов;
<LI>адресная книга, расширяющая стандартный класс шаблона за счет
применения запросов SQL. </LI></UL>
<P>В следующей главе мы продолжим знакомство с разработкой динамических
web-приложений. Вы узнаете, как при помощи cookie и отслеживания сеансовых
данных наделить ваш web-сайт новыми интерактивными
возможностями.</P></LI></DIV>
<center>
[ <a href="11.php">Назад</a> | <a href="index.php">Содержание</a> | <a href="13.php">Вперед</a> ]
</center><br>
<?php
echo gb.'<a href="../../ycheb">Учебники</a>'.div;
echo gb.'<a href="'.H.'enter">Прихожая</a>'.div;
foot(); ?>