Спешный listing php. Самые основы

  • 19.01.2024

Reg.ru: домены и хостинг

Крупнейший регистратор и хостинг-провайдер в России.

Более 2 миллионов доменных имен на обслуживании.

Продвижение, почта для домена, решения для бизнеса.

Более 700 тыс. клиентов по всему миру уже сделали свой выбор.

*Наведите курсор мыши для приостановки прокрутки.

Назад Вперед

Получение списка папок с помощью PHP

Список каталогов средствами PHP, или листинг директорий

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

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

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

Замечание: в PHP5 есть функция scandir , которая "возвращает список файлов и каталогов, внутри директории, по заданному пути", однако она не выводит какую-либо дополнительную информацию о находящихся внутри директории файлах.

Листинг одной директории

Для начала, вот пример простой функции, которая возвращает список файлов, каталогов и их свойства, из одной директории (более продвинутые версии этой функции вы найдёте чуть ниже в данном уроке.)

Вы можете использовать эту функцию как ниже:

Возвращаемое значение является ассоциативным массивом файлов, включающим в себя информацию о пути к файлу, размер и дату последней модификации, кроме случая, когда файл является директорией, в этом случае строка "(dir)" возникает вместо размера файла.

Пример 1:

Пример 2:

Вывод списка файлов через HTML

Чтобы получить результаты вывода на странице в HTML, мы прокрутим возвращаемый массив через цикл

Этот код довольно просто модифицировать, например:

  • - вывести результаты листинга списком вместо таблицы;
  • - сделать названия файлов активными ссылками;
  • - заменить имена иконками на основании того, какой тип у файла;
  • и т.д.

Например, для вывода только PNG-файлов, добавьте простое условие в цикл вывода:

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

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

Рекурсивный листинг директории

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

Чтобы новый функционал заработал, вам нужно ввести значение true (или 1) в качестве второго параметра.

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

Как и раньше, возвращаемая величина - это массив, ассоциативный массивов. Фактически, единственное дополнение - это ещё одна дополнительная опция для рекурсивного листинга.

Ограничение глубины рекурсии

Этот финальный пример добавляет ещё одно свойство - способность к определению, как "глубоко" должна проходить рекурсия. Предыдущий код будет продолжать исследовать вложенные директории, до тех пор пока они не закончатся. Этот скрипт поможет установить ограничение, по количеству уровней вложенных директорий.

Как и раньше, мы добавили всего 1 новый параметр и пару строк кода. Если значение по умолчанию, отвечающее за глубину рекурсинга, не задано, то оно устанавливается в false . Это позволяет нам быть уверенными в том, что предыдущие особенности остаются и последующий код не "поломается" при изменении функции.


Начиная писать программы для веба, многие начинающие программисты сталкиваются с такой ошибкой. Они рассматривают систему браузер-сервер, как обычное приложение. Интерактивное. Нажал кнопку - система среагировала. Провел мышкой - среагировала. Вся информация, которая доступна клиенту - доступна и программе, программа все время находится в памяти.
Так вот, в веб-программировании это не так! .
В момент, когда пользователь видит перед собой страницу и начинает совершать какие-то действия с ней, PHP уже завершил работу ! И пользователь взаимодействует не с PHP скриптом, а со своей страницей HTML, которую он получил в браузер. Результатом работы скрипта на PHP в большинстве случаев является обычный текст. Текст HTML страницы. Которая отдается браузеру и показывается им, как обычный HTML. Вы сами можете в этом убедиться, написав в скрипте
;
А потом просмотрев в браузере исходный текст полученной страницы. Никаких тегов PHP там нет! Только
Привет, Вася!
Потому, что PHP исполняется на сервере!

Сервер и браузер общаются, посылая друг другу запросы по особому протоколу - HTTP . Соединение может инициировать только браузер. Он посылает серверу запрос - показать такой-то файл. Сервер клиенту файл посылает.
Только так и происходит. Клиент запросил - сервер отдал. И забыл сразу о клиенте. Отсюда становится понятным ответ на вопрос, можно ли точно узнать, сколько юзеров сечас на сайте. Нельзя. потому, что "на сайте" нету ни одного. Они соединяются, запрашивают страницу, и отсоединяются. Не имеют постоянного cоединения с сервером, как, например, игроки в Кваку. Узнать можно только примерно, записывая время каждого соединения и выбирая записи за определенный промежуток времени.

Пример общения браузера с сервером:
Пользователь нажимает на ссылку, браузер посылает запрос серверу и ждет ответа:
Браузер -> PHP
PHP выполняет скрипт, отдает результат в браузер и завершает работу:
PHP -> браузер
Браузер отображает страницу, "просматривая" её на предмет ссылок, которые надо запросить у сервера (теги , и так далее) и посылает соответствующие запросы. Их можно увидеть, просматривая обмен заголовками, о чем речь будет чуть ниже:
Браузер -> сервер, Браузер -> сервер, Браузер -> сервер...
Пользователь заполняет форму и нажимает на кнопку:
Браузер -> PHP
PHP обрабатывает форму, записывает данные в базу и посылает браузеру заголовок
Location:
PHP -> браузер
Браузер, получив этот заголовок, запрашивает указанную страницу
Браузер -> PHP
PHP выполняет ее... и так далее.

Просмотр обмена HTTP заголовками
Я очень рекомендую попрактиковаться с HTTP заголовками, посмотреть, как ими обмениваются сервер и клиент.
Для этого есть множество разных способов. Если у вас стоит популярный download manager FlashGet, то можно использовать его. Так же заголовки показывает популярная программа Proxomitron, можно скачать какие-нибудь специальные утилиты.
Для IE можно предложить плагин http://blunck.se/iehttpheaders/iehttpheaders.html
Для браузера Mozilla есть удобный плагин http://livehttpheaders.mozdev.org/
Так же, существует много других утилит, легко находимых в сети по запросу HTTP sniffer.
Обязательно воспользуйтесь любым способом посмотреть HTTP заголовки, которыми обменивается браузер с сервером. Это очень хорошая практика, а так же проверка - что шлет твой скрипт. Удобно при отладке установки кук или проблемах с сессиями.
Примерное представление о пришедших заголовках можно также получить, воспользовавшись функцией getallheaders() . Но следует учитывать, что работает она только если PHP собран, как модуль.

ОЧЕНЬ ВАЖНОЕ ЗАМЕЧАНИЕ
Из того факта, что PHP исполняется на сервере, и посылает результат своей работы браузеру, следует один простой, но очень важный вывод. Что PHP в принципе НЕ МОЖЕТ отобразить в браузере ничего такого, что невозможно было бы сделать средствами html.
ПРЕЖДЕ, чем что-то писать на PHP - попробуйте это сделать чистым HTML.
"Нажатие на Энтер" не переводит строку? А в html вы не пробовали таким образом строки переводить? Не получилось? Какая досада. Прочитайте, как в html сделать перевод строки и приходите снова.

PHP в результате своей работы формирует не картинку с текстами, как вы ее видите на экране монитора! PHP формирует HTML код! И этот код ЗНАЧИТЕЛЬНО отличается от того изображения, которое вы видите на экране. Если у вас что-то не получается, то надо всегда смотреть именно ИСХОДНЫЙ код страницы, а не то, как вам ее рисует браузер. В браузере Internet Explorer исходный код можно посмотреть, выбрав в меню Вид - Просмотр HTML-кода.
Если у вас не работает яваскрипт, сформированный PHP скриптом, или html показывает не то, что вы хотите, то исправить эту проблему очень просто.
1. Сначала пишете нужный яваскрипт или html руками. Если у вас с этим проблемы - обратитесь в соотвествующий форум - по яваскрипту или html. PHP тут не при чём.
2. Сравниваете с тем, что получено из PHP
3. Вносите исправления в PHP скрипт, чтобы текст, отдаваемый им, не отличался от написанного руками.

Браузер не умеет показывать файлы, в которые напихан одновременно и html картинки. Браузер умеет показывать только известные ему типы данных. В частности, это ИЛИ html ИЛИ картинка. Но не вместе. Если картинка - то ОДНА. Несколько картинок подряд браузер показывать не умеет. Браузер умеет показывать HTML, в котором прописаны ССЫЛКИ на несколько картинок.
Пожалуйста, прежде, чем изучать PHP - изучите хотя бы основы HTML! Прежде, чем что-то требовать от PHP - попробуйте сделать это на html.

Кэмерон Лэйрд

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

Многообразие параллелизма

Сначала отложим в сторону случаи, лежащие вне русла главной темы. У PHP сложные взаимоотношения с многозадачностью или параллелизмом. На верхнем уровне PHP постоянно вовлечен в многозадачность - стандартные установки PHP на сервере (например, модуль Apache) используются многозадачным способом. То есть несколько клиентских приложений (Web-браузеров) могут одновременно запросить одну и ту же PHP-страницу, и Web-сервер возвратит ее всем более или менее одновременно.

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

Параллелизм на клиентской стороне под названием Ajax тоже привлек внимание разработчиков в последние несколько лет. Хотя значение Ajax стало несколько неясным, одним из аспектов этой технологии является то, что браузер может одновременно выполнять вычисления и оставаться чувствительным к таким действиям пользователя, как выбор пунктов меню. Это действительно отчасти многозадачность. Закодированный на PHP Ajax делает это, но без какого-либо специального участия PHP; интегрированные среды Ajax для других языков работают точно также.

Третьим примером параллелизма, который только поверхностно затрагивает PHP, является PHP/TK. PHP/TK - это расширение PHP, предоставляющее переносимые связывания графического интерфейса пользователя (Graphical User Interface - GUI) ядру PHP. PHP/TK позволяет создавать настольные GUI-приложения, написанные на PHP. Его основанные на событиях аспекты моделируют форму параллелизма, которую легко изучить, и она меньше подвержена ошибкам, чем работа с потоками. Опять же, параллелизм "унаследован" от дополнительной технологии, а не является фундаментальной функциональностью PHP.

Было несколько экспериментов по добавлению поддержки поточности в сам PHP. Насколько я знаю, ни один не был удачным. Однако ориентированные на события интегрированные среды Ajax и PHP/TK показывают, что события могут еще лучше выразить параллелизм для PHP, чем это делают потоки. PHP V5 доказывает это.

PHP V5 предлагает stream_select()

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

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

Первый пример

Новая функция stream_select , вместе с несколькими своими друзьями, предоставляет эту возможность. Рассмотрим следующий пример:


Если выполнить эту программу, отобразится примерно следующая информация:

Program starts at 02:38:50. Stream 4 closes at 02:38:53. Stream 3 closes at 02:38:56. Stream 2 closes at 02:38:59. Stream 1 closes at 02:39:02. Stream 0 closes at 02:39:05.

Важно понимать, что здесь происходит. На высоком уровне первая программа выполняет несколько HTTP-запросов и получает страницы, которые передает ей Web-сервер. Хотя реальное приложение, наверное, запрашивало бы несколько различных Web-серверов (возможно google.com, yahoo.com, ask.com и т.д.), этот пример передает все запросы на наш корпоративный сервер на Phaseit.net просто ради уменьшения сложности.

Запрошенные Web-страницы возвращают результаты после переменной задержки, показанной ниже. Если бы программа выполняла запросы последовательно, для ее завершения понадобилось бы около 15+12+9+6+3 (45) секунд. Как показано в листинге 2, на самом деле она завершается за 15 секунд. Утроение производительности - это отличный результат.

Такое стало возможно благодаря stream_select - новой функции в PHP V5. Запросы инициируются обычным способом - открытием нескольких stream_socket_clients и написанием GET к каждому из них, что соответствует http://phaseit.net/demonstration/delay?delay=$DELAY . При запросе этого URL в браузере вы должны увидеть:


Хотя конкретная реализация в листинге 3 предназначена для UNIX®, почти все сценарии данной статьи с тем же успехом применимы для установок PHP в Windows® (особенно после Windows 98) или UNIX. В частности, с листингом 1 можно работать на любой операционной системе. Linux® и Mac OS X являются вариациями UNIX, и весь приведенный здесь код будет работать в обеих системах.

Запросы к серверу задержки выполняются в следующем порядке:

delay=15 delay=12 delay= 9 delay= 6 delay= 3

Целью stream_select является как можно более быстрое получение результатов. В данном случае порядок задержек противоположен порядку, в котором были сделаны запросы. Через 3 секунды первая страница готова для чтения. Эта часть программы является обычным PHP-кодом - в данном случае с fread . Также как и в другой PHP-программе чтение могло бы осуществляться при помощи fgets .

Обработка продолжается таким же образом. Программа блокируется в stream_select , пока не будут готовы данные. Решающим является то, что она начинает чтение, как только какое-либо соединение будет иметь данные, в любом порядке. Именно так программа реализует многозадачность или параллельную обработку результатов нескольких запросов.

Обратите внимание на то, что при этом нет дополнительной нагрузки на CPU хост-компьютера. Нет ничего необычного в том, что сетевые программы, выполняющие fread таким способом, вскоре начинают использовать 100% мощности CPU. Здесь не этот случай, поскольку stream_select имеет желаемые свойства и отвечает немедленно, как только какое-нибудь чтение становится возможным, но при этом минимальным образом загружает CPU в режиме ожидания между операциями чтения.

Что нужно знать о stream_select()

Подобное основанное на событиях программирование не является элементарной задачей. Хотя листинг 1 и уменьшен до самых основных моментов, любое кодирование, базирующееся на обратных вызовах или координации (что является необходимым в многозадачных приложениях) будет менее привычным по сравнению с простой процедурной последовательностью. В данном случае наибольшая трудность заключена в массиве $read . Обратите внимание на то, что это ссылка; stream_select возвращает важную информацию путем изменения содержимого $read . Так же как указатели имеют репутацию постоянного источника ошибок в C, ссылки, по-видимому, являются той частью PHP, которая представляет наибольшую трудность для программистов.

Такую методику запросов можно использовать из любого числа внешних Web-сайтов, удостоверяясь в том, что программа будет получать каждый результат как можно быстрее, не ожидая других запросов. Фактически, данная методика корректно работает с любым TCP/IP-соединением, а не только с Web (порт 80), то есть в принципе вы можете управлять извлечением LDAP-данных, передачей SMTP, SOAP-запросами и т.д.

Но это не все. PHP V5 управляет различными соединениями как "потоками" (stream), а не простыми сокетами. Библиотека PHP Client URL (CURL) поддерживает HTTPS-сертификаты, исходящую FTP-загрузку, куки и многое другое (CURL позволяет PHP-приложениям использовать различные протоколы для соединения с серверами). Поскольку CURL предоставляет интерфейс stream, с точки зрения программы соединение прозрачно. В следующем разделе рассказывается, как stream_select мультиплексирует даже локальные вычисления.

Для stream_select существует несколько предостережений. Эта функция не документирована, поэтому не рассматривается даже в новых книгах по PHP. Несколько примеров кода, доступные в Web, просто не работают или не понятны. Второй и третий аргументы stream_select , управляющие каналами write и exception , соответствующими каналам read в листинге 1, почти всегда должны быть равны null. За некоторыми исключениями выбор этих каналов является ошибкой. Если вы не имеете достаточного опыта, используйте только хорошо описанные варианты.

Кроме того, в stream_select , по всей видимости, имеются ошибки, по крайней мере, в PHP V5.1.2. Наиболее существенным является то, что значению возврата функции нельзя доверять. Хотя я еще не отладил реализацию, мой опыт показал, что безопасно тестировать count($read) так, как в листинге 1, но это не относится к значению возврата самой stream_select , несмотря на официальную документацию.

Локальный параллелизм PHP

Пример и основная часть обсуждения выше были посвящены тому, как управлять несколькими удаленными ресурсами одновременно и получать результаты по мере их появления, а не ожидать обработки каждого в порядке первоначальных запросов. Это, несомненно, важное применение параллелизма PHP. Иногда реальные приложения можно ускорить в десять и более раз.

Что если замедление происходит поближе? Есть ли способ ускорить получение результатов в PHP при локальной обработке? Есть несколько. Пожалуй, они еще менее известны, чем ориентированный на сокеты подход в листинге 1. Этому есть несколько причин, в том числе:

  • В своем большинстве PHP-страницы достаточно быстры. Лучшая производительность могла бы быть преимуществом, но этого недостаточно для оправдания инвестиций в новый код.
  • Использование PHP в Web-страницах может сделать частичные ускорения кода не важными. Перераспределение вычислений таким образом, чтобы получать промежуточные результаты быстрее, не имеет значения, когда единственным критерием является скорость доставки Web-страницы в целом.
  • Немного локальных узких мест находится под контролем PHP. Пользователи могут выражать недовольство тем, что извлечение информации об учетной записи занимает 8 секунд, но это может быть ограничением обработки базы данных или каких-либо других ресурсов, внешних для PHP. Даже если уменьшить время PHP-обработки до нуля, все равно будет затрачено более 7 секунд просто на поиск.
  • Еще меньшее количество ограничений поддается параллельной обработке. Предположим, что конкретная страница вычисляет рекомендуемую цену для перечисленных обыкновенных акций, а вычисления достаточно сложны и выполняются в течение многих секунд. Вычисление может быть последовательным по природе. Не существует очевидного способа распределить его для "совместной работы".
  • Мало PHP-программистов понимает потенциал PHP для реализации параллельной обработки. Говоря о возможности распараллеливания, большинство из встреченных мной программистов просто цитировали фразу "PHP не работает с потоками" и возвращались к своей сложившейся модели вычислений.

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

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


Данная программа выведет на экран следующую информацию:

Program starts at 10:28:41. Finished with delay of 1. Finished with delay of 3.

Смысл заключается в том, что PHP запустил два независимых субпроцесса, получил данные от первого, а затем от второго, хотя последний стартовал раньше. Если хост-компьютер является многопроцессорным, а операционная система корректно настроена, она сама заботится о назначении различных субпрограмм разным процессорам. Это один из способов использования преимуществ многопроцессорных машин в PHP.

Резюме

PHP поддерживает многозадачность. PHP не поддерживает обработку потоков так, как это делают другие языки программирования, например Java или C++, но приведенные выше примеры показали, что PHP имеет более высокий потенциал для ускорения работы, чем многие себе представляют.

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

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

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

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

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

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

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

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

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

:::::{page_title}:::::

Welcome to your default home page. {user_name}!

Обратите внимание на три строки (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, в наших примерах в качестве ограничителей будут использоваться фигурные скобки ({ }). Впрочем, вы можете изменить два последних атрибута и выбрать ограничители по своему усмотрению. Главное -- проследите за тем, чтобы эти символы не использовались для других целей.

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

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

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

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

    function register_file($file_id, $file_name) {

    // с ключом $file_id. $this->

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

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

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

    include("tempiate.class"):

    $template = new template:

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

    $template->

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

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

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

    function register_vanables($file_id, $variable_name) {

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

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

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

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

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

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

    В параметре $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. Метод обработки файла

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

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

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

    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.

    $this->files[$file_id]);

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

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

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

    $page_title = "Welcome to your homepage!";

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

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

    $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 your homepage!";

    $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:

    :::::Welcome to your homepage!:::::

    Welcome to your default home page, Chef Jacques!

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

    Как видно из приведенного примера, все зарегистрированные переменные были заменены соответствующими значениями. При всей своей простоте класс 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_contents = fread($fh, filesize($file_name));

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

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

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

    } // Функция: 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:

    } // Функция: 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++;

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

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

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

    function print_file($file_id) {

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

    print $this->files[$file_id];

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

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

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

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

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

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

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

    • PHPLib Base Library: http://phplib.netuse.de ;
    • Richard Hayes"s Template Class: http://www.heyes-computing.net ;
    • Fast Template: http://www.thewebmasters.net/php .
    Недостатки системы шаблонов

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

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

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

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

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

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

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

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

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

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

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

    mysql>CREATE table addressbook (

    last_name char(35) NOT NULL,

    first_name char(20) MOT NULL,

    tel char(20) NOT NULL,

    email char(55) NOT NULL);

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

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

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

    :::::{page_title}:::::

    Address Book: {letter}

    ...) и ячеек (). Дело в том, что этот файл вставляется в шаблон многократно, по одному разу для каждого адреса, прочитанного из базы данных. Поскольку имя переменной rows.addresses в листинге 12.8 включается внутрь тегов

    A |

    B |

    C |

    D |

    E |

    F |

    G |

    H |

    I |

    J |

    K |

    L |

    M |

    N |

    O |

    P |

    Q |

    R |

    S |

    T |

    U |

    V |

    W |

    X |

    Y |

    Z

    {rows.addresses}

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

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

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

    {last_name},{first_name}

    {telephone}

    {email}

    В листинге 12.9 встречаются четыре переменных, заключенных в ограничители: last_name, first_name, telephone и emal. Смысл этих переменных очевиден (см. определение таблицы addressbook). Следует заметить, что этот файл состоит только из табличных тегов строк (

    ...
    ...
    , форматирование HTML будет обработано правильно. Чтобы вы лучше поняли, как работает этот шаблон, взгляните на рис. 12.1 -- на нем изображена копия страницы адресной книги. Затем проанализируйте листинг 12.10, содержащий исходный текст этой страницы. Вы увидите, что содержимое файла rows.addresses многократно встречается в странице.