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

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

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

562 Глава 14

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

Следует отметить ссылки на несколько GIF+файлов. Класс HTML_TreeMenu для создания знаков ‘‘плюс’’, ‘‘минус’’ и т.д. использует еще несколько таких файлов, ко+ торые явно в XML+файле не упоминаются. Эти файлы включены в дистрибутив, и их можно скопировать в каталог для изображений, расположенный ниже корневого ка+ талога, в котором сохранен следующий код.

Теперь следует сформировать навигационную страницу. Создайте файл treemenutest.php и введите в него следующий код:

<HTML>

<HEAD>

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

<BODY>

<?php require_once('HTML/TreeMenu.php'); require_once('xmlhtmltree.phpm');

$objXMLTree = new XMLHTMLTree("treemenutest.xml"); $objXMLTree->GenerateHandOffs(); $objXMLTree->ParseXML();

$objTreeMenu = $objXMLTree->GetTreeHandoff(); ?> <H1>Тестирование древовидного меню</H1>

<HR>

<?php $objTreeMenu->printMenu();

?>

</BODY>

</HTML>

Здесь кроме PEAR+пакета HTML_TreeMenu подключается новый пакет, xmlhtmltree.phpm. Это упомянутый ранее вспомогательный класс; он конверти+ рует XML+код в PHP+операторы, позволяя, таким образом, использовать в классе

HTML_TreeMenu XML+код.

Ниже представлен код вспомогательного компонента. Сохраните этот код в файле xmlhtmltree.phpm в том же каталоге, что и treemenutest.php. Пока разбираться в его работе не стоит, это можно сделать позднее.

<?

class XMLHTMLTree { private $xml_content; private $hanTreeHandoff; private $objHTMLTreeMenu;

function __construct($strXMLSourceFile = "", $strXMLSource = "") { if ($strXMLSourceFile) {

$this->xml_content = implode(", @file($strXMLSourceFile)); } else {

$this->xml_content = $strXMLSource; };

$this->objHTMLTreeMenu = new HTML_TreeMenu(); $this->depth = 0;

}

function ParseXML() {

$strXML = $this->xml_content;

$objDOM = simplexml_load_string($strXML); foreach ($objDOM->node as $thisNode) {

PEAR 563

$this->_ParseNode($thisNode); };

}

private function_ParseNode(&$objNode, & $arPoint = "") { // Добавляем узел

if (!$arPoint) {

$objNewNode = new HTML_TreeNode(array('text' => $objNode['text'], 'link' => $objNode['link'], 'icon' => 'folder.gif', 'expandedIcon' =>

'folder-expanded.gif', 'expanded' => false)); $newArPoint = &$objNewNode;

}else {

$newArPoint = &$arPoint->addItem(new HTML_TreeNode(array('text' =>

$objNode['text'], 'link' => $objNode['link'], 'icon' => 'folder.gif', 'expandedIcon' => 'folder-expanded.gif')));

}; // Проверяем, есть ли в оригинале дочерние узлы

foreach ($objNode->node as $thisNode) {

if ($thisNode['text']) { $this->_ParseNode($thisNode, $newArPoint);

};

};

if (!empty($objNewNode)) { $this->objHTMLTreeMenu->addItem($objNewNode); };

}

function GenerateHandOffs() {

// Создаем класс представления

$this->hanTreeHandoff = &new HTML_TreeMenu_DHTML($this->objHTMLTreeMenu, array('defaultClass' => 'treeMenuDefault'));

$this->hanTreeHandoff->images = 'images'; }

function GetTreeHandoff() { return($this->hanTreeHandoff);

}

}

?>

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

$this->hanTreeHandoff->images = 'images';

и указать в ней необходимый путь к изображениям. Изображения, показанные на рис. 14.1 ниже, включены в дистрибутив пакета HTML_TreeMenu и находятся в подка+ талоге /usr/local/lib/php/data/HTML_TreeMenu (Unix) или C:\PHP\PEAR\PEAR\ DATA\HTML_TreeMenu (Windows).

Необходимо пройти последний этап, прежде чем класс можно будет посмотреть в действии. В дистрибутив включен файл TreeMenu.js, содержащий библиотеку JavaScript+функций. Она располагается в том каталоге, который доступен PHP, но не+ доступен Web+браузеру (JavaScript+файлы просто передаются Web+браузеру, а PHP+файлы предварительно обрабатываются). Чтобы обойти это препятствие, можно скопиро+ вать JavaScript+файл из каталога /usr/local/lib/php/data/HTML_TreeMenu (или его Windows+эквивалента) в тот каталог, в котором находится только что созданный PHP+ файл treemenutest.php.

564 Глава 14

Запустите Web+браузер и вызовите в нем страницу treemenutest.php. Результат показан на рис. 14.1.

Рис. 14.1.

Можно поэкспериментировать с меню. Разверните и сверните раздел Здоровье и страховка. Если все работает, примите поздравления! Вы только что воспользо+ вались первым PEAR+модулем.

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

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

Рассмотрим работу пакета HTML_TreeMenu, связанную с преобразованием просто+ го XML+файла в работоспособное навигационное меню.

Подключение пакета

В начале используется функция require_once, заставляющая PHP подключить код из PEAR+пакета HTML_TreeMenu.

Кроме того, необходимо подключить вспомогательный класс xmlhtmltree.phpm, который позволит передавать XML+код классу HTML_TreeMenu.

PEAR 565

Есть две причины использования require_once вместо других PHP+функций под+ ключения файлов:

если пакет не будет найден, то PHP сгенерирует неисправимое исключение и остановит выполнение сценария;

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

Работа с классами

После подключения пакета необходимо создать экземпляры классов. Сначала создается объект класса HTML_TreeMenu:

$objXMLTree = new XMLHTMLTree("treemenutest.xml");

Если вместо require использовать include+функцию для подключения пакета, то

вслучае отсутствия соответствующего пакета PHP не остановит выполнение сцена+ рия до тех пор, пока не достигнет этой строки. В данном примере, где пакет исполь+ зуется сразу после подключения, это не большая проблема. Однако в реальных при+ ложениях, где между подключением и использованием пакета создаются записи в базе данных, это действительно очень важно. Примером такого приложения может быть Web+магазин, в котором в результате одного заказа создается несколько записей в базе данных. Если подключенный PEAR+класс не используется, например, в течение вре+ мени, необходимого для создания половины записей в базе данных, то после сбоя приложения будет создана только половина записей. Это не только неаккуратно

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

вслучае отсутствия необходимого компонента на этапе подключения генерируется неисправимая ошибка и остальная часть сценария не выполняется понапрасну.

Следует подчеркнуть, что здесь PEAR+класс не используется непосредственно ++++++

создается экземпляр вспомогательного класса, которому сообщается путь к входному XML+документу.

Далее, вспомогательный класс должен создать необходимый DHTML

HTML_TreeMenu+объект, т.е. ++++++ ‘‘переключатель’’. Этот объект имеет сложную связь с объектом класса HTML_TreeMenu, поэтому, когда в объект дерева добавляется узел, этот узел также добавляется в переключатель.

$objXMLTree->GenerateHandOffs();

Внутренняя работа этого метода будет рассмотрена далее при подробном изуче+ нии вспомогательного класса.

Затем вспомогательный класс фактически выполняет разбор XML+кода во входном документе. Для этого используется встроенное расширение SimpleXML, удобно пре+ образующее XML+код в иерархию объектов, которую впоследствии можно будет легко обрабатывать. Хотя эту иерархию обрабатывает вспомогательный класс, для добавле+ ния узлов (по сути пунктов в списке) его собственному объекту класса HTML_TreeMenu он использует методы, предоставленные исходным PEAR+классом (HTML_TreeMenu).

Теперь вспомогательный класс имеет объект, готовый к созданию динамического HTML+кода, отображающего привлекательное навигационное меню. Можно было бы

566 Глава 14

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

Сначала необходимо получить DHTML HTML_TreeMenu+объект от вспомогатель+ ного класса:

$objTreeMenu = $objXMLTree->GetTreeHandoff();

Чтобы этот объект создал требуемый HTML+код, нужно вызвать один метод:

$objTreeMenu->printMenu();

который сгенерирует необходимый для меню код.

Еще любопытнее просмотреть исходный код меню из Web+браузера. Фактически PHP не генерирует HTML+код вообще ++++++ генерируется только JavaScript+код. PHP в данном случае используется для наполнения JavaScript+массива ++++++ его собственного объекта древовидного меню, который будет использоваться Web+браузером для ото+ бражения меню. Фактическая логика для генерации DHTML+кода содержится в файле treeMenu.js. Использование динамической серверной логики для генерации стати+ ческой клиентской JavaScript+логики в наши дни становится все более популярным, а рассматриваемый здесь пакет предоставляет наглядный пример, объясняющий такую популярность. Это позволяет не только воспользоваться преимуществами невероятно мощных PHP+методик синтаксического анализа XML+кода для определения того, ка+ кая информация будет отображаться в Web+браузерах, это также позволяет полностью использовать преимущества сегодняшнего поколения Web+браузеров при определе+ нии того, как эта информация должна отображаться. Такое разделение уровней под+ сказывает более современную методику программирования, которая называется MVC (Model, View, Controller ++++++ модель, отображение, контроллер). В книге эта методика применяется мало, но очень полезно выработать привычку ее использовать.

Работа DHTML выходит за рамки данной книги, но заинтересованным читате+ лям рекомендуется посетить Web+сайт Дена Стейнмана (Dan Steinman) Dynamic Duo www.dansteinman.com/dynduo/.

Вспомогательный класс

Рассмотрим вспомогательный класс xmlhtmltree.phpm более подробно. Как от+ мечалось ранее, этот класс позволяет использовать в качестве источника данных для создания пунктов древовидного меню XML+код вместо PHP+кода.

Следовательно, задача класса заключается в том, чтобы принять правильно сфор+ мированный XML+документ, создать объект класса HTML_TreeMenu, а затем обойти все XML+элементы, одновременно вызывая предоставленные этим объектом методы, и заполнить, таким образом, объект исключительно на основании XML+документа.

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

Сначала рассмотрим переменные экземпляра:

private $xml_content; private $hanTreeHandoff; private $objHTMLTreeMenu;

Первая переменная содержит XML+содержимое, которое хранится в виде строки, вто+ рая представляет собой переключатель ++++++ ссылку на DHTML+версию HTML_TreeMenu+ объекта, а третья ++++++ сам HTML_TreeMenu+объект.

PEAR 567

Конструктор принимает один из двух возможных параметров: либо путь к XML+ файлу, либо строку, содержащую XML+код. Используя оператор = "", можно сооб+ щить PHP о том, что оба параметра являются необязательными. Если указывается ис+ ходный XML+файл, сценарий извлекает его содержимое. В любом случае исходный XML+код сохраняется впоследствии в переменной xml_content и подготавливается, таким образом, к разбору.

function __construct($strXMLSourceFile = "", $strXMLSource = "") { if ($strXMLSourceFile) {

$this->xml_content = implode(", @file($strXMLSourceFile));

}else {

$this->xml_content = $strXMLSource;

};

$this->objHTMLTreeMenu = new HTML_TreeMenu();

}

Кроме того, в конструкторе создается объект класса HTML_TreeMenu, который со+ храняется как переменная экземпляра класса (objHTMLTreeMenu).

Метод GenerateHandOffs создает отображаемый HTML_TreeMenu+объект (DHTML+ код), к которому затем можно получить доступ с помощью метода GetTreeHandoff.

function GenerateHandOffs() { // Создаем класс отображения

$this->hanTreeHandoff = &new HTML_TreeMenu_DHTML($this->objHTMLTreeMenu, array('defaultClass' => 'treeMenuDefault'));

$this->hanTreeHandoff->images = 'images';

}

Синтаксис для отображения DHTML+кода объекта меню взят непосредственно из документации HTML_TreeMenu. Чтобы программа могла находить изображения, при+ меняемые для генерации навигационного меню, здесь также присваивается значение свойству images.

Метод ParseXML начинает разбор XML+содержимого вспомогательного класса.

function ParseXML() {

$strXML = $this->xml_content;

$objDOM = simplexml_load_string($strXML); foreach ($objDOM->node as $thisNode) {

$this->_ParseNode($thisNode); };

}

Сначала этому методу передается XML+код. Это не обязательно, но ясность кода такой прием повышает.

Затем используется SimpleXML+метод simplexml_load_string для загрузки XML+структуры и ее представления в виде псевдообъектной структуры, которая со+ храняется в переменной $objDom.

Далее вызывается метод foreach для обхода семейства узловых ‘‘объектов’’, кото+ рые доступны благодаря SimpleXML. Ссылка на каждый из этих объектов передается ча+ стному методу _ParseNode, который недоступен для кода вне вспомогательного класса.

private function _ParseNode($objNode, & $arPoint = "") { // Добавляем узел

if (!$arPoint) {

$objNewNode = new HTML_TreeNode(array('text' => $objNode['text'], 'link' => $objNode['link'], 'icon' => 'folder.gif', 'expandedIcon' => 'folder-expanded.gif', 'expanded' => false));

$newArPoint = &$objNewNode;

}else {

$newArPoint = &$arPoint->addItem(new HTML_TreeNode(array('text' =>

568 Глава 14

$objNode['text'], 'link' => $objNode['link'], 'icon' => 'folder.gif', 'expandedIcon' => 'folder-expanded.gif')));

}; // Проверяем, есть ли в оригинале дочерние узлы

foreach ($objNode->node as $thisNode) { if ($thisNode['text']) {

$this->_ParseNode($thisNode, $newArPoint); };

};

if ($objNewNode) { $this->objHTMLTreeMenu->addItem($objNewNode); };

}

Метод _ParseNode рекурсивно вызывает сам себя. Рекурсия лучше всего описыва+ ется как средство определенного метода выполнять выборочные вызовы самого себя с целью обхода структуры данных с неизвестной степенью вложенности. Например, если есть двумерный массив чисел, то чтобы обойти его, понадобится два for+ цикла ++++++ вероятно, в одном из них будет использоваться переменная счетчика x, а в дру+ гом y. Аналогично, если есть трехмерный массив чисел, придется использовать три вложенных цикла со счетчиками x, y и z. Однако в этих двух примерах число измерений постоянно и известно. В XML структура может иметь любое число дочерних узлов, каж+ дый их которых в свою очередь тоже может иметь любое количество дочерних узлов и т.д. Использование рекурсии гарантирует, что каждый отдельный узел обрабатывает+ ся новым экземпляром метода каждый раз, когда находится новое множество дочерних узлов; как только множество дочерних узлов обработано, родительский экземпляр про+ должает работу с той точки, в которой прекратилась обработка дочерних узлов.

Эта методика весьма четко и просто работает в примере с генерацией меню. Для каждого узла в XML+коде в HTML_TreeMenu+объекте с помощью методов, описанных в документации к данному пакету, создается соответствующий узел. Затем выполняет+ ся проверка существования в генерируемом в текущий момент времени узле дочерних узлов. Если дочерние узлы есть, каждый из них в свою очередь передается новому эк+ земпляру метода _ParseNode, выполняющему точно такой же процесс с дочерним уз+ лом. Ссылка на родительский узел (если он есть) также передается методу, поэтому метод знает, к какому узлу в создаваемой иерархии следует добавить дочерний объект.

Наконец, рассмотрим метод GetTreeHandoff().

function GetTreeHandoff() { return($this->hanTreeHandoff);

}

Все очень просто. Этот метод возвращает ссылку на DHTML+код, сгенерированный HTML_TreeMenu, доступный благодаря использованному выше методу GenerateHandOffs(). Структура, возвращаемая этим методом, может содержать собственный ме+ тод printMenu, который при необходимости вызывается для генерации HTML+ и JavaScript+кода, делающего меню видимым в окне браузера.

Использование вспомогательных классов иногда бывает неизбежным. Если разра+ ботчик многократно использует собственноручно написанный класс, то он может пе+ редать этот класс в репозиторий PEAR, указав в качестве зависимости исходный PEAR+класс, на котором базируется его собственная разработка. О том, как это дела+ ется, подробно рассказано на Web+сайте проекта PEAR.

PEAR 569

Использование PEAR-пакетов

Итак, вы уже знаете, как найти необходимый PEAR+пакет для использования в разраба+ тываемом проекте, как установить этот пакет и все пакеты, от которых он зависит, а также как интегрировать PEAR+пакеты в собственный код. Теперь можно применить получен+ ные знания на практике и создать работоспособное приложение, используя один PEAR+ компонент и несколько более распространенных встроенных в PHP подпрограмм.

Практика Создание приложения с использованием одного PEAR-компонента

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

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

Предположим, пользователь хранит MP3+файлы в каталоге C:\MP3. Если это не так (или приложение должно работать на Unix+системе), то можно изменить в коде значение переменной $MY_MP3_DIR.

PEAR-пакет: MP3_ID

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

Более подробную информацию о пакете MP3_ID можно получить на странице http://pear.php.net/package/MP3_ID.

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

Пакет устанавливается как обычно:

root@genesis:~# pear install MP3_ID downloading MP3_Id-1.0.tgz ...

...done: 7,517 bytes install ok: MP3_Id 1.0 root@genesis:~#

Приложение

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

570 Глава 14

их проигрывания). Если в каталоге найдено больше пяти файлов, то отображаются только первые пять из них.

Создайте файл с именем mp3id.php и поместите его в каталог, доступный Web+ серверу. Введите в файл следующий код. Не забудьте при этом изменить значение пере+ менной $MY_MP3_DIR так, чтобы оно указывало на каталог, в котором действительно хра+ нятся MP3+файлы (убедитесь, что в этом каталоге есть несколько MP3+файлов). Не беспо+ койтесь, если не все понятно ++++++ позднее работа сценария будет рассмотрена подробно.

<?php

require_once 'MP3/Id.php';

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++;

};

};

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

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

arsort($arFileTimeHash); $arFileList = Array(); $intThisArrayIndex = 0;

foreach ($arFileTimeHash as $strFilename => $intAccessTime){ $arFileList[$intThisArrayIndex]["FILENAME"] = $strFilename; $arFileList[$intThisArrayIndex]["ACCESSED"] = $intAccessTime; $intThisArrayIndex ++;

};

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

$intNumFiles = 5;

};

?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html>

<head>

<title>Моя MP3-коллекция</title> </head>

<body>

<H1>Моя MP3-коллекция </H1>

<HR>

Ниже перечислены пять верхних песен, которые я недавно прослушал

<BR><BR>

<table border="1" cellpadding="3" cellspacing="3">

<tr> <td>Место</td>

<td>Исполнитель</td> <td>Название композиции</td>

<td>Дата последнего прослушивания:</td> </tr>

<?php

$objMP3ID = new MP3_Id();

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

?>

PEAR 571

<tr>

<?php

$strThisFile = $arFileList[$i]["FILENAME"]; $strPath = $MY_MP3_DIR . "/" . $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>

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

<?php

};

?>

</body>

</html>

Запустите Web+браузер и откройте в нем только что созданную PHP+страницу. Ре+ зультат должен быть похожим на рис. 14.2.

Рис. 14.2.

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