На главную Назад
Добро пожаловать, уважаемый посетитель!

 

ГЛАВА 12

Шаблоны

Шаблоныможно рассматривать как <расширение>программного кода. Шаблоны не толькоавтоматизируют утомительный процесскодирования, но и обеспечивают структурноеделение проекта в рабочих группах. Рольтакого деления возрастает с увеличениемобъемов проекта и численности групп, атакже с усложнением архитектуры проекта,причем не только на стадиипрограммирования, но и при последующемсопровождении программы.

Сказанноестоит пояснить на конкретном примере.Допустим, у нас имеется командаразработчиков, состоящая из web-дизайнеров ипрограммистов. В идеале группа web-дизайнеровтрудится над созданием привлекательного иудобного сайта, а группа программистов вэто время работает над эффективностью иширотой возможностей web-приложения. Ксчастью, шаблоны заметно упрощают подобноеструктурирование процесса. Настоящая главапосвящена созданию системы шаблонов,обеспечивающих подобное <разделение труда>.

О чемговорилось выше

Донастоящего момента я упоминал о двух разныхподходах к созданию шаблонов РНР:

  • внедрениеHTML в код РНР;
  • включениефайлов в страницу.

Хотя перваясхема более понятна и проще реализуется,она также в большей степени ограничиваетвашу свободу действий. Главная проблемазаключается в том, что код РНР смешивается скомпонентами HTML, образующими макетстраницы. Возникающие при этом проблемысвязаны не только с необходимостьюпотенциальной поддержки одновременногодоступа к странице и ее модификации, но и сповышенной вероятностью ошибок принепосредственном просмотре иредактировании страниц.

Вторая схемаво многих ситуациях оказывается гораздоудобнее первой. Тем не менее, хотя структура<заголовок - основная часть - колонтитул>(см. главу 9)

хорошоподходит для структурированияотносительно малых сайтов с четкоопределенным форматом, с увеличениемобъемов и сложности проекта этиограничения проявляются все заметнее.Попытки решения этих проблем привели кразработке новой схемы применения шаблонов,более сложной по сравнению с двумя первыми,но и обладающей существенно большейгибкостью. В этой схеме разделяются дваглавных компонента web-приложения: дизайн ипрограммирование. Подобное делениеобеспечивает возможность параллельнойразработки (web-дизайн и программирование)без необходимости постоянной координациина протяжении всего рабочего цикла. Болеетого, оно позволяет в будущеммодифицировать один компонент, не влияя наработу другого. В следующем разделе япокажу, как устроена одна из таких схем <нетривиальныхшаблонов>. Следует помнить, что эта схемасуществует не только в РНР. Более того, онапоявилась задолго до РНР и в настоящеевремя используется в нескольких языках,включая РНР, Perl и Java Server Pages. To, что описано вэтой главе, - не более чем адаптация этойсхемы применительно к РНР.

Нетривиальнаясистема шаблонов

Какговорилось ранее, главной целью приразработке подобных систем шаблоновявляется фактическое отделение дизайна отфункциональных возможностей. Собственно,эта система и создается для того, чтобыпрограммисты и дизайнеры могли независимотрудиться над своими аспектами приложения, немешая работе другойгруппы.

К счастью,сделать это проще, чем кажется на первыйвзгляд, - при условии, что доначала разработки былопроведено некоторое предварительноепланирование. В листинге 12.1 представленнекий базовый шаблон, созданный на основематериала этой главы.

Листинг12.1.Примершаблона

<html>

<head>

<title>:::::{page_title}:::::</title>

</head>

<body bgcolor="{bg_color}">

Welcome to your defaulthome page. {user_name}!<br>

You have 5 MB and 3email addresses at your disposal.<br>

Have fun!

</body>

</html>

Обратитевнимание на три строки (page_title, bg_color и userjiame),заключенные в фигурные скобки ({ }). Фигурныескобки имеют особый смысл при обработкешаблонов - заключенная в них строкаинтерпретируется как имя переменной,вместо которого подставляется ее значение.Дизайнер строит страницу по своемуусмотрению; все, что от него потребуется, -включать в соответствующие места документаэти ключевые строки. Конечно, программистыи дизайнеры должны заранее согласоватьимена всех переменных!

Итак, как жеработает эта схема? Прежде всего, возможно,нам придется одновременно работать снесколькими шаблонами, обладающими однимии теми же общими атрибутами. В такихситуациях применение технологии объектно-ориентированногопрограммирования (ООП) оказываетсяособенно эффективным. По этой причине всефункции построения и выполнения операций сшаблонами будут оформлены в виде методовкласса. Определение класса начинается так:

class template {

VAR $files = array( );

VAR $variables = array( );

VAR $openi ng_escape = '{';

VAR $closing_escape ='}';

В массиве$files хранятся идентификаторы файлов исодержимое каждого файла. Атрибут $variablesпредставляет собой двухмерный массив дляхранения файлового идентификатора (ключа) ивсех соответствующих переменных,обрабатываемых в схеме шаблонов. Наконец,атрибуты $opening_escape и $closing_escape задаютограничители для частей шаблона, которыедолжны заменяться системой. Как былопоказано в листинге 12.1, в наших примерах вкачестве ограничителей будутиспользоваться фигурные скобки ({ }). Впрочем,вы можете изменить два последних атрибута ивыбрать ограничители по своему усмотрению.Главное - проследите за тем, чтобы этисимволы не использовались для других целей.

Каждый методкласса решает конкретную задачу,соответствующую той или иной операции впроцессе обработки шаблона. На простейшемуровне этот процесс можно разделить начетыре стадии.

  • Регистрацияфайлов -регистрация всех файлов, обрабатываемыхсценариями шаблонов.
  • Регистрацияпеременных -регистрация всех переменных, которыедолжны заменятьсясвоими значениями в зарегистрированныхфайлах.
  • Обработкафайлов -замена всех переменных, находящихся междуограничителями, в зарегистрированныхфайлах.
  • Выводфайла -вывод обработанных зарегистрированныхфайлов в браузере.

Применениеконцепций ООП в РНР рассматривалось в главе6. Если вы не знакомы с ООП, я рекомендуюбегло просмотреть главу 6 перед тем, какчитать дальше.

Регистрацияфайлов

Впроцессе регистрации содержимоефайла сохраняется в массиве с ключом,однозначно идентифицирующим этот файл.Метод register_file( ) открывает и читаетсодержимое файла, имя которого передается вкачестве параметра. Код этого методаприведен в листинге 12.2.

Листинг12.2. Методрегистрации файла

function register_file($file_id,$file_name) {

// Открыть$file_name для чтения или завершить программу

// с выдачейсообщения об ошибке.

$fh = fopen($file_name,"r") or die("Couldn't open $file_name!");

// Прочитатьвсе содержимое файла $file_name в переменную.

$file_contents = fread($fh, filesize($file_name));

// Присвоитьсодержимое элементу массива

// с ключом $file_id.$this->files[$file_id] = $file_contents;

// Работас файлом завершена, закрыть его.

fclose($fh);

}

Параметр$file_id содержит идентификатор - <псевдоним>для последующих операций с файлом,упрощающий последующие вызовы метода.Идентификатор используется в качествеключа для индексирования массива $files.Пример регистрации файла:

// Включитькласс шаблона

include("tempiate.class"):

// Создатьновый экземпляр класса

$template = new template:

//Зарегистрировать файл "homepage.html",

//присвоив ему псевдоним "home"

$template->register_file("home","homepage.html");

Регистрацияпеременных

Послерегистрации файлов необходимозарегистрировать все переменные, которыебудут интерпретироваться особым образом.Метод register_variables( ) (листинг 12.3) работает потому же принципу, что и register_file( ), - ончитает имена переменных и сохраняет их вмассиве $variables.

Листинг12.3. Методрегистрации переменнных

functionregister_vanables($file_id, $variable_name) {

// Попытатьсясоздать массив,

// содержащийпереданные имена переменных

$input_variables -explode(".", $variable_name);

// Перебратьимена переменных

while (Iist($value) =each($input_variables)) :

// Присвоитьзначение очередному элементу массива

$this->variables$this->variables[$file_id][] = $value:

endwhile;

}

Впараметре $file_id передается ранее присвоенныйпсевдоним файла. Например,в предыдущем примере файлу homepage.html былприсвоен псевдоним home. Обратите внимание -при регистрации имен переменных, которыедолжны особым образом обрабатываться вфайле homepage.html, вы должны ссылаться на файлпо псевдониму! В параметре $variable_nameпередаются имена одной или несколькихпеременных, регистрируемых для указанногопсевдонима. Пример:

// Включитькласс шаблона include("tempiate.class");

// Создатьновый экземпляр класса $template = new template;

//Зарегистрировать файл "homepage.html",

//присвоив ему псевдоним "home" $template->register_file("home","homepage.html");

//Зарегистрировать несколько переменных

$template->register_variablest"home","page_title.bg_color,user_name");

Обработкафайла

После тогокак файлы и переменные будутзарегистрированы в системе шаблонов, можнопереходить к обработке зарегистрированныхфайлов и замене всех ссылок на переменные ссоответствующими значениями. Метод file_parser( )приведен в листинге 12.4.

Листинг12.4. Методобработки файла

function file_parser($file_id){

// Сколькопеременных зарегистрировано для данногофайла?

$varcount = count($this->variables[$file_id]);

// Сколькофайлов зарегистрировано?

$keys = array_keys($this->files):

// Если файл$file_id существует в массиве

$this->files

// и с нимсвязаны зарегистрированные переменные

If ( (in_array($file_id.$keys)) && ($varcount > 0) ) :

// Сбросить $x$x = 0:

// Покаостаются переменные для обработки...

while ($x< sizeof($this->variables[$file_id])) :

// Получитьимя очередной переменной $string = $this->variables[$file_id][$x];

// Получитьзначение переменной. Обратите внимание:

//для получения значения используетсяконструкция $$.

// Полученное значениеподставляется в файл вместо

// указанногоимени переменной.GLOBAL $$string:

// Построитьточный текст замены вместе сограничителями

$needle = $this->opening_escape.$string.$this->closing_escape;

// Выполнитьзамену.

$this->files[$file_id] = str_replace( $needle.

$$string.

$this->files[$file_id]);

// Увеличить $х $x++;

endwhile;

endif;

}

Сначала мыпроверяем, присутствует ли указанное имяфайла в массиве $this->files. Если файл былзарегистрирован, мы также проверяем, былили для него зарегистрированы переменные, иесли были - значения этих переменныхподставляются в содержимое $file_id. Пример:

// Включитькласс шаблона include("template. class") ;

$page_title = "Welcometo your homepage!";

$bg_color = "white"; $user_name = "ChefJacques";

// Создатьновый экземпляр класса

$template = new template;

//Зарегистрировать файл "homepage.html",

II присвоивему псевдоним "home"

$template->register_file("home", "homepage.html");

//Зарегистрировать несолько переменных

$template->register_variables("home","page_titie, bg_color, user_name");

$template->file_parser("home");

Посколькупеременные page_title, bg_color и user_name былизарегистрированы, значения каждойпеременной (присвоенные в начале сценария)подставляются в страницу homepage.html,хранящуюся в массиве files (атрибуте объекта-шаблона).На этом предварительная подготовказавершается, остается лишь вывестиполученный шаблон в браузере. Эта операциярассматривается в следующем разделе.

Вывод файла

Вероятно,после обработки файла вы захотитеотправить его в браузер, чтобы пользовательувидел результат обработки шаблона. В нашемпримере для вывода

файласоздается отдельный метод, приведенный влистинге 12.5, однако в зависимости отситуации вывод также может интегрироватьсяс методом f i I e_parser().

Листинг12.5. Методвывода файла в браузере

function pnnt_file($file_id){

// Вывестисодержимое файла с идентификатором

$file_id print$this->files[$file id];

}

Все оченьпросто - при вызове print_file( ) содержимоефайла, представленного ключом $file_id,передается в браузер.

Влистинге 12.6 приведен пример использованиякласса template.

Листинг 12.6. Примериспользования класса template

// Включитькласс шаблона, include("tempiate.class");

// Присвоитьзначения переменным

$page_title = "Welcome to yourhomepage!";

$bg_color = "white"; $user_name = "Chef Jacques":

// Создатьновый экземпляр класса $template= new template;

//Зарегистрировать файл "homepage.html" спсевдонимом "home"

$template->register_file("home","homepage.html");

//Зарегистрировать переменные

$template->register_variables("home","page_title, bg_color.user_name");

$template->file_parser("home");

// Передатьрезультат в браузер

$template->print_file("home");

Если бышаблон, приведенный в листинге 12.1, хранилсяв файле homepage.html в одном каталоге сосценарием из листинга 12.6, то в браузер былбы направлен следующий код HTML:

<html>

<head>

<title>:::::Welcometo your homepage!:::::</title>

</head>

<body bgcolor=white>

Welcome to your defaulthome page, Chef Jacques!<br>

You have 5 MB and 3email addresses at your disposal.<br>

Have fun!

</body>

</html>

Как видно изприведенного примера, всезарегистрированные переменные былизаменены соответствующими значениями. Привсей своей простоте класс tempi ate

обеспечиваетстопроцентное разделение уровнейпрограммирования и дизайна. Полный кодкласса template приведен в листинге 12.7.

Листинг12.7. Полныйкод класса template

class template {

VAR $files = array( );

VAR $variables = array( );

VAR $opening_escape = '{';

VAR $closing_escape = '}';

// Функция:register_file( )

// Назначение:сохранение в массиве содержимого файла.

//определяемого идентификатором $file_id

function register_file($file_id.$file_name) {

// Открыть$file_name для чтения или завершить программу

// с выдачейсообщения об ошибке.

$fh = fopen($file_name,"r") or die("Couldn't open $file_name!");

// Прочитатьвсе содержимое файла $file_name в переменную.

$file_contents = fread($fh, filesize($file_name));

// Присвоитьсодержимое элементу массива

// с ключом $file_id.$this->files[$file_id] = $file_contents;

// Работа сфайлом завершена, закрыть его.

fclose($fh):

} //Функция: register_variables( )

// Назначение:сохранение переменных, переданных

// впараметре $variable_name. в массиве с ключом $file_id.

function register_variables($file_id,$variable_name) {

// Попытатьсясоздать массив.

// содержащийпереданные имена переменных

$input_variables =explode(".", $vahable_name);

// Перебратьимена переменных

while (list(, $value) =each($input_variables)) :

// Присвоитьзначение очередному элементу массива $this->variables$this->variables[$file_id][] = $value:

endwhile;

} //Функция: file_parser( )

// Назначение:замена всех зарегистрированных переменных

// в файле с идентификатором $file_id

function file_parser($file_id){

// Сколькопеременных зарегистрировано для данногофайла?

$varcount = count($this->variables[$file_id]):

// Сколькофайлов зарегистрировано?

$keys = array_keys($this->files):

// Если файл$file_id существует в массиве $this->files

// и с нимсвязаны зарегистрированные переменные

if ( (in_array($file_id.$keys)) && ($varcount > 0) ) :

// Сбросить $х$x - 0;

// Покаостаются переменные для обработки...

while ($x< sizeof($this->variables[$file_id])) :

// Получитьимя очередной переменной

$string = $this->variables[$file_id][$x];

// Получитьзначение переменной. Обратите внимание:

//для получения значения используетсяконструкция $$.

// Полученное значениеподставляется в файл вместо

// указанногоимени переменной.

GLOBAL $$string;

// Построитьточный текст замены вместе сограничителями

$needle = $this->opemng_escape.$string.$this->closing_escape;

// Выполнитьзамену.

$this->files[$file_id] = str_replace( $needle, $$string,

$this->files[$file_idj);

// Увеличить $х $x++;

endwhile;

endif;

}

// Функция:print_file()

// Назначение:вывод содержимого файла,

//определяемого параметром $file_id

function print_file($file_id){

// Вывестисодержимое файла с идентификатором $file_id

print $this->files[$file_id];

}

} //END template.class

Расширениякласса template

Конечно,класс tempi ate обладает весьма ограниченнымивозможностями, хотя для проектов,создаваемых на скорую руку, он вполнеподходит. Объектно-ориентированныесхемы хороши тем, что они позволяют легконаращивать функциональность, не беспокоясьо возможных нарушениях работысуществующего кода. Допустим, вы решилисоздать новый метод, который будетзагружать значения для последующей заменыиз базы данных. Хотя такой метод устроенчуть сложнее, чем метод file_parser( ),производящий простую замену глобальныхпеременных, его реализация на базе SQLсостоит из нескольких строк и легкоинкапсулируется в отдельном методе. Болеетого, мы создадим нечто подобное в проектеадресной книги, завершающем эту главу.

В класс tempi ateможно внести несколько очевидныхусовершенствований. Первое - объединениефункций register_file( ) и register_variables( ),обеспечивающее автоматическую регистрациюпеременных для каждого регистрируемогофайла. Конечно, при этом также необходимореализовать проверку ошибок, чтобыпредотвратить регистрацию неверных файлови переменных.

Однако наэтом возможности усовершенствованиядалеко не исчерпаны. Подумайте, как бы выреализовали методы, работающие с целымимассивами? На самом деле это проще, чемкажется на первый взгляд. Проанализируйтерешение, использованное в проекте адреснойкниги в конце главы. Общие принципы легкотрансформируются под любую конкретнуюреализацию.

Общие схемыработы с шаблонами были реализованы нанескольких языках и ни в коем случае неявляются чем-то принципиально новым. В Webможно найти немало информации о реализациишаблонов. Рекомендую два особенноинтересных ресурса - сборники статей,написанных с ориентацией на JavaScript:

В следующейстатье затронута тема использованияшаблонов применительно к Java Server Pages:

Кроме того,описанная схема построения шаблоновиспользуется в нескольких библиотеках РНР,среди которых наибольший интереспредставляют следующие:

На сайтересурсов РНР, PHPBuilder (http://www.phpbuilder.com), такжеимеется несколько интересных учебников,посвященных обработке шаблонов. Кроме того,загляните на сайт РНР Classes Repository (http://phpclasses.UpperDesign.com),здесь также можно найти несколькореализаций.

Недостаткисистемы шаблонов

Хотярассмотренная система шаблоновсправляется со своей главной задачей -полным разделением дизайна ипрограммирования, она не лишенанедостатков. Некоторые из этих недостатковперечислены ниже.

Необоснованныенадежды на <идеальное решение>

Шаблоныпомогают четко выделить в проекте аспектыпрограммирования и дизайна, но они незаменяют нормального взаимодействия междуэтими аспектами. Более того, правильностьих работы зависит от предварительногосогласования списка переменных, заменяемыхв процессе обработки шаблона. Как и в любомуспешном проекте, переходить к написаниюкода РНР следует лишь после тщательнойпроработки спецификации всего приложения.Это значительно уменьшает вероятностьошибок при последующей обработке,приводящих к непредвиденным последствиямпри использовании шаблонов.

Снижениебыстродействия

Затраты наобработку файлов приводят к некоторомузамедлению работы программы. В какой мерезамедляется работа, зависит от рядафакторов, в том числе от размера страницы,размера запроса SQL (если они задействован) иаппаратной конфигурации компьютера. Какправило, эти потери настолько малы, что имиможно пренебречь, но в некоторых ситуацияхони оказываются довольно значительными (например,при одновременной обработке несколькихшаблонов в условиях высокого трафика).

Ориентациядизайна на РНР

Одна изглавных целей создания шаблоновзаключается в том, чтобы по возможностиизолировать дизайнера от программного кодапри редактировании внешнего вида иповедения страницы. В идеальном случаедизайнер должен обладать некоторыминавыками программирования или, по крайнеймере, быть знакомым с общими концепциями -переменными, циклами и условными командами.Дизайнеру, абсолютно не разбирающемуся вних, применение шаблонов практическиничего не даст, кроме относительнобесполезных сведений из области синтаксиса.В общем, независимо от того, захотите выпользоваться этим типом шаблонов или нет, янастоятельно рекомендую потратить немноговремени и обучить дизайнера азам языка РНР...а еще лучше - купить ему эту книгу! От этоговыиграют обе стороны, поскольку дизайнерприобретет дополнительные навыки и станетболее ценным членом рабочей группы, а упрограммиста появится новый источник идей.Может, дизайнер и не изобретет ничеговыдающегося, но зато он взглянет наситуацию под новым углом зрения, обычнонедоступным для программиста.

Проект:адресная книга

Хотя системышаблонов хорошо подходят для многих типовweb-приложений, они приносят особеннуюпользу в приложениях, ориентированных навыборку и вывод данных, в которых особенноважно обеспечить правильноеформатирование.

Примеромтакого приложения является адресная книга.Представьте себе обычную (бумажную)адресную книгу: все страницы выглядятпрактически одинаково, различаются развечто буквы, с которых начинаются имена наконкретной странице. Аналогичный подходможно применить и к адресной книге на базеWeb. Форматирование в данном случае играетеще более важную роль, поскольку неисключено, что данные придетсяэкспортировать в другое приложение в каком-нибудьспецифическом формате. Подобные приложенияпрекрасно работают на базе шаблонов,поскольку дизайнеру остается лишь создатьединый формат страницы, который будетиспользоваться для всех 26 букв алфавита.

Прежде всего,необходимо решить, какие данные и в какомформате будут храниться в адресной книге.Конечно, оптимальным носителем информациив данном случае является база данных,поскольку это упростит такие полезныеоперации, как поиск и сортировка данных. Всвоем примере я воспользуюсь СУБД MySQL.Определение таблицы выглядит следующимобразом:

mysql>CREATE tableaddressbook (

last_name char(35) NOT NULL,

first_name char(20) MOTNULL,

telchar(20) NOT NULL,

email char(55) NOT NULL );

Разумеется,вы можете самостоятельно добавить поля дляхранения адреса, города и т. д. Длянаглядности я буду использоватьсокращенную таблицу, приведенную ранее.

Теперь явозьму на себя роль дизайнера и займусьсозданием шаблонов. Для этого проекта нужныдва шаблона. Код первого, <родительского>шаблона book.html приведен в листинге 12.8.

Листинг12.8. Основнойшаблон адресной книги book.html

<html>

<head>

<title>:::::{page_title}:::::</title>

</head>

<body bgcolor="white">

<table cellpadding=2cellspacing=2 width=600>

<h1>Address Book: {letter}</h1> <tr><td>

<a href="index.php?letter=a">A</a>| 

<a href="index.php?letter=b">B</a>| 

<a href="index.php?letter=c">C</a>| 

<a href="index.php?letter=d">D</a> | 

<a href="index.php?letter=e">E</a>| 

<a href="index.php?letter=f">F</a> | 

<a href="index,php?letter=g">G</a>| 

<a href="index.php?letter=h">H</a> | 

<a href="index.php?letter=i">I</a>| 

<a href="index.php?letter=j">J</a> | 

<a href="index.php?letter=k">K</a>| 

<a href="index.php?letter=l">L</a> | 

<a href="index.php?letter=m">M</a>| 

<a href="index.php?letter=n">N</a>| 

<a href="index.php?letter=o">O</a>| 

<a href="index.php?letter=p">P</a> | 

<a href="index.php?letter=q">Q</a>| 

<a href="index.php?letter=r">R</a>| 

<a href="index.php?letter=s">S</a>| 

<a href="index.php?letter=t">T</a> | 

<a href="index.php?letter=u">U</a>| 

<a href="index.php?letter=v">V</a> | 

<a href="index.php?letter=w">W</a>| 

<a href="index.php?letter=x">X</a> | 

<a href="index.php?letter=y">Y</a>| 

<a href="index.php?letter=z">Z</a>

</td></tr>

{rows.addresses}

</table>

</body>

</html>

Как видите,файл в основном состоит из ссылок с разнымибуквами алфавита. Если щелкнуть на букве, вбраузере отображается информация обо всехконтактах в адресной книге, фамилии которыхначинаются с указанной буквы.

В страницевстречаются три имени переменных,заключенных в ограничители: page_title, letter иrows_addresses. Смысл первых двух переменныхочевиден: текст в заголовке страницы ибуква адресной книги, использованная длявыборки текущих адресных данных. Третьяпеременная относится к дополнительномушаблону (листинг 12.9) и определяет файлконфигурации таблицы, включаемый восновной шаблон. Файлы конфигурации таблициспользуются в связи с тем, что в сложныхстраницах может быть одновременнозадействовано несколько шаблонов, в каждомиз которых данные форматируются в видетаблиц HTML. Шаблон rows.addresses (листинг 12.9)выполняет вспомогательные функции ивставляется в основной шаблон book.html. Вскоревы поймете, почему это необходимо.

Листинг12.9.Вспомогательныйшаблон rows.addresses

<tr><td bgcolor="#c0c0c0">

<b>{last_name},{first_name}</b>

</td></tr>

<tr><td>

<b>{telephone}</b>

</td></tr>

<tr><td>

<b><a href ="mailto:{email}">{email}</a></b>

</td></tr>

В листинге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многократно встречается в странице.

Листинг12.10.Исходныйтекст страницы, изображенной на рис. 12.1

<html>

<head>

<title>:::::AddressBook:::::</title>

</head>

<body bgcolor="white">

<table cellpadd1ng=2cellspacing=2 width=600>

<hl>Address Book:f</hl>

<tr><td>

<a href="index.php?letter=a">A</a>| 

<a href="index.php?letter=b">B</a>| 

<a href="index.php?letter=c">C</a>| 

<a href="index.php?letter=d">D</a>| 

<a href="index.php?letter=e">E</a>| 

<a href="index.php?letter=f">F</a>| 

<a href="index.php?letter=g">G</a>| 

<a href="index.php?letter=h">H</a>| 

<a href="index.php?letter=i">I</a>| 

<a href="index.php?letter=j">J</a>| 

<a href="index.php?letter=k">K</a>| 

<a href="index.php?letter=l">L</a>| 

<a href="index.php?letter=m">M</a>| 

<a href="index.php?1etter=n">N</a>| 

<a href="index.php?letter=o">0</a>| 

<a href="index.php?letter=p">P</a>| 

<a href="index.php?letter=q">Q</a>| 

<a href="index.php?letter=r">R</a>| 

<a href="index.php?letter=s">S</a>| 

<a href="index.php?letter=t">T</a>| 

<a href="index.php?letter=u">U</a>| 

<a href="index.php?letter=v">V</a>| 

<a href="index.php?letter=w">W</a>| 

<a href="index.php?letter=x">X</a>| 

<a href="index.php?letter=y">Y</a>| 

<a href="index.php?letter=z">Z</a>

</td></tr>

<tr><t

bgcolor="#c0c0c0">

<b>Fries.Bobby</b>

</td></tr>

<tr><td>

<b>(212)563-5678</b>

</td></tr>

<tr><td>"

<b>

<a href="mailto:bobby@fries.com">bobby@fries.com</a>

</b>

</td></tr>

<tr><tdbgcolor="#c0c0c0">

<b>Frenchy.Pierre</b>

</td></tr>

<tr><td>

<b>002-(30)-09-7654321</b>

</td></tr>

<tr><td>

<b><a href ="mailto:frenchy@frenchtv.com">
frenchy@frenchtv.com</a></b>

</td></tr>

</table>

</body>

</html>

Как видно изприведенного листинга, в адресной книгехранятся записи двух лиц, фамилии которыхначинаются с буквы F: Bobby Fries и Pierre Frenchy.Соответственно в таблицу вставляютсяданные двух записей.

Дизайнерскаячасть проекта адресной книги завершена, и яперехожу к роли программиста. Возможно, васудивит тот факт, что класс tempiate. class (см.листинг 12.7) практически не изменился, еслине считать появления одного нового метода- address_sql( ). Код этого метода приведен влистинге 12.11.

Листинг12.11. Обработкаданных, полученных в результате запроса

class template {

VAR $files = array( );

VAR $variab!es = array( ):

VAR $sql = array();

VAR $opening_escape -'{';

VAR $closing_escape ='}';

VAR $host = "localhost";

VAR $user = "root";

VAR $pswd = "";

VAR $db = "book";

VAR $address table ="addressbook";

function address_sql($file_id,$vanable_name, $letter) {

//Подключиться к серверу MySQL и выбрать базуданных 

mysql_connect($this->host, $this->user, $this->pswd)

ordie("Couldn't connect to MySQL server!");

mysql_select_db($this->db)or die('Couldn't select MySQL database!");

// Обратитьсяс запросом к базе данных

$query = "SELECTlast_name, first_name, tel, email

FROM $this->address_tableWHERE lastjiame LIKE '$letter%' ";

$result = mysql_query($query);

// Открытьфайл "rows.addresses"

// ипрочитать его содержимое в переменную

$fh - fopen("$variable_name","r");

$file_contents = fread($fh,filesize("rows.addresses") ):

// Заменитьимена переменных в ограничителях

// данными избазы.

while ($row =mysql_fetch_array($result)) :

$new_row = $file_contents;

$new_row=str_replace($this->opening_escape.
"last_name".$this->closing_escape. 

$row["last_name"].$new_row);

$new_row=

str_replace($th1s->opening_escape.
"first_name".$this->closing_escape.

$row["first_name"], $new_row);

$new_row=str_replace($this->opening_escape.
"telephone".$this->closing_escape.

 $row["tel"],$new_row);

$new_row = str_replace($this->opening_escape.
"email".$this->closing_escape,

 $row["email"],

$new_row);

//Присоединить запись к итоговой строкезамены

$complete_table .= $new_row;

endwhile;

$sql_array_key = $variable_name;

$this->sql[$sql_array_key] = $complete_table;

// Включитьключ в массив variables для последующего поиска

$this->variables[$file_id][ ] = $variable_name;

// Закрытьфайловый манипулятор fclose(lfh);

Комментариев,приведенных в листинге 12.11, вполнедостаточно для того, чтобы вы разобрались впроисходящем, однако я должен сделатьнесколько важных замечаний. Во-первых,обратите внимание на то, что файл rows.addressesоткрывается только одинраз. Возможен и другойвариант - многократно открывать изакрывать файл rows.addresses, каждый разпроизводя замену и присоединяя егосодержимое к переменной $complete_table. Впрочем,такое решение будет крайне неэффективным.Потратьте немного времени и разберитесь втом, как новые данные таблицы в циклеприсоединяются к переменной $complete_table.

Второе, начто следует обратить внимание припросмотре листинга 12.11, - появление пятиновых атрибутов класса: $host, $user, $pswd, $db и$address_table. В этих атрибутах хранитсяинформация, необходимая для сервера SQL.Полагаю, смысл каждого атрибута понятен безобъяснений, а если нет - вернитесь иповторите материал главы 11.

 

Рис. 12.1. Страницаадресной книги

Все, чтоосталось сделать - написать файл index.php,инициирующий обработку шаблонов, Код этогофайла приведен в листинге 12.12. Если щелкнутьна одной из ссылок (index.php?letter=буква) настранице book.html (см. листинг 12.8), загружаетсястраница index.php, которая, в свою очередь,заново строит book.html с включением новойинформации.

Листинг12.12.Обработчикшаблонов index.php

include("Listing12-11.php");$page_title = "Address Book";

// Поумолчанию загружается страница с фамилиями,

// начинающимися с буквы 'а' if (! isset($letter) ) :

$letter = "а";

endif ;

$tpl = new template;

$tpl->register_file("book","book.html");

$tpl->register_variables("book", "page_title.letter");

$tpl ->address_sql("book", "rows.addresses", "$letter");

$tpl ->file_parser("book");

$tpl->phnt_fil("book");

Перед вамипрактический пример, показывающий, как припомощи шаблонов организовать эффективноеразделение труда между программистом идизайнером. Подумайте, как бы выиспользовали шаблоны для организации своихразработок. Готов поспорить, что вы найдетеим полезное применение.

Итоги

В этой главебыла представлена концепция, особенноважная как для РНР, так и для web-программированияв целом, - применение шаблонов. Главаначалась с обзора двух схем; упоминавшихсяранее, - простой замены переменныхсредствами РНР и логическим делениемстраницы при помощи включаемых файлов.Затем мы познакомились с третьей схемойприменения шаблонов, позволяющей полностьюотделить программирование от дизайнастраницы. Оставшаяся часть главы былапосвящена анализу класса, построенного дляреализации шаблонов такого рода. Главузавершает пример практическогоиспользования шаблонов в адресной книге набазе Web. В частности, в этой главерассматривались следующие темы:

  • для чегонужны шаблоны;
  • простойшаблон № 1: внедрение РНР в HTML;
  • простойшаблон № 2: разделение компонентовстраницы при помощи включаемых файлов;
  • нетривиальноеиспользование шаблонов для полногоразделения программирования и дизайна;
  • класс дляработы с шаблонами;
  • регистрацияфайлов;
  • регистрацияпеременных;
  • подстановказначений переменных в файл;
  • выводфайла в браузере;
  • недостаткишаблонов;
  • адреснаякнига, расширяющая стандартный классшаблона за счет применения запросов SQL.

В следующейглаве мы продолжим знакомство сразработкой динамических web-приложений. Выузнаете, как при помощи cookie и отслеживаниясеансовых данных наделить ваш web-сайтновыми интерактивными возможностями.