Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

web - tec / PHP 5 для начинающи

.pdf
Скачиваний:
72
Добавлен:
12.06.2015
Размер:
26.79 Mб
Скачать

572 Глава 14

Как это работает

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

В первой строке require+функция загружает модуль:

require_once 'MP3/Id.php';

Объект класса пока не создается, потому что в этом нет необходимости.

Сначала нужно просмотреть каталог MP3+файлов и создать ассоциативный массив, в котором будет храниться время доступа к файлам:

static $MY_MP3_DIR = "/home/ed/mp3"; # Измените это значение! $objDir = dir($MY_MP3_DIR);

$intNumFiles = 0;

$arFileTimeHash = Array();

# Пройти по всем найденным файлам в списке

while (false !== ($strEntry = $objDir->read())) {

#Проверить, является ли текущий файл MP3-файлом,

#а не каталогом или файлом другого типа!

if (eregi("\.mp3$", $strEntry)) {

$arFileTimeHash[$strEntry] = fileatime($MY_MP3_DIR . "/" . $strEntry); $intNumFiles++;

};

};

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

Чтобы обойти эту проблему, можно пропускать все возвращаемые Dir+объектом ре+ зультаты через регулярное выражение. Описание регулярных выражений ++++++ большая те+ ма, которая вкратце рассматривалась в главе 5. Применяемое здесь регулярное выраже+ ние, .mp3$, передается нечувствительному к регистру символов методу сравнения, eregi, который проверяет, заканчивается ли запись строкой .mp3. Если это так, то, скорее все+ го, данный файл является MP3+файлом и его следует включить в ассоциативный массив.

Ключами ассоциативного массива являются имена файлов (без путей), а значения+ ми ++++++ время последнего доступа к соответствующим файлам, которое можно получить, используя функцию fileatime. Значение переменной $intNumFiles, которое будет использоваться позднее, увеличивается на единицу после нахождения каждого файла.

Не нарушая связь значений и ключей ассоциативного массива, можно заставить PHP+функцию arsort отсортировать его по значениям в обратном порядке:

#Отсортировать список файлов по дате последнего доступа и

#поместить список в обычный массив для последующего отображения

arsort($arFileTimeHash);

В этом примере ключом элемента массива является имя файла, а значением ++++++

время доступа, т.е. после сортировки массива файлы, к которым недавно осуществ+ лялся доступ, окажутся первыми.

После этого отсортированный массив преобразуется в обычный индексирован+ ный массив, каждый элемент которого является ассоциативным массивом с двумя ключами: FILENAME (имя файла) и ACCESSED (время доступа к файлу):

$intThisArrayIndex = 0;

foreach ($arFileTimeHash as $strFilename => $intAccessTime)

PEAR 573

{

$arFileList[$intThisArrayIndex]["FILENAME"] = $strFilename; $arFileList[$intThisArrayIndex]["ACCESSED"] = $intAccessTime; $intThisArrayIndex ++;

};

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

Наконец, добавляется простое правило для ограничения вывода ++++++ если в массиве больше пяти MP3+файлов, то в списке показываются только первые пять из них, а ес+ ли меньше, то показываются все файлы:

# Если найдено более 5 MP3-файлов, показать только первые 5 if ($intNumFiles > 5) {

$intNumFiles = 5;

};

Теперь можно начинать формирование HTML+кода. Для создания таблицы, в ко+ торой отображаются результаты, используется цикл по массиву от 0 (первый MP3+ файл) до значения переменной $intNumFiles (определенное ранее количество файлов). Естественно, так как отсчет начинается с нуля, из общего количества файлов необходимо вычесть единицу.

В каждой итерации цикла используется объект класса MP3_ID ++++++ его методу read передается имя и путь к файлу, который необходимо обработать, а метод getTag по+ зволяет извлечь теги ‘‘artist’’ и ‘‘name’’:

<?php

$objMP3ID = new MP3_Id();

for ($i = 0; $i<=($intNumFiles)-1; $i++)

{

?>

<tr>

<?php

$strThisFile = $arFileList[$i]["FILENAME"]; $strPath = $strMyMP3Directory . "/" . $strThisFile; $intResult = $objMP3ID->read ($strPath);

$strArtist = $objMP3ID->getTag ("artists", "Unknown Artist"); $strName = $objMP3ID->getTag ("name", "Unknown Track"); $intAccessTime = $arFileList[$i]["ACCESSED"];

?>

<td><?=$i+1?></td> <td><?=$strArtist?></td> <td><?=$strName?></td></st>

<td><?=date("m/d/Y H:i", $intAccessTime)?></td> </tr>

<?php

};

?>

Следует отметить, что здесь также выводится время доступа к файлу. Эта инфор+ мация извлекается не из 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"

link="radiorequest.php?requestfile=9991885931034.mp3" />

576 Глава 14

<node text="Extreme Temper Loss" icon="document.gif" link="radiorequest.php?requestfile=9991885931035.mp3" />

</node>

<node text="Massive Pitch Correction" icon="folder.gif"> <node text="How long ago" icon="document.gif"

link="radiorequest.php?requestfile=9991885931036.mp3" /> <node text="Ten minutes to Sunrise" icon="document.gif"

link="radiorequest.php?requestfile=9991885931037.mp3" /> </node>

</treemenu>

В данном случае в коллекции 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+серверу.

<?php

header("Content-Type: text/xml\n\n");

require_once 'MP3/Id.php';

static $strMyMP3Directory = "/home/ed/mp3"; $objDir = dir($strMyMP3Directory); $intNumFiles = 0;

$arMP3Files = Array();

// Пройти в цикле по всем файлам и поместить их имена в массив

while (false !== ($strEntry = $objDir->read())) { // Проверить, является ли этот файл MP3-файлом

if (eregi("\.mp3$", $strEntry)) {

$arMP3Files[] = $strMyMP3Directory . "/" . $strEntry;

};

};

//Создать объект класса MP3_ID $objMP3ID = new MP3_Id();

//Создать массив уникальных имен исполнителей $arArtists = Array();

$arTestArtists = Array();

for ($i=0; $i<=sizeof($arMP3Files)-1; $i++) { $strPath = $arMP3Files[$i];

$intResult = $objMP3ID->read ($strPath);

$strArtist = $objMP3ID->getTag ("artists", "Unknown Artist");

$strTestArtist = strtoupper(preg_replace("/[{}\^{}A-Za-z0-9]/", "", $strArtist));

//Проверить, находится ли имя данного исполнителя (после того

//как необычные символы удалены и все буквы

//переведены в верхний регистр) в массиве – если нет, то

//добавить исполнителя в массив

if (in_array($strTestArtist, $arTestArtists) == false) { array_push($arArtists, $strArtist);

//Обратите внимание, для добавления имен в массив $arArtists

//используются исходное количество пробелов и регистр символов...

array_push($arTestArtists, $strTestArtist);

};

};

//Для каждого исполнителя создается массив, содержащий все

//имена файлов его песен, а также названия песен

$arTracks = Array();

//Кроме того, чтобы предотвратить повторное считывание информации

//о песнях, создается массив всех уже вычисленных индексов песен

$arAlreadyAccountedForSongIndices = Array(); for ($i=0; $i<=sizeof($arArtists)-1; $i++) {

578 Глава 14

$strArtistName = $arArtists[$i]; $strTestArtistName = $arTestArtists[$i];

$arTracks[$strArtistName] = Array();

// Определяем, какие песни написаны данным исполнителем for ($j=0; $j<=sizeof($arMP3Files)-1; $j++) {

if (in_array($j, $arAlreadyAccountedForSongIndices) == false) { $strPath = $arMP3Files[$j];

$intResult = $objMP3ID->read ($strPath);

$strThisArtist = $objMP3ID->getTag ("artists", "Unknown Artist"); $strThisTestArtist = strtoupper(preg_replace("/[^A-Za-z0-9]/",

"", $strThisArtist));

if ($strThisTestArtist == $strTestArtistName) {

//Эта песня действительно принадлежит проверяемому

//исполнителю, поэтому вводим ее индекс в массив

//$arAlreadyAccountedForSongIndices, чтобы не

//проверять ее снова

array_push($arAlreadyAccountedForSongIndices, $j);

//Получаем название песни и ссылку для ее заказа и

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

$arSongHash["TITLE"] = $objMP3ID->getTag ("name", "Unknown Title"); $strSongFilename = str_replace("$strMyMP3Directory" . "/", "",

$arMP3Files[$j]);

//Создаем ссылку по имени файла, обработав имя файла

//с помощью функции urlencode

$arSongHash["LINK"] = "radiorequest.php?requestfile=" . urlencode($strSongFilename);

//Помещаем в массив следующий доступный индекс

//$arTracks[имя исполнителя]

array_push($arTracks[$strArtistName], $arSongHash); };

};

};

};

// Сортируем массив по именам исполнителей по //возрастанию (от A до Z), используя функцию ksort

ksort($arTracks);

// Выводим подходящий XML-код

?>

<treemenu>

<?php foreach ($arTracks as $artist_name => $arHash) { ?> <node text="<?=$artist_name?>" icon="folder.gif">

<?php for ($i=0; $i<=sizeof($arHash)-1; $i++) { ?>

<node text="<?=htmlentities($arHash[$i]["TITLE"])?>" icon="document.gif" link="<?=htmlentities($arHash[$i]["LINK"])?>" />

<?php }; ?> </node> <?php }; ?> </treemenu>

Сценарий radiorequest.php

Создайте новый файл и введите в него следующий код. Сохраните файл с именем radiorequest.php и поместите его в тот же каталог сервера, в котором находится файл radiogeneratexml.php. Сценарий отображает дерево навигации по доступ+ ным для заказа MP3+записям, сгенерированное сценарием radiogeneratexml.php, и обрабатывает сделанные пользователями заказы.

<?php

static $strMyMP3Directory = "/home/ed/mp3"; static $strMyDJsEmailAddress = "ed@example.com";

PEAR 579

//Подключаем необходимые PEAR-объекты require_once('HTML/TreeMenu.php'); require_once('xmlhtmltree.phpm');

//Определяем URL для получения XML и получаем его. Этот

//фрагмент можно модифицировать, если XML-генератор

//расположен на другом сервере.

$strXMLURL = "http://" . $_SERVER["SERVER_NAME"] . str_replace("request", "generatexml", $_SERVER["SCRIPT_NAME"]);

$strXML = implode (false, file($strXMLURL));

$objXMLTree = new XMLHTMLTree("", $strXML); $objXMLTree->GenerateHandOffs(); $objXMLTree->ParseXML();

$objTreeMenu = $objXMLTree->GetTreeHandoff();

//Проверим, сделан ли запрос – если да, то следует слегка

//изменить вывод

$requestMade = false; $requestSuccessful = false;

if (!empty($_GET['requestfile'])) {

$requestMade = true; // Получаем имя файла

$strRequestFilename = $_GET['requestfile']; // Проверяем существование файла

$strFullPath = $strMyMP3Directory . "/" . $strRequestFilename;

}

if (@filesize($strFullPath) > 0) {

$requestSuccessful = true;

// Все сработало, отправляем диджею e-mail mail($strMyDJsEmailAddress, "Заказ новой песни", "Сделан заказ песни: " .

$strFullPath);

} else {

$requestSuccessful = false;

}

?>

<HTML>

<HEAD>

<SCRIPT LANGUAGE="Javascript" SRC="TreeMenu.js"></SCRIPT> </HEAD>

<BODY>

<H1>Радио PHP</H1> <?php

if ($requestMade) {

?>

<B>Спасибо за заказ!</B> <BR><BR>

<?php

if ($requestSuccessful) {

?>

Ваш заказ принят, мы постараемся как можно быстрее поставить заказанную вами песню.

<?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+файлов (упорядоченных по имени) в этом каталоге:

Соседние файлы в папке web - tec