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

PHP5_nachinayushim

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

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

582 Глава 14

static $strMyMP3Directory = "/home/ed/mp3"; # Укажите реальный каталог с MP3-файлами $objDir = dir($strMyMP3Directory);

$intNumFiles = 0; $arMP3Files = Array();

while (false !== ($strEntry = $objDir->read())) { if (eregi("\.mp3$", $strEntry)) {

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

};

};

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

Получение имени исполнителя из каждого файла ++++++ первая цель использования класса 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); 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+файл и из него извлекается имя ис+ полнителя, к которому применяется регулярное выражение. Затем нужно проверить, присутствует ли измененное имя исполнителя в тестовом массиве. Если это не так, то имя исполнителя записывается в оба массива ++++++ в тестовый массив (сюда записывается

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]