Как и в примере с пакетом HTML_TreeMenu, всю основную работу приложение вы+ полняет в начале, а затем в случае необходимости отображает информацию, аккурат+ но вставленную в HTML+код. Код пакета MP3_ID лаконичен благодаря тому, что в нем нет зависимостей и его можно использовать сразу после установки.
В первой строке require+функция загружает модуль:
require_once 'MP3/Id.php';
Объект класса пока не создается, потому что в этом нет необходимости.
Сначала нужно просмотреть каталог MP3+файлов и создать ассоциативный массив, в котором будет храниться время доступа к файлам:
Полезный Dir+объект позволяет просмотреть необходимый каталог. К сожале+ нию, он не дает возможности фильтровать вывод. При создании листинга каталога существует вероятность, что в результате будет выведено большое количество ненуж+ ной информации.
Чтобы обойти эту проблему, можно пропускать все возвращаемые Dir+объектом ре+ зультаты через регулярное выражение. Описание регулярных выражений ++++++ большая те+ ма, которая вкратце рассматривалась в главе 5. Применяемое здесь регулярное выраже+ ние, .mp3$, передается нечувствительному к регистру символов методу сравнения, eregi, который проверяет, заканчивается ли запись строкой .mp3. Если это так, то, скорее все+ го, данный файл является MP3+файлом и его следует включить в ассоциативный массив.
Ключами ассоциативного массива являются имена файлов (без путей), а значения+ ми ++++++ время последнего доступа к соответствующим файлам, которое можно получить, используя функцию fileatime. Значение переменной $intNumFiles, которое будет использоваться позднее, увеличивается на единицу после нахождения каждого файла.
Не нарушая связь значений и ключей ассоциативного массива, можно заставить PHP+функцию arsort отсортировать его по значениям в обратном порядке:
#Отсортировать список файлов по дате последнего доступа и
#поместить список в обычный массив для последующего отображения
arsort($arFileTimeHash);
В этом примере ключом элемента массива является имя файла, а значением ++++++
время доступа, т.е. после сортировки массива файлы, к которым недавно осуществ+ лялся доступ, окажутся первыми.
После этого отсортированный массив преобразуется в обычный индексирован+ ный массив, каждый элемент которого является ассоциативным массивом с двумя ключами: FILENAME (имя файла) и ACCESSED (время доступа к файлу):
$intThisArrayIndex = 0;
foreach ($arFileTimeHash as $strFilename => $intAccessTime)
Этот этап не обязателен ++++++ он лишь проясняет логику, которая необходима для отображения перечня файлов.
Наконец, добавляется простое правило для ограничения вывода ++++++ если в массиве больше пяти MP3+файлов, то в списке показываются только первые пять из них, а ес+ ли меньше, то показываются все файлы:
# Если найдено более 5 MP3-файлов, показать только первые 5 if ($intNumFiles > 5) {
$intNumFiles = 5;
};
Теперь можно начинать формирование HTML+кода. Для создания таблицы, в ко+ торой отображаются результаты, используется цикл по массиву от 0 (первый MP3+ файл) до значения переменной $intNumFiles (определенное ранее количество файлов). Естественно, так как отсчет начинается с нуля, из общего количества файлов необходимо вычесть единицу.
В каждой итерации цикла используется объект класса MP3_ID ++++++ его методу read передается имя и путь к файлу, который необходимо обработать, а метод getTag по+ зволяет извлечь теги ‘‘artist’’ и ‘‘name’’:
Следует отметить, что здесь также выводится время доступа к файлу. Эта инфор+ мация извлекается не из IDv3+тега файла, а из самого массива с перечнем файлов; ин+ формация извлекается и записывается фактически во время создания листинга ката+ лога, поэтому к ней можно запросто вернуться.
Время доступа, которое представлено в виде временной метки Unix (количество истекших секунд с первого января 1970 года), с помощью PHP+функции date отобра+ жается в более читабельном формате.
Итак, в несколько простых шагов была создана оригинальная реклама для началь+ ной страницы (которая будет гораздо интереснее, чем ссылка вроде ‘‘Заполните мою гостевую книгу’’).
574 Глава 14
Что делать, если найдена проблема?
При использовании этого интересного приложения возможно появление неболь+ шой проблемы. Если запустить сценарий один раз, то все будет работать нормально, но если запустить его дважды (нажать кнопку ‘‘Обновить’’ в Web+браузере), то колон+ ка ‘‘время последнего прослушивания’’ будет выглядеть необычно.
Это не ошибка, а скорее случайная функция. Чтобы считать ID+тег MP3+файла, фактически необходимо осуществить доступ к этому файлу, в результате чего изменя+ ется время последнего доступа к нему. Поэтому при повторном запуске приложения время доступа к MP3+файлу равно времени запуска сценария, а не времени последнего прослушивания файла.
Есть простой способ обойти эту проблему ++++++ использование PHP+функции touch. Сразу после доступа к файлу и получения IDv3+сведений следует, используя функцию touch, установить время доступа к файлу, равное его предыдущему значению. К со+ жалению, данный метод работает, только если владельцем MP3+файлов является тот же пользователь, от имени которого работает процесс Web+сервера.
Более сложное решение предполагает периодическое индексирование MP3+ файлов в базе данных, чтобы не нужно было считывать IDv3+теги каждый раз, когда пользователь посещает Web+страницу; затем можно обращаться к базе данных, остав+ ляя время доступа к файлу нетронутым. Реализация этого способа ++++++ хорошее упраж+ нение для читателей.
Время доступа к файлу ****** свойство, которое операционная система предоставляет PHP. В зависимости от используемой операционной системы и от файловой системы сервера это свойство может быть недоступно в PHP. В таком случае вместо времени доступа можно использовать время последнего изменения файла. Это означает, что данные, возвращаемые сценарием, не будут абсолютно точными. Поэтому если планируется развернуть такое приложение в реальной среде, прежде чем предоставлять к нему общий доступ, желательно обеспечить проверку возвращаемых результатов.
Создание приложения с использованием двух PEAR-компонентов
Объединим полученные навыки работы с PEAR и создадим новое приложение с нуля. Ниже рассматривается пример создания полезного приложения с использова+ нием двух уже описанных PEAR+компонентов ++++++ HTML_TreeMenu и MP3_ID.
Приложение
В наши дни некоторые из самых передовых Internet+радиостанций, а также обыч+ ные радиостанции, имеющие свои Web+сайты, предлагают своим слушателям возмож+ ность заказывать на этих сайтах любимые песни.
Использование ограниченного списка композиций с запросами в свободной фор+ ме было бы неразумным, поскольку многие запросы слушателей оставались бы невы+ полненными. Обычно станции предлагают списки всех музыкальных композиций, находящихся в их архивах, и позволяют слушателям выбирать из этих списков инте+ ресующие их песни.
Постоянные поступления в архив, которые обычно состоят из MP3+файлов, могут сильно усложнять поддержку запросной части Web+сайта, поэтому желательно иметь способ динамической генерации и удобного форматирования списка запросов.
Разрабатываемое приложение обладает указанными функциями. Оно предостав+ ляет все MP3+файлы в каталоге сервера в виде иерархического списка (используя
PEAR 575
компонент HTML_TreeMenu), отсортированного по имени исполнителя. Пользователь заказывает песню, щелкнув на ее названии, затем приложение генерирует и отправ+ ляет диджею радиостанции e+mail+письмо, в котором указан соответствующий файл.
Для отправки почты в приложении используется встроенная PHP+функция mail(). Это очень простой способ отправки e+mail и в большинстве реальных приложений требуется нечто более сложное. В следующей главе описывается поиск PEAR+классов, которые могут выручить разработчика.
Для создания вывода компонента HTML_TreeMenu также используется XML. Что+ бы преобразовать XML+код в PHP+методы, понятные HTML_TreeMenu, также придет+ ся использовать вспомогательный класс.
Архитектура
С целью аккуратной организации компонентов приложения его можно разделить на два отдельных PHP+файла. Первый файл radiogeneratexml.php генерирует XML+представление каталога MP3+файлов на сервере, отсортированное по имени ис+ полнителя. Второй сценарий radiorequest.php фактически отображает этот спи+ сок и обрабатывает переданные пользователем данные.
XML+код передается от одного сценария другому посредством HTTP+запросов. Сце+ нарий будет заставлять сервер делать запросы от своего имени. Для тех, кто знаком с идеей Web+служб на базе XML, такой подход не покажется бессмысленным. В данном случае идея заключается в том, чтобы реализовать возможность получать XML+данные практически из любого источника. Например, реальная радиостанция может использо+ вать один сервер для обслуживания общедоступного Web+сайта и полностью обособ+ ленный сервер для хранения MP3+файлов. Второй сервер может быть недоступен из внешнего мира. Вместо того чтобы пытаться считывать информацию о файлах через какие+либо сетевые ресурсы, что весьма неэффективно, можно поместить файл radiogeneratexml.php на сервер с MP3+файлами, а сценарий radiorequest.php ++++++ на обще+ доступный Web+сервер, и заставить последний запрашивать XML+код с другого сервера.
Кроме всего прочего, это позволит в случае необходимости сделать XML+код обще+ доступным и, таким образом, предоставить Web+сайтам третьих лиц доступ к данным.
XML*код в целях разрабатываемого приложения соответствует стандартам, которых требует компонент HTML_TreeMenu. Если учесть возможность вторичного использования XML, то впоследствии можно обновить XML*код так, чтобы приложение было более ориентированным на службу, а не на формат отображения. Это позволит использовать простую таблицу стилей XSL для преобразования служебного XML*кода в XML*код, понятный PEAR*компоненту.
Генерация XML-кода
Сценарий radiogeneratexml.php отвечает не только за создание XML+кода, ко+ торый может использоваться страницей заказов, но и за чтение MP3+каталога, извле+ чение полезных данных об исполнителе и названии песни из каждого MP3+файла, а также за необходимую группировку и сортировку списка композиций.
Рассмотрим кратко XML+код, который нужно генерировать для компонента
HTML_TreeMenu:
<?xml version="1.0"?> <treemenu>
<node text="Extreme Metal Grinding" icon="folder.gif"> <node text="Extreme Noises" icon="document.gif"
В данном случае в коллекции MP3+записей имеется два исполнителя: ‘‘Extreme Metal Grinding’’ и ‘‘Massive Pitch Correction’’ (это не самая большая радиостанция в мире). На+ звания песен обоих исполнителей размещаются в соответствующих узлах иерархии.
Название каждой песни представляет собой ссылку на страницу radiorequest.php; в ссылке передается GET+параметр requestFile. Имя MP3+файла передается потому, что диджею удобнее использовать имя файла, а не IDv3+тег. PHP+функция urlencode() гарантирует, что имя файла перед передачей будет преобразовано в необходимую для URL форму.
Чтобы проверить, как страница выглядит после обработки HTML_TreeMenu, мож+ но использовать код из примера в первом разделе ‘‘Практика’’ этой главы и вставить XML+код в файл treemenutest2.xml. Затем необходимо модифицировать файл treemenutest.php, указав в нем корректное имя XML+файла:
$objXMLTree = new XMLHTMLTree("treemenutest2.xml");
Запустите Web+браузер и снова откройте в нем страницу treemenutest.php. Ре+ зультат работы сценария показан на рис. 14.3.
Рис. 14.3.
PEAR 577
Теперь страница заказов выглядит так, как нужно (хотя HTML+код можно немного исправить). Необходимо только определить, каким образом подобный код будет ге+ нерироваться динамически на основании содержимого каталога MP3+файлов.
Практика Создание двухуровневого, двухкомпонентного PEAR-приложения
Работа кода для обоих компонентов подробнее описывается после листингов.
Сценарий radiogeneratexml.php
Создайте новый файл, вставьте в него следующий код и сохраните его под именем radiogeneratexml.php. Именно этот уровень автоматически генерирует XML+код для дерева навигации на основании содержимого каталога MP3+файлов. Поместите файл в каталог, доступный Web+серверу.
Создайте новый файл и введите в него следующий код. Сохраните файл с именем radiorequest.php и поместите его в тот же каталог сервера, в котором находится файл radiogeneratexml.php. Сценарий отображает дерево навигации по доступ+ ным для заказа MP3+записям, сгенерированное сценарием radiogeneratexml.php, и обрабатывает сделанные пользователями заказы.
Ваш заказ принят, мы постараемся как можно быстрее поставить заказанную вами песню.
<?php
} else {
?>
К сожалению, мы не можем поставить заказанную вами песню.Возможно, она была недавно удалена из нашей коллекции. Пожалуйста, попробуйте заказать другую песню.
<?php
580 Глава 14
};
?>
<BR><BR>
<?php
};
?>
<?php
if (!($requestSuccessful)) {
?>
Закажите песню из приведенного ниже списка. Заказ будет отправлен нашему диджею. Если она давно не проигрывалась, то мы включим ее как можно быстрее!
<HR>
<?php $objTreeMenu->printMenu();
?>
<?php
};
?> </BODY> </HTML>
Откройте в браузере страницу radiorequest.php. Должна появиться страница, похожая на рис. 14.4.
Рис. 14.4.
Разверните один из узлов и щелкните на какой+либо песне. В результате этого должно появиться сообщение, показанное на рис. 14.5 (предполагается, что параметр sendmail_path в файле php.ini задан правильно).
PEAR 581
Рис. 14.5.
Как это работает: radiogeneratexml.php
Рассмотрим работу сценария radiogeneratexml.php шаг за шагом.
Строго говоря, клиенту отправляется XML+код, а не HTML, поэтому следует сооб+ щить об этом клиенту (встроенный в PHP объект запросов не делает этого), используя правильно сформированный HTTP+заголовок Content Type:
header("Content-Type: text/xml\n\n");
Очень важно, чтобы перед этим заголовком в коде не было пустот вне разделите+ лей <?php и ?>, потому что, встретив любой не+PHP+код ++++++ даже пробел или перевод строки, ++++++ PHP отправляет заголовок типа содержимого text/html, а это в данном случае нежелательно.
Далее следует подключить PEAR+класс (MP3_ID):
require_once 'MP3/Id.php';
В этом файле выводится обычный XML+код, поэтому нет необходимости подклю+ чать класс HTML_TreeMenu.
Рассмотрим создание перечня MP3+файлов в каталоге. Измените значение соот+ ветствующей переменной, чтобы указать каталог, в котором находятся MP3+файлы (для работы этого примера в каталоге должно быть хотя бы три MP3+файла). Затем с помощью модифицированной версии кода из предыдущего MP3_ID+проекта создается одномерный массив всех MP3+файлов (упорядоченных по имени) в этом каталоге: