Этот этап не обязателен ++++++ он лишь проясняет логику, которая необходима для отображения перечня файлов.
Наконец, добавляется простое правило для ограничения вывода ++++++ если в массиве больше пяти 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+файлов (упорядоченных по имени) в этом каталоге:
Теперь можно использовать линейную коллекцию MP3+файлов. Так как физиче+ ская структура создаваемого приложения в своей основе упорядочена по именам ис+ полнителей, имеет смысл создать один массив с именами всех исполнителей в кол+ лекции MP3+файлов. Возможно, коллекция состоит из тысяч файлов, но, скорее всего, отдельных исполнителей в ней будет только несколько сотен.
Получение имени исполнителя из каждого файла ++++++ первая цель использования класса MP3_ID:
$objMP3ID = new MP3_Id(); $arArtists = Array(); $arTestArtists = Array();
for ($i=0; $i<=sizeof($arMP3Files)-1; $i++) { $strPath = $arMP3Files[$i];
if (in_array($strTestArtist, $arTestArtists) == false) { array_push($arArtists, $strArtist); array_push($arTestArtists, $strTestArtist);
};
};
Следует отметить, что создается два массива ++++++ $arArtists, содержащий имена исполнителей, и $arTestArtists с измененной версией каждого имени, где все бук+ вы переведены в верхний регистр и удалены все не алфавитно+цифровые символы (включая пробелы). Это сделано по одной причине: необходимо убедиться, что каж+ дый исполнитель появляется в списке только один раз. Удаление пробелов и знаков препинания, а также перевод всех символов в верхний регистр позволяет с большой долей уверенности сравнить имя исполнителя, извлеченное из IDv3+тега, с тем име+ нем, которое уже встречалось в течение времени жизни цикла.
В качестве примера рассмотрим вымышленного исполнителя ‘‘Extreme Metal Grind+ ing’’. Предположим, что в коллекции имеется три композиции этого исполнителя, соз+ данных в разное время тремя различными программами. Имя исполнителя в IDv3+теге вполне может быть представлено тремя незначительно отличающимися способами, на+ пример, ‘‘Extreme Metal Grinding’’, ‘‘EXTREME ‘Metal’ Grinding’’ и ‘‘Extreme Metalgrind+ ing’’. Использованное в операторе $strTestArtist = strtoupper (preg_replace ("/[^A-Za-z0-9]/", "", $strArtist)) регулярное выражение и преобразова+ ние к верхнему регистру для каждого из этих тегов позволяет всегда получать один и тот же результат: EXTREMEMETALGRINDING. Вероятно, на экране такое имя будет выглядеть некрасиво, но оно хорошо подходит для сравнения и создания учетной за+ писи для этой группы исполнителей.
Итак, в цикле просматривается каждый MP3+файл и из него извлекается имя ис+ полнителя, к которому применяется регулярное выражение. Затем нужно проверить, присутствует ли измененное имя исполнителя в тестовом массиве. Если это не так, то имя исполнителя записывается в оба массива ++++++ в тестовый массив (сюда записывается