Вход Регистрация
Файл: work/php_teach/12.php
Строк: 361
<?
require '../../config.php';
$title 'Учебник PHP';
include 
'../../style/head.php';
aut();
who_add(0'servise');
?>
<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>&nbsp;</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>&lt;html&gt;</P>
      <P>&lt;head&gt;</P>
      <P>&lt;title&gt;:::::{page_title}:::::&lt;/title&gt;</P>
      <P>&lt;/head&gt;</P>
      <P>&lt;body bgcolor="{bg_color}"&gt;</P>
      <P>Welcome to your default home page. {user_name}!&lt;br&gt;</P>
      <P>You have 5 MB and 3 email addresses at your disposal.&lt;br&gt;</P>
      <P>Have fun!</P>
      <P>&lt;/body&gt;</P>
      <P>&lt;/html&gt;</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-&gt;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-&gt;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-&gt;variables $this-&gt;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-&gt;register_file("home",
      "homepage.html");</P>
      <P>// Зарегистрировать несколько переменных</P>
      <P>$template-&gt;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-&gt;variables[$file_id]);</P>
      <P>// Сколько файлов зарегистрировано?</P>
      <P>$keys = array_keys($this-&gt;files):</P>
      <P>// Если файл $file_id существует в массиве</P>
      <P>$this-&gt;files</P>
      <P>// и с ним связаны зарегистрированные переменные</P>
      <P>If ( (in_array($file_id. $keys)) &amp;&amp; ($varcount &gt; 0) ) :</P>
      <P>// Сбросить $x $x = 0:</P>
      <P>// Пока остаются переменные для обработки...</P>
      <P>while ($x &lt; sizeof($this-&gt;variables[$file_id])) :</P>
      <P>// Получить имя очередной переменной $string =
      $this-&gt;variables[$file_id][$x];</P>
      <P>// Получить значение переменной. Обратите внимание:</P>
      <P>// для получения значения используется конструкция $$.</P>
      <P>// Полученное значение подставляется в файл вместо</P>
      <P>// указанного имени переменной.GLOBAL $$string:</P>
      <P>// Построить точный текст замены вместе с ограничителями</P>
      <P>$needle =
$this-&gt;opening_escape.$string.$this-&gt;closing_escape;</P>
      <P>// Выполнить замену.</P>
      <P>$this-&gt;files[$file_id] = str_replace( $needle.</P>
      <P>$$string.</P>
      <P>$this-&gt;files[$file_id]);</P>
      <P>// Увеличить $х $x++;</P>
      <P>endwhile;</P>
      <P>endif;</P>
      <P>}</P>
      <P>Сначала мы проверяем, присутствует ли указанное имя файла в массиве
      $this-&gt;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-&gt;register_file( "home", "homepage.html");</P>
      <P>// Зарегистрировать несолько переменных</P>
      <P>$template-&gt;register_variables("home", "page_titie, bg_color,
      user_name");</P>
      <P>$template-&gt;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-&gt;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-&gt;register_file("home", "homepage.html");</P>
      <P>// Зарегистрировать переменные</P>
      <P>$template-&gt;register_variables("home", "page_title,
      bg_color.user_name");</P>
      <P>$template-&gt;file_parser("home");</P>
      <P>// Передать результат в браузер</P>
      <P>$template-&gt;print_file("home");</P>
      <P>Если бы шаблон, приведенный в листинге 12.1, хранился в файле
      homepage.html в одном каталоге со сценарием из листинга 12.6, то в браузер
      был бы направлен следующий код HTML:</P>
      <P>&lt;html&gt;</P>
      <P>&lt;head&gt;</P>
      <P>&lt;title&gt;:::::Welcome to your homepage!:::::&lt;/title&gt;</P>
      <P>&lt;/head&gt;</P>
      <P>&lt;body bgcolor=white&gt;</P>
      <P>Welcome to your default home page, Chef Jacques!&lt;br&gt;</P>
      <P>You have 5 MB and 3 email addresses at your disposal.&lt;br&gt;</P>
      <P>Have fun!</P>
      <P>&lt;/body&gt;</P>
      <P>&lt;/html&gt;</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-&gt;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-&gt;variables
      $this-&gt;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-&gt;variables[$file_id]):</P>
      <P>// Сколько файлов зарегистрировано?</P>
      <P>$keys = array_keys($this-&gt;files):</P>
      <P>// Если файл $file_id существует в массиве $this-&gt;files</P>
      <P>// и с ним связаны зарегистрированные переменные</P>
      <P>if ( (in_array($file_id. $keys)) &amp;&amp; ($varcount &gt; 0) ) :</P>
      <P>// Сбросить $х $x - 0;</P>
      <P>// Пока остаются переменные для обработки...</P>
      <P>while ($x &lt; sizeof($this-&gt;variables[$file_id])) :</P>
      <P>// Получить имя очередной переменной</P>
      <P>$string = $this-&gt;variables[$file_id][$x];</P>
      <P>// Получить значение переменной. Обратите внимание:</P>
      <P>// для получения значения используется конструкция $$.</P>
      <P>// Полученное значение подставляется в файл вместо</P>
      <P>// указанного имени переменной.</P>
      <P>GLOBAL $$string;</P>
      <P>// Построить точный текст замены вместе с ограничителями</P>
      <P>$needle = $this-&gt;opemng_escape.$string.$this-&gt;closing_escape;</P>
      <P>// Выполнить замену.</P>
      <P>$this-&gt;files[$file_id] = str_replace( $needle, $$string,</P>
      <P>$this-&gt;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-&gt;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&gt;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>&lt;html&gt;</P>
      <P>&lt;head&gt;</P>
      <P>&lt;title&gt;:::::{page_title}:::::&lt;/title&gt;</P>
      <P>&lt;/head&gt;</P>
      <P>&lt;body bgcolor="white"&gt;</P>
      <P>&lt;table cellpadding=2 cellspacing=2 width=600&gt;</P>
      <P>&lt;h1&gt;Address Book: {letter}&lt;/h1&gt; &lt;tr&gt;&lt;td&gt;</P>
      <P>&lt;a href="index.html.php?letter=a"&gt;A&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=b"&gt;B&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=c"&gt;C&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=d"&gt;D&lt;/a&gt;&nbsp;|&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=e"&gt;E&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=f"&gt;F&lt;/a&gt;&nbsp;|&nbsp;</P>
      <P>&lt;a href="index.html,php?letter=g"&gt;G&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=h"&gt;H&lt;/a&gt;&nbsp;|&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=i"&gt;I&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=j"&gt;J&lt;/a&gt;&nbsp;|&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=k"&gt;K&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=l"&gt;L&lt;/a&gt;&nbsp;|&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=m"&gt;M&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=n"&gt;N&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=o"&gt;O&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=p"&gt;P&lt;/a&gt;&nbsp;|&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=q"&gt;Q&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=r"&gt;R&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=s"&gt;S&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=t"&gt;T&lt;/a&gt;&nbsp;|&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=u"&gt;U&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=v"&gt;V&lt;/a&gt;&nbsp;|&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=w"&gt;W&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=x"&gt;X&lt;/a&gt;&nbsp;|&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=y"&gt;Y&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=z"&gt;Z&lt;/a&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>{rows.addresses}</P>
      <P>&lt;/table&gt;</P>
      <P>&lt;/body&gt;</P>
      <P>&lt;/html&gt;</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>&lt;tr&gt;&lt;td bgcolor="#c0c0c0"&gt;</P>
      <P>&lt;b&gt;{last_name},{first_name}&lt;/b&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>&lt;tr&gt;&lt;td&gt;</P>
      <P>&lt;b&gt;{telephone}&lt;/b&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>&lt;tr&gt;&lt;td&gt;</P>
      <P>&lt;b&gt;&lt;a href =
      "mailto:{email}"&gt;{email}&lt;/a&gt;&lt;/b&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>В листинге 12.9 встречаются четыре переменных, заключенных в
      ограничители: last_name, first_name, telephone и emal. Смысл этих
      переменных очевиден (см. определение таблицы addressbook). Следует
      заметить, что этот файл состоит только из табличных тегов строк
      (&lt;tr&gt;...&lt;/tr&gt;) и ячеек (&lt;td&gt;...&lt;/td&gt;). Дело в том,
      что этот файл вставляется в шаблон многократно, по одному разу для каждого
      адреса, прочитанного из базы данных. Поскольку имя переменной
      rows.addresses в листинге 12.8 включается внутрь тегов
      &lt;table&gt;...&lt;/table&gt;, форматирование HTML будет обработано
      правильно. Чтобы вы лучше поняли, как работает этот шаблон, взгляните на
      рис. 12.1 — на нем изображена копия страницы адресной книги. Затем
      проанализируйте листинг 12.10, содержащий исходный текст этой страницы. Вы
      увидите, что содержимое файла rows.addresses многократно встречается в
      странице.</P>
      <P>Листинг 12.10. Исходный текст страницы, изображенной на рис. 12.1</P>
      <P>&lt;html&gt;</P>
      <P>&lt;head&gt;</P>
      <P>&lt;title&gt;:::::Address Book:::::&lt;/title&gt;</P>
      <P>&lt;/head&gt;</P>
      <P>&lt;body bgcolor="white"&gt;</P>
      <P>&lt;table cellpadd1ng=2 cellspacing=2 width=600&gt;</P>
      <P>&lt;hl&gt;Address Book: f&lt;/hl&gt;</P>
      <P>&lt;tr&gt;&lt;td&gt;</P>
      <P>&lt;a href="index.html.php?letter=a"&gt;A&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=b"&gt;B&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=c"&gt;C&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=d"&gt;D&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=e"&gt;E&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=f"&gt;F&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=g"&gt;G&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=h"&gt;H&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=i"&gt;I&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=j"&gt;J&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=k"&gt;K&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=l"&gt;L&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=m"&gt;M&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?1etter=n"&gt;N&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=o"&gt;0&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=p"&gt;P&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=q"&gt;Q&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=r"&gt;R&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=s"&gt;S&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=t"&gt;T&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=u"&gt;U&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=v"&gt;V&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=w"&gt;W&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=x"&gt;X&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=y"&gt;Y&lt;/a&gt; |&nbsp;</P>
      <P>&lt;a href="index.html.php?letter=z"&gt;Z&lt;/a&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>&lt;tr&gt;&lt;t</P>
      <P>bgcolor="#c0c0c0"&gt;</P>
      <P>&lt;b&gt;Fries.Bobby&lt;/b&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>&lt;tr&gt;&lt;td&gt;</P>
      <P>&lt;b&gt;(212) 563-5678&lt;/b&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>&lt;tr&gt;&lt;td&gt; "</P>
      <P>&lt;b&gt;</P>
      <P>&lt;a href="mailto.html:bobby@fries.com"&gt;bobby@fries.com&lt;/a&gt;</P>
      <P>&lt;/b&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>&lt;tr&gt;&lt;td bgcolor="#c0c0c0"&gt;</P>
      <P>&lt;b&gt;Frenchy.Pierre&lt;/b&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>&lt;tr&gt;&lt;td&gt;</P>
      <P>&lt;b&gt;002-(30)-09-7654321&lt;/b&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>&lt;tr&gt;&lt;td&gt;</P>
      <P>&lt;b&gt;&lt;a href =
      "mailto:frenchy@frenchtv.com"&gt;<BR>frenchy@frenchtv.com&lt;/a&gt;&lt;/b&gt;</P>
      <P>&lt;/td&gt;&lt;/tr&gt;</P>
      <P>&lt;/table&gt;</P>
      <P>&lt;/body&gt;</P>
      <P>&lt;/html&gt;</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 и выбрать базу данных&nbsp;</P>
      <P>mysql_connect($this-&gt;host, $this-&gt;user, $this-&gt;pswd)</P>
      <P>or die("Couldn't connect to MySQL server!");</P>
      <P>mysql_select_db($this-&gt;db) or die('Couldn't select MySQL
      database!");</P>
      <P>// Обратиться с запросом к базе данных</P>
      <P>$query = "SELECT last_name, first_name, tel, email</P>
      <P>FROM $this-&gt;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-&gt;opening_escape.<BR>"last_name".$this-&gt;closing_escape.&nbsp;</P>
      <P>$row["last_name"]. $new_row);</P>
      <P>$new_row=</P>
      <P>str_replace($th1s-&gt;opening_escape.<BR>"first_name".$this-&gt;closing_escape.</P>
      <P>$row["first_name"], $new_row);</P>
      <P>$new_row=str_replace($this-&gt;opening_escape.<BR>"telephone".$this-&gt;closing_escape.</P>
      <P>&nbsp;$row["tel"], $new_row);</P>
      <P>$new_row =
      str_replace($this-&gt;opening_escape.<BR>"email".$this-&gt;closing_escape,</P>
      <P>&nbsp;$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-&gt;sql[$sql_array_key] = $complete_table;</P>
      <P>// Включить ключ в массив variables для последующего поиска</P>
      <P>$this-&gt;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>&nbsp;<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-&gt;register_file("book", "book.html");</P>
      <P>$tpl-&gt;register_variables("book", "page_title.letter");</P>
      <P>$tpl -&gt;address_sql("book", "rows.addresses", "$letter");</P>
      <P>$tpl -&gt;file_parser("book");</P>
      <P>$tpl-&gt;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>
<?
echo '&laquo; <a href="/work/?">Назад</a>';
include 
'../../style/foot.php';
?>
Онлайн: 3
Реклама