Введение
Значение XML в современной среде разработки приложений трудно переоценить. Тем, кто никогда не работал с XML в РНР или еще не перешел на РНР5, это краткое руководство поможет освоить новую функциональность РНР5, связанную с XML, и оценить, насколько простой может быть работа с XML. Эта первая из трех статей посвящена знакомству с API и демонстрирует, что SimpleXML в сочетании с DOM служит идеальным инструментом для разработчиков, имеющих дело с простыми, предсказуемыми и относительно компактными XML-документами. Именно такими документами оперируют, например, Ajax-приложения при передаче информации о заполнении формы, в такие же документы оформляются ответы API Web-сервисов, таких как weather.com.
Основы XML
Краткое введение в основы XML позволит читателю понять важность этого языка для РНР-разработчика и научиться создавать простые XML-документы.
Сведения об XML
Язык Extensible Markup Language (XML) можно назвать и языком разметки, и форматом хранения текстовых данных. Это подмножество языка Standard Generalized Markup Language (SGML); он предоставляет текстовые средства для описания древовидных структур и их применения к информации. XML служит основой для целого ряда языков и форматов, таких как Really Simple Syndication (RSS), Mozilla XML User Interface Language (XUL), Macromedia Maximum eXperience Markup Language (MXML), Microsoft eXtensible Application Markup Language (XAML) и open source-язык Java XML UI Markup Language (XAMJ).
Структура XML
Базовым блоком данных в XML является элемент. Элементы выделяются начальным тегом, таким как <book>, и конечным тегом, таким как </book>. Каждому начальному тегу должен соответствовать конечный тег. Если для какого-то начального тега отсутствует конечный тег, XML-документ оформлен неправильно, и синтаксический анализатор (парсер) не сможет проанализировать его надлежащим образом. Названия тегов обычно отражают тип элемента. Можно ожидать, что элемент book содержит название книги, например, «Большой американский роман» (см. листинг 1). Текст, содержащийся между тегами, включая пробелы, называется символьными данными.
Листинг 1. Пример XML-документа
<books> <book> <title>Большой американский роман</title> <characters> <character> <name>Клифф</name> <desc>отличный парень</desc> </character> <character> <name>Миловидная Женщина</name> <desc>редкая красавица</desc> </character> <character> <name>Преданный Пес</name> <desc>любит поспать</desc> </character> </characters> <plot> Клифф встречает Миловидную Женщину. Преданный Пес спит, но просыпается, чтобы облаять почтальона. </plot> <success type="bestseller">4</success> <success type="bookclubs">9</success> </book> </books>
Имена XML-элементов и атрибутов могут состоять из латинских букв верхнего (A-Z ) и нижнего (a-z) регистров, цифр (0-9), некоторых специальных и неанглийских символов, а также трех знаков пунктуации: дефиса, знака подчеркивания и точки. Другие символы в именах не допускаются.
XML чувствителен к регистру. В приведенном примере <Book> и <book> описывают два разных элемента. Оба имени приемлемы. Однако описание двух разных элементов именами <Book> и <book> нельзя считать разумным решением ввиду высокой вероятности опечаток.
Каждый документ XML содержит один и только один корневой элемент. Корневой элемент — это единственный элемент XML-документа, для которого нет родительского элемента. В приведенном выше примере корневым является элемент <books>. Большинство XML-документов содержат родительские и дочерние элементы. Элемент <books> имеет один дочерний элемент <book>. У элемента <book> четыре дочерних элемента: <title>, <characters>, <plot> и <success>. У элемента <characters> три дочерних элемента, каждый из которых является элементом <character>. У каждого элемента <character> по два дочерних элемента, <name> и <desc>.
Кроме вложенных элементов, что создает отношения родительский-дочерний, XML-элементы могут иметь атрибуты. Это пары имя-значение, присоединенные к начальному тегу элемента. Имена отделяются от значений знаком равенства, =. Значения заключаются в одинарные или двойные кавычки. В листинге 1 элемент <success> имеет два атрибута: "bestseller" и "bookclubs". XML-разработчики практикуют разные подходы к использованию атрибутов. Большую часть информации, содержащейся в атрибуте, можно поместить в дочерний элемент. Некоторые разработчики настаивают на том, чтобы информация атрибутов состояла не из данных, а из метаданных, то есть сведений о данных. Сами данные должны содержаться в элементах. На самом деле решение о том, использовать ли атрибуты, зависит от природы данных и от того, как они извлекаются из XML.
Достоинства XML
Одно из достоинств XML состоит в его относительной простоте. XML-документ можно составить в простом текстовом редакторе или текстовом процессоре, не прибегая к специальным инструментам или ПО. Базовый синтаксис XML состоит из вложенных элементов, некоторые из которых имеют атрибуты и содержание. Обычно элемент начинается открывающим тегом <тег> и заканчивается соответствующим закрывающим тегом </тег >. XML чувствителен к регистру и не игнорирует пробелы и табуляции. Он очень похож на HTML, но, в отличие от HTML, позволяет присваивать тегам имена для лучшего описания свох данных. К числу преимуществ XML относится самодокументирование, читабельный для людей и компьютеров формат, поддержка Unicode, что позволяет создавать документы на разных языках, и простые требования к синтаксису и синтаксическому анализу. К сожалению, в РНР5 поддержка UTF-8 сопряжена с проблемами; это один из тех недостатков, которые привели к разработке РНР6.
Недостатки XML
XML многословен и избыточен, что порождает документы большого объема, занимающие много дискового пространства и сетевых ресурсов. Предполагается, что он должен быть читабелен для людей, но трудно представить себе человека, пытающегося прочесть файл XML с 7 млн. узлов. Простейшие синтаксические анализаторы функционально не способны поддерживать широкий набор типов данных; по этой причине редкие или необычные данные, каких бывает много, становятся серьезным источником затруднений.
Правильно построенный XML
XML-документ считается построенным правильно, если в нем соблюдены правила XML-синтаксиса. Неправильно построенный формат в техническом смысле не является XML-документом. Например, такой HTML-тег, как <br>, в XML неприемлем; соответствующий правильный тег выглядит как <br />. Корневой элемент можно представить себе как бесконечный шкаф с документами. У вас всего один шкаф, но почти нет ограничений на тип и количество его содержимого. В вашем шкафу помещается бесконечное количество ящиков и папок для документов.
Основы PHP
Большинство читателей этой статьи уже работают с РНР, но, возможно, незнакомы с его историей и эволюцией.
О PHP
Hypertext Preprocessor (PHP) — это не зависящий от платформы язык сценариев, используемый для создания динамических Web-страниц и серверных приложений. Он начинался как Personal Home Page/Form Interpreter (PHP/FI) и обрел новую жизнь в руках Сураски (Suraski) и Гутманса (Gutmans), которые в июне 1998 года выпустили РНР3. Их компания Zend Technologies до сих пор управляет разработкой PHP.
В июле 2004 года вышел PHP5 с Zend Engine II и многими новыми функциями, такими как:
Поддержка объектно-ориентированного программирования
Улучшенная поддержка MySQL
Улучшенная поддержка XML
PHP5 и XML
Поддержка XML присутствовала в РНР с самых ранних версий, но в РНР5 она существенно улучшена. Поддержка XML в РНР4 была ограниченной, в частности, предлагался только включенный по умолчанию парсер на базе SAX, а поддержка DOM не соответствовала стандарту W3C. В РНР5 разработчики PHP XML, можно сказать, изобрели колесо, обеспечив соответствие общепринятым стандартам.
Новое в поддержке XML в версии PHP5
PHP5 содержит полностью переписанные и новые расширения, включая парсер SAX, DOM, SimpleXML, XMLReader, XMLWriter и процессор XSLT. Теперь все эти расширения основаны на libxml2.
Наряду с улучшенной по сравнению с PHP4 поддержкой SAX, в РНР5 реализована поддержка DOM в соответствии со стандартом W3C, а также расширение SimpleXML. По умолчанию включены и SAX, и DOM, и SimpleXML. Тем, кто знаком с DOM по другим языкам, станет проще реализовать аналогичную функциональность в РНР
Чтение, обработка и написание XML в PHP5
SimpleXML, при необходимости в сочетании с DOM, — идеальный выбор для чтения, обработки и составления в РНР5 простых, предсказуемых и относительно компактных документов.
Краткий обзор предпочтительных API
Из множества API, присутствующих в РНР5, DOM — самый знакомый, а на SimpleXML проще всего программировать. В типичных ситуациях, таких как те, что мы здесь рассматриваем, они наиболее эффективны.
Расширение DOM
Document Object Model (DOM) — это принятый W3C стандартный набор объектов для документов HTML и XML, стандартная модель сочетания этих объектов и стандартный интерфейс для доступа к ним и манипуляций с ними. Многие поставщики поддерживают DOM в качестве интерфейса к своим специальным структурам данных и API, благодаря чему модель DOM знакома массе разработчиков. DOM легко освоить и применять, так как его структура в памяти напоминает исходный документ XML. Чтобы передать информацию приложению, DOM создает дерево объектов, которое в точности повторяет дерево элементов файла XML, так что каждый элемент XML служит узлом этого дерева. DOM — это парсер, основанный на древовидной структуре. Поскольку DOM строит дерево всего документа, он потребляет много ресурсов памяти и времени процессора. Поэтому анализ очень крупных документов посредством DOM непрактичен из-за проблем производительности. В контексте данной статьи расширение DOM используется главным образом из-за его способности импортировать формат SimpleXML и выводить XML в формате DOM, или, наоборот, для использования в качестве строковых данных или XML-файла.
SimpleXML
Расширение SimpleXML – это предпочтительный инструмент для синтаксического анализа XML. Для его работы требуется РНР5. Оно взаимодействует с DOM при составлении XML-файлов и имеет встроенную поддержку XPath. SimpleXML лучше всего работает с несложными данными типа записей, такими как XML, передаваемый в виде документа или строки из другой части того же приложения. Если XML-документ не слишком сложный, не слишком глубокий и не содержит смешанного контента, для SimpleXML кодировать проще, чем для DOM, как и предполагает название. К тому же он надежнее при работе с известной структурой документа.
Простые примеры
Ниже приведены простые примеры работы с DOM и SimpleXML на компактных XML-файлах.
DOM в действии
Модель DOM, реализованная в PHP5, — это та же спецификация W3C DOM, с которой вы имеете дело в браузере и с которой работаете посредством JavaScript. Используются те же методы, так что способы кодирования покажутся вам знакомыми. Листинг 2 иллюстрирует использование DOM для создания XML-строки и XML-документа, отформатированных в целях читабельности.
Листинг 2. Применение DOM
<?php //Создает XML-строку и XML-документ при помощи DOM $dom = new DomDocument('1.0'); //добавление корня - <books> $books = $dom->appendChild($dom->createElement('books')); //добавление элемента <book> в <books> $book = $books->appendChild($dom->createElement('book')); // добавление элемента <title> в <book> $title = $book->appendChild($dom->createElement('title')); // добавление элемента текстового узла <title> в <title> $title->appendChild( $dom->createTextNode('Great American Novel')); //генерация xml $dom->formatOutput = true; // установка атрибута formatOutput // domDocument в значение true // save XML as string or file $test1 = $dom->saveXML(); // передача строки в test1 $dom->save('test1.xml'); // сохранение файла ?>
Это приводит к созданию выходного файла, приведенного в листинге 3.
Листинг 3. Выходной файл
<?xml version="1.0"?> <books> <book> <title>Great American Novel</title> </book> </books>
Листинг 4 импортирует объект SimpleXMLElement в объект DOMElement, иллюстрируя взаимодействие DOM и SimpleXML.
Листинг 4. Взаимодействие, часть 1 - DOM импортирует SimpleXML
<?php $sxe = simplexml_load_string('<books><book><title>'. 'Great American Novel</title></book></books>'); if ($sxe === false) { echo 'Error while parsing the document'; exit; } $dom_sxe = dom_import_simplexml($sxe); if (!$dom_sxe) { echo 'Error while converting XML'; exit; } $dom = new DOMDocument('1.0'); $dom_sxe = $dom->importNode($dom_sxe, true); $dom_sxe = $dom->appendChild($dom_sxe); echo $dom->save('test2.xml'); ?>
Функция из листинга 5 берет узел документа DOM и превращает его в узел SimpleXML. Затем этот новый объект можно использовать в качестве «родного» элемента SimpleXML. В случае любой ошибки возвращается значение FALSE.
Листинг 5. Взаимодействие, часть 2 - SimpleXML импортирует DOM
<?php $dom = new domDocument; $dom->loadXML('<books><book><title>Great American Novel</title></book></books>'); if (!$dom) { echo 'Error while parsing the document'; exit; } $s = simplexml_import_dom($dom); echo $s->book[0]->title; // Great American Novel ?>
SimpleXML в действии
Расширение SimpleXML – это предпочтительный инструмент для синтаксического анализа XML. Оно взаимодействует с DOM при составлении XML-файлов и имеет встроенную поддержку XPath. Для SimpleXML проще писать код, чем для DOM, как и предполагает его название.
Для тех, кто не знаком с РНР, листинг 6 форматирует тестовый XML-файл как include-файл в целях читабельности.
Листинг 6. В следующих примерах тестовый XML-файл отформатирован как include-файл РНР с именем example.php.
<?php $xmlstr = <<<XML <books> <book> <title>Great American Novel</title> <characters> <character> <name>Cliff</name> <desc>really great guy</desc> </character> <character> <name>Lovely Woman</name> <desc>matchless beauty</desc> </character> <character> <name>Loyal Dog</name> <desc>sleepy</desc> </character> </characters> <plot> Cliff meets Lovely Woman. Loyal Dog sleeps, but wakes up to bark at mailman. </plot> <success type="bestseller">4</success> <success type="bookclubs">9</success> </book> </books> XML; ?>
В гипотетическом Ajax-приложении может, например, понадобиться извлечь из XML-документа почтовый индекс и обратиться к базе данных. В листинге 7 из XML-файла предыдущего примера извлекается элемент <plot>.
Листинг 7. Извлечение узла – как легко его получить?
<?php include 'example.php'; $xml = new SimpleXMLElement($xmlstr); echo $xml->book[0]->plot; // "Cliff meets Lovely Woman. ..." ?>
С другой стороны, может понадобиться извлечь многострочный адрес. Когда у одного родительского элемента несколько экземпляров дочернего элемента, применяется обычная методика итерирования. Эта функциональность показана в листинге 8.
Листинг 8. Извлечение нескольких экземпляров элемента
<?php include 'example.php'; $xml = new SimpleXMLElement($xmlstr); /* For each <book> node, echo a separate <plot>. */ foreach ($xml->book as $book) { echo $book->plot, '<br />'; } ?>
Кроме чтения имен элементов и их значений, SimpleXML может также обращаться к атрибутам элемента. В листинге 9 производится обращение к атрибутам элемента; это делается точно так же, как обращение к элементам массива.
Листинг 9. Демонстрация обращения SimpleXML к атрибутам элемента
<?php //Повторяем входной XML-файл повторяется для наглядности $xmlstr = <<<XML <?xml version='1.0' standalone='yes'?> <books> <book> <title>Great American Novel</title> <characters> <character> <name>Cliff</name> <desc>really great guy</desc> </character> <character> <name>Lovely Woman</name> <desc>matchless beauty</desc> </character> <character> <name>Loyal Dog</name> <desc>sleepy</desc> </character> </characters> <plot> Cliff meets Lovely Woman. Loyal Dog sleeps, but wakes up to bark at mailman. </plot> <success type="bestseller">4</success> <success type="bookclubs">9</success> </book> </books> XML; ?> <?php include 'example.php'; $xml = new SimpleXMLElement($xmlstr); /* Access the <success> nodes of the first book. * Output the success indications, too. */ foreach ($xml->book[0]->success as $success) { switch((string) $success['type']) { // Get attributes as element indices case 'bestseller': echo $success, ' months on bestseller list'; break; case 'bookclubs': echo $success, ' bookclub listings'; break; } } ?>
Чтобы сравнить элемент или атрибут со строкой или передать его функции, которой требуется строка, нужно преобразовать его в строку при помощи оператора (string). Иначе, по умолчанию, РНР рассматривает элемент как объект (листинг 10).
Листинг 10. Преобразуй в строку, или проиграешь
<?php include 'example.php'; $xml = new SimpleXMLElement($xmlstr); if ((string) $xml->book->title == 'Great American Novel') { print 'My favorite book.'; } htmlentities((string) $xml->book->title); ?>
Данные в SimpleXML не обязаны быть неизменными. Листинг 11 выводит новый XML-документ, точную копию исходного, за исключением того, что в новом имя Cliff изменено на Big Cliff.
Листинг 11. Изменение текстового узла с помощью SimpleXML
<?php $xmlstr = <<<XML <?xml version='1.0' standalone='yes'?> <books> <book> <title>Great American Novel</title> <characters> <character> <name>Cliff</name> <desc>really great guy</desc> </character> <character> <name>Lovely Woman</name> <desc>matchless beauty</desc> </character> <character> <name>Loyal Dog</name> <desc>sleepy</desc> </character> </characters> <plot> Cliff meets Lovely Woman. Loyal Dog sleeps, but wakes up to bark at mailman. </plot> <success type="bestseller">4</success> <success type="bookclubs">9</success> </book> </books> XML; ?> <?php include 'example.php'; $xml = new SimpleXMLElement($xmlstr); $xml->book[0]->characters->character[0]->name = 'Big Cliff'; echo $xml->asXML(); ?>
Начиная с версии PHP 5.1.3 SimpleXML дополнен возможностью легко добавлять дочерние элементы и атрибуты. Листинг 12 выводит XML-документ, основанный на исходном, но с добавленным новым персонажем и описанием.
Листинг 12. Добавление дочерних и текстовых узлов с помощью SimpleXML
<?php $xmlstr = <<<XML <?xml version='1.0' standalone='yes'?> <books> <book> <title>Great American Novel</title> <characters> <character> <name>Cliff</name> <desc>really great guy</desc> </character> <character> <name>Lovely Woman</name> <desc>matchless beauty</desc> </character> <character> <name>Loyal Dog</name> <desc>sleepy</desc> </character> <character> <name>Yellow Cat</name> <desc>aloof</desc> </character> </characters> <plot> Cliff meets Lovely Woman. Loyal Dog sleeps, but wakes up to bark at mailman. </plot> <success type="bestseller">4</success> <success type="bookclubs">9</success> </book> </books> XML; ?> <?php include 'example.php'; $xml = new SimpleXMLElement($xmlstr); $character = $xml->book[0]->characters->addChild('character'); $character->addChild('name', 'Yellow Cat'); $character->addChild('desc', 'aloof'); $success = $xml->book[0]->addChild('success', '2'); $success->addAttribute('type', 'reprints'); echo $xml->asXML(); ?>