Журнал LinuxFormat - перейти на главную

LXF71:PHP

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
м (викификация)
Строка 7: Строка 7:
 
Цезарю, звучит так: «Beati Hispani quibus vivere bibere est». В
 
Цезарю, звучит так: «Beati Hispani quibus vivere bibere est». В
 
свободном переводе это значит: «блаженны испанцы, для
 
свободном переводе это значит: «блаженны испанцы, для
которых жить – значит пить». И кто с этим не согласится? Но мало кто
+
которых жить — значит пить». И кто с этим не согласится? Но мало кто
 
знает, что эта фраза очень похоже прозвучит в современной Испании.
 
знает, что эта фраза очень похоже прозвучит в современной Испании.
там вы можете сказать “Dichosos los espaсoles, para quienes vivir es
+
там вы можете сказать «Dichosos los espaсoles, para quienes vivir es
beber.Любой, хорошо знакомый с испанским языком, может посмотреть на фразу на латыни и догадаться о её значении, благодаря тому
+
beber.» Любой, хорошо знакомый с испанским языком, может посмотреть на фразу на латыни и догадаться о её значении, благодаря тому
факту что испанский язык относится к романской группе – не из-за своей романтичности, а потому что он происходит от разговорного латинского языка, на котором говорили римляне (Romans) много лет назад.
+
факту что испанский язык относится к романской группе — не из-за своей романтичности, а потому что он происходит от разговорного латинского языка, на котором говорили римляне (Romans) много лет назад.
  
 
Удивительно, но в компьютерных науках наблюдается такая же степень стандартизации. Несмотря на то, что мы никак не можем договориться, сколько кнопок должно быть на мышке, мы при этом способны
 
Удивительно, но в компьютерных науках наблюдается такая же степень стандартизации. Несмотря на то, что мы никак не можем договориться, сколько кнопок должно быть на мышке, мы при этом способны
 
создавать системы для обмена данными в понятной форме. XML (the
 
создавать системы для обмена данными в понятной форме. XML (the
eXtensible Markup Language, расширяемый язык разметки) это формат
+
eXtensible Markup Language, расширяемый язык разметки) — это формат
 
данных, основанный на текстовом представлении, позволяющий легко
 
данных, основанный на текстовом представлении, позволяющий легко
 
читать и сохранять данные, а так же делиться ими с другими, в том числе и через Интернет. как латынь и языки романской группы, разные XML-схемы (правила, по которым создаются XML-файлы) отличаются друг от
 
читать и сохранять данные, а так же делиться ими с другими, в том числе и через Интернет. как латынь и языки романской группы, разные XML-схемы (правила, по которым создаются XML-файлы) отличаются друг от
 
друга, но не настолько, чтобы другие стали непонятными, если вам знакома только одна из них. В сегодняшней статье мы рассмотрим XML для
 
друга, но не настолько, чтобы другие стали непонятными, если вам знакома только одна из них. В сегодняшней статье мы рассмотрим XML для
 
сохранения кроссвордов: мы будем читать их, записывать и показывать
 
сохранения кроссвордов: мы будем читать их, записывать и показывать
на экране. Мы не собираемся их разгадывать – мы займёмся совершенно другой проблемой.
+
на экране. Мы не собираемся их разгадывать — мы займёмся совершенно другой проблемой.
  
 
=== Зачем использовать XML ===
 
=== Зачем использовать XML ===
Даже теперь, когда XML присутствует везде, мы всё еще видим разнообразие форматов. Причина этого проста – XML несовершенен. он очень
+
Даже теперь, когда XML присутствует везде, мы всё еще видим разнообразие форматов. Причина этого проста — XML несовершенен. он очень
 
многословен, он не имеет строгой типизации, он понятен для человека и
 
многословен, он не имеет строгой типизации, он понятен для человека и
 
поэтому заметно медленнее бинарных форматов данных. Но у него есть
 
поэтому заметно медленнее бинарных форматов данных. Но у него есть
свои достоинства. Главные из них – возможность проверить корректность файла, не имея представления о его схеме, поддержка Unicode,
+
свои достоинства. Главные из них — возможность проверить корректность файла, не имея представления о его схеме, поддержка Unicode,
 
возможности самодокументирования и жёсткие синтаксические правила,
 
возможности самодокументирования и жёсткие синтаксические правила,
 
позволяющие быстро и безошибочно читать XML-файл.
 
позволяющие быстро и безошибочно читать XML-файл.
Строка 53: Строка 53:
 
</requiem></source>
 
</requiem></source>
 
В этом примере корневым элементом является requiem. он содержит два элемента line, в которых в свою очередь находятся latin и
 
В этом примере корневым элементом является requiem. он содержит два элемента line, в которых в свою очередь находятся latin и
english – как видите, тут нет ничего мудрёного. Мы можем разобрать
+
english — как видите, тут нет ничего мудрёного. Мы можем разобрать
 
этот файл и напечатать результат разбора с помощью двух строк на PHP:
 
этот файл и напечатать результат разбора с помощью двух строк на PHP:
 
<source lang="php"><?php
 
<source lang="php"><?php
Строка 81: Строка 81:
 
}
 
}
 
}</source>
 
}</source>
Понимание этой структуры – это ключ к использованию SimpleXML.
+
Понимание этой структуры — это ключ к использованию SimpleXML.
  
 
Корневой объект SimpleXMLElement содержит только одну переменную, line. О на является массивом из двух элементов (с номерами 0
 
Корневой объект SimpleXMLElement содержит только одну переменную, line. О на является массивом из двух элементов (с номерами 0
Строка 95: Строка 95:
 
}
 
}
 
?></source>
 
?></source>
Обратите внимание на то, как мы используем $file->line и $line->english. Т ак как $file и $line – объекты, мы можем получить доступ к
+
Обратите внимание на то, как мы используем $file->line и $line->english. Т ак как $file и $line — объекты, мы можем получить доступ к
 
их переменным при помощи оператора ->. Ничего не мешает вам трактовать объекты как массивы. Например, использовав их внутри цикла
 
их переменным при помощи оператора ->. Ничего не мешает вам трактовать объекты как массивы. Например, использовав их внутри цикла
 
foreach, можно перебирать все поля объекта по одному, словно это элементы массива. Но лучше всё же мысленно различать объекты и массивы, так как SimpleXML использует «массивоподобный» метод для представления атрибутов. Например, если изменить первую строчку нашего
 
foreach, можно перебирать все поля объекта по одному, словно это элементы массива. Но лучше всё же мысленно различать объекты и массивы, так как SimpleXML использует «массивоподобный» метод для представления атрибутов. Например, если изменить первую строчку нашего
Строка 133: Строка 133:
 
print $park->asXML();
 
print $park->asXML();
 
?></source>
 
?></source>
Этот сценарий превращает парк с белками в обычный набор объектов и массивов, получает доступ к одной из белок (второй, так как элементы нумеруются начиная с нуля, то есть squirrel[1] это Nick) и изменяет число её орехов. Важной частью является вызов asXML, так как он
+
Этот сценарий превращает парк с белками в обычный набор объектов и массивов, получает доступ к одной из белок (второй, так как элементы нумеруются начиная с нуля, то есть squirrel[1] — это Nick) и изменяет число её орехов. Важной частью является вызов asXML, так как он
 
позволяет превратить наши объекты снова в XML, и это лучший XML чем
 
позволяет превратить наши объекты снова в XML, и это лучший XML чем
 
тот, с которого мы начинали:
 
тот, с которого мы начинали:
Строка 145: Строка 145:
 
</squirrel>
 
</squirrel>
 
</park></source>
 
</park></source>
Nick обзавёлся орехами, а весь файл – заголовком с обозначением
+
Nick обзавёлся орехами, а весь файл — заголовком с обозначением
 
версии, которого у нас не было раньше. Метод asXML доступен для
 
версии, которого у нас не было раньше. Метод asXML доступен для
 
любого объекта SimpleXMLElement, так что вы можете получить XML
 
любого объекта SimpleXMLElement, так что вы можете получить XML
Строка 156: Строка 156:
  
 
Для того, чтобы увеличить возможности XML, было разработано
 
Для того, чтобы увеличить возможности XML, было разработано
множество стандартов, самый известный из которых – Xpath. он делает
+
множество стандартов, самый известный из которых — Xpath. он делает
XML более похожим на SQL – теперь вы можете не просто использовать
+
XML более похожим на SQL — теперь вы можете не просто использовать
 
его для хранения информации, но и выполнять запросы. По своей сути
 
его для хранения информации, но и выполнять запросы. По своей сути
Xpath – это способ вытащить нужную часть XML, указав путь к ней (похожий на путь к файлу в файловой системе). В PHP это достигается возвращением массива объектов SimpleXMLElement, соответствующих
+
Xpath — это способ вытащить нужную часть XML, указав путь к ней (похожий на путь к файлу в файловой системе). В PHP это достигается возвращением массива объектов SimpleXMLElement, соответствующих
 
вашему запросу.
 
вашему запросу.
  
Строка 189: Строка 189:
 
?></source>
 
?></source>
 
Запрос «//book» ищет элемент <book> в любом месте дерева XML.
 
Запрос «//book» ищет элемент <book> в любом месте дерева XML.
Часть запроса // значит “искать везде”, что для функции xpath значит
+
Часть запроса // значит «искать везде», что для функции xpath значит
 
«найти все корневые элементы <book>, найти все <book> внутри элемента, содержащего <author>, найти все <book> внутри элементов <author>»,
 
«найти все корневые элементы <book>, найти все <book> внутри элемента, содержащего <author>, найти все <book> внутри элементов <author>»,
 
то есть найти <book> вообще везде, и вернуть все найденные элементы
 
то есть найти <book> вообще везде, и вернуть все найденные элементы
Строка 211: Строка 211:
 
</book>
 
</book>
 
</library></source>
 
</library></source>
теперь элементов <book> у нас стало больше, но они разные – элементы внутри <author> обозначают, какие книги этими авторами были
+
теперь элементов <book> у нас стало больше, но они разные — элементы внутри <author> обозначают, какие книги этими авторами были
написаны, а <book> внутри < library> - это список книг, имеющихся в библиотеке. Если по-прежнему использовать строку поиска //book, то мы
+
написаны, а <book> внутри < library> — это список книг, имеющихся в библиотеке. Если по-прежнему использовать строку поиска //book, то мы
 
получим их все, вне зависимости от того, есть эта книга в библиотеке
 
получим их все, вне зависимости от того, есть эта книга в библиотеке
 
или нет.
 
или нет.
Строка 230: Строка 230:
 
}
 
}
 
?></source>
 
?></source>
XML нельзя назвать быстрым, но XPath – уже можно, так как в
+
XML нельзя назвать быстрым, но XPath — уже можно, так как в
 
момент его использования XML преобразован во внутреннее представление, поиск по которому происходит сравнительно легко.
 
момент его использования XML преобразован во внутреннее представление, поиск по которому происходит сравнительно легко.
  
Строка 245: Строка 245:
 
?></source>
 
?></source>
 
ключевая часть запроса заключена в квадратные скобки. Мы указываем, что хотим получить элемент author, вложенный в books, а в квадратных скобках содержится фильтр, которому должны удовлетворять
 
ключевая часть запроса заключена в квадратные скобки. Мы указываем, что хотим получить элемент author, вложенный в books, а в квадратных скобках содержится фильтр, которому должны удовлетворять
наши элементы. @nationality обоозначает “Выбрать только такие элементы, у которых параметр nationality...” и далее написано условие
+
наши элементы. @nationality обоозначает «Выбрать только такие элементы, у которых параметр nationality…» и далее написано условие
=British”. Это условие позволяет ограничить поиск только английскими
+
«=British». Это условие позволяет ограничить поиск только английскими
 
авторами. Символ @ имеет важное значение, без него условие применялось бы к вложенным элементам, а не к атрибутам. Например, запрос
 
авторами. Символ @ имеет важное значение, без него условие применялось бы к вложенным элементам, а не к атрибутам. Например, запрос
books/author[book=”Cien anos de soledad”] вернёт “Gabriel Garcia Marquez”.
+
books/author[book="Cien anos de soledad"] вернёт «Gabriel Garcia Marquez».
  
 
кроме проверки на равенство вы можете использовать стандартные
 
кроме проверки на равенство вы можете использовать стандартные
Строка 255: Строка 255:
 
<source lang="php">$club_1830_eligible = $holidaymakers->XPath('/books/author[@age>=18 or @age<=30]');</source>
 
<source lang="php">$club_1830_eligible = $holidaymakers->XPath('/books/author[@age>=18 or @age<=30]');</source>
 
кроме того, условия можно заключать в скобки, примерно так:
 
кроме того, условия можно заключать в скобки, примерно так:
/books/author[@age>=18 and (@name=”Jim” or @name=”Bob”)].
+
/books/author[@age>=18 and (@name="Jim" or @name="Bob")].
  
 
кроме операторов сравнения, в условиях XPath доступны простые
 
кроме операторов сравнения, в условиях XPath доступны простые
Строка 277: Строка 277:
 
Sudoku). так что задачи практически совпадают.
 
Sudoku). так что задачи практически совпадают.
  
Существует два способа хранения таких данных. “Чистое” решение
+
Существует два способа хранения таких данных. «Чистое» решение -
это хранение каждой клеточки в отдельном XML-элементе. Другой вариант – сделать один элемент в XML-файле, который состоит из символов,
+
это хранение каждой клеточки в отдельном XML-элементе. Другой вариант — сделать один элемент в XML-файле, который состоит из символов,
 
образующих сетку. Давайте рассмотрим сначала второй вариант как
 
образующих сетку. Давайте рассмотрим сначала второй вариант как
 
более простой:
 
более простой:
Строка 295: Строка 295:
 
и затем разобрать его посимвольно. Но это не очень хорошее решение.
 
и затем разобрать его посимвольно. Но это не очень хорошее решение.
 
как хранить номера слов в кроссворде? как добавить вопросы? как
 
как хранить номера слов в кроссворде? как добавить вопросы? как
сохранять состояние решения? Для решения всех этих проблем нам потребуется “правильный” XML, в котором каждый квадратик описывается
+
сохранять состояние решения? Для решения всех этих проблем нам потребуется «правильный» XML, в котором каждый квадратик описывается
 
отдельным элементом. Эти квадратные элементы могут иметь следующие атрибуты:
 
отдельным элементом. Эти квадратные элементы могут иметь следующие атрибуты:
* type– тип квадратика, black(чёрный) или white (белый).
+
* type- тип квадратика, black(чёрный) или white (белый).
* number– номер слова (если есть)
+
* number- номер слова (если есть)
* direction– направление слова, down (вниз), across (вправо) или both (оба), если есть.
+
* direction- направление слова, down (вниз), across (вправо) или both (оба), если есть.
* downclue– вопрос для слова, идущего вниз, если есть.
+
* downclue- вопрос для слова, идущего вниз, если есть.
* acrossclue– вопрос для слова, идущего вправо, если есть.
+
* acrossclue- вопрос для слова, идущего вправо, если есть.
* correctanswer– буква, которая должна быть тут.
+
* correctanswer- буква, которая должна быть тут.
* currentanswer– буква, которую сюда написал отгадчик.
+
* currentanswer- буква, которую сюда написал отгадчик.
* guessedanswer– буква, в которой отгадчик не уверен.
+
* guessedanswer- буква, в которой отгадчик не уверен.
 
Мы отсортируем элементы так, что сначала укажем первую строчку,
 
Мы отсортируем элементы так, что сначала укажем первую строчку,
потом вторую и так далее. Элемент <grid> будет хранить свойства – автора, сложность (номер от 1 до 4, 4 – самый сложный), и размер сетки ( 6
+
потом вторую и так далее. Элемент <grid> будет хранить свойства — автора, сложность (номер от 1 до 4, 4 — самый сложный), и размер сетки (6
 
значит шесть квадратиков вправо и вниз).
 
значит шесть квадратиков вправо и вниз).
  
Строка 337: Строка 337:
 
Вложенный в цикл второй оператор if отвечает за правильную расстановку символов перевода строки. Элементы XML загружаются в
 
Вложенный в цикл второй оператор if отвечает за правильную расстановку символов перевода строки. Элементы XML загружаются в
 
порядке очереди, так что нам нужно прервать строку после символа
 
порядке очереди, так что нам нужно прервать строку после символа
номер $crossword[“size”] или любого, кратного ему.
+
номер $crossword["size"] или любого, кратного ему.
  
 
И это всё? Да, нашей целью было только отображение кроссворда,
 
И это всё? Да, нашей целью было только отображение кроссворда,
а не программа для их решения или генерации (подсказка – для этого
+
а не программа для их решения или генерации (подсказка — для этого
 
можно воспользоваться одним из множества бесплатных словарей в
 
можно воспользоваться одним из множества бесплатных словарей в
 
web), и у нас нет красивого графического интерфейса пользователя. я
 
web), и у нас нет красивого графического интерфейса пользователя. я

Версия 19:58, 6 декабря 2008

Содержание

SimpleXML и XPath

Пол Хадсон (Paul Hudson) вновь пытается помочь с решением загадки Sudoku и погружается в изучение XML для дела и удовольствия.

одна из многих латинских поговорок, приписываемых юлию Цезарю, звучит так: «Beati Hispani quibus vivere bibere est». В свободном переводе это значит: «блаженны испанцы, для которых жить — значит пить». И кто с этим не согласится? Но мало кто знает, что эта фраза очень похоже прозвучит в современной Испании. там вы можете сказать «Dichosos los espaсoles, para quienes vivir es beber.» Любой, хорошо знакомый с испанским языком, может посмотреть на фразу на латыни и догадаться о её значении, благодаря тому факту что испанский язык относится к романской группе — не из-за своей романтичности, а потому что он происходит от разговорного латинского языка, на котором говорили римляне (Romans) много лет назад.

Удивительно, но в компьютерных науках наблюдается такая же степень стандартизации. Несмотря на то, что мы никак не можем договориться, сколько кнопок должно быть на мышке, мы при этом способны создавать системы для обмена данными в понятной форме. XML (the eXtensible Markup Language, расширяемый язык разметки) — это формат данных, основанный на текстовом представлении, позволяющий легко читать и сохранять данные, а так же делиться ими с другими, в том числе и через Интернет. как латынь и языки романской группы, разные XML-схемы (правила, по которым создаются XML-файлы) отличаются друг от друга, но не настолько, чтобы другие стали непонятными, если вам знакома только одна из них. В сегодняшней статье мы рассмотрим XML для сохранения кроссвордов: мы будем читать их, записывать и показывать на экране. Мы не собираемся их разгадывать — мы займёмся совершенно другой проблемой.

Зачем использовать XML

Даже теперь, когда XML присутствует везде, мы всё еще видим разнообразие форматов. Причина этого проста — XML несовершенен. он очень многословен, он не имеет строгой типизации, он понятен для человека и поэтому заметно медленнее бинарных форматов данных. Но у него есть свои достоинства. Главные из них — возможность проверить корректность файла, не имея представления о его схеме, поддержка Unicode, возможности самодокументирования и жёсткие синтаксические правила, позволяющие быстро и безошибочно читать XML-файл.

В общем, XML силён несмотря на все свои проблемы, потому что он позволяет легко обмениваться данными между различными программами, в том числе и через Интернет. Используя его для сохранения кроссвордов, мы можем быть уверены что при желании другие программы смогу легко воспользоваться ими. Если вы читали о возможностях технологии Ajax, то вы уже знаете о возможностях, которые даёт XML при программировании на стороне клиента. Если у вас нет каких-то специфических требований, для большинства проектов XML подходит лучше всего.

создание простого XML

Поддержка XML в PHP находится в очень неустойчивом положении. С момента её появления было создано множество разных реализаций, несколько переработок и серия расширений, предназначенных для чтения и записи XML. Последнее из них, SimpleXML (простой XML), так называется потому, что предназначено для представления XML-файла в виде простых переменных PHP.

Вот пример XML-файла под названием requiem.xml:

<requiem>
<line>
<latin>Confutatis maledictis</latin>
<english>When the wicked are confounded</english>
</line>
<line>
<latin>Flammis acribus addictis</latin>
<english>Doomed to flames of woe unbounded</english>
</line>
</requiem>

В этом примере корневым элементом является requiem. он содержит два элемента line, в которых в свою очередь находятся latin и english — как видите, тут нет ничего мудрёного. Мы можем разобрать этот файл и напечатать результат разбора с помощью двух строк на PHP:

<?php
$file = simplexml_load_file("requiem.xml");
var_dump($file);
?>

Функция var_dump даёт прекрасную возможность разобраться, как что-то работает в PHP, так как она позволяется наглядно увидеть данные, содержащиеся в переменной. Вот, что выводит наша программа:

object(SimpleXMLElement)#1 (1) {
["line"]=>
array(2) {
[0]=>
object(SimpleXMLElement)#2 (2) {
["latin"]=>
string(21) "Confutatis maledictis"
["english"]=>
string(30) "When the wicked are confounded"
}
[1]=>
object(SimpleXMLElement)#3 (2) {
["latin"]=>
string(25) "Flammis acribus addictis"
["english"]=>
string(33) "Doomed to flames of woe unbounded"
}
}
}

Понимание этой структуры — это ключ к использованию SimpleXML.

Корневой объект SimpleXMLElement содержит только одну переменную, line. О на является массивом из двух элементов (с номерами 0 и 1), соответствующих двум элементам <line> в исходном файле. Эти элементы, в свою очередь, являются объектами SimpleXMLElement, содержащими строковые переменные latin и english с соответствующими данными из исходного XML. Итак, вызов функции simplexml_load_file вернул нам смесь из объектов и массивов, соответствующую структуре XML-файла и содержащий все его данные в обычных переменных php. Вывод var_dump познавателен, но непригоден для нормальной работы, так что давайте перепишем нашу программу так, чтобы она печатала только английский текст:

<?php
$file = simplexml_load_file("requiem.xml");
foreach($file->line as $line) {
echo $line->english, "\n";
}
?>

Обратите внимание на то, как мы используем $file->line и $line->english. Т ак как $file и $line — объекты, мы можем получить доступ к их переменным при помощи оператора ->. Ничего не мешает вам трактовать объекты как массивы. Например, использовав их внутри цикла foreach, можно перебирать все поля объекта по одному, словно это элементы массива. Но лучше всё же мысленно различать объекты и массивы, так как SimpleXML использует «массивоподобный» метод для представления атрибутов. Например, если изменить первую строчку нашего XML следующим образом:

<requiem key=”D”>

то теперь с разобранным файлом вы можете сделать следующее:

$file = simplexml_load_file("requiem.xml");
print $file["key"];

Этот код отобразит значение атрибута key корневого элемента. Синтаксис $file->key не сработает, так как он обозначает обращение ко вложенному элементу <key>, которого не существует. Да, это похоже на чёрную магию, мы привыкли к тому что операторы массивов работают по другому. Всё дело в том, что здесь приходится работать с объектами SimpleXMLElement, а не с обычными плоскими массивами.

XML на входе, объекты на выходе

Как вы только что могли убедиться, SimpleXML позволяет вам работать с XML-файлами так же, как с объектами и массивами, так что вы можете игнорировать семантику XML и сосредоточиться на правильной обработке данных. Эта простота использования простирается вплоть до возможности изменять значения переменных, поскольку раз уж у вас есть объекты и массивы php, то вы можете делать с ними всё, что пожелаете. Что более интересно, после внесения изменений вы можете экспортировать результат обратно в XML. Для демонстрации этого мы используем новый XML файл:

<park>
<squirrel name=”Squirly”>
<nuts>320</nuts>
</squirrel>
<squirrel name=”Nick”>
<nuts>0</nuts>
</squirrel>
</park>

И так, в нашем парке (park) есть две белки (squirrel) по имени Squirly и Nick. У Squirly много орехов (nuts), а у Nick’а ничего нет. Чтобы это исправить, воспользуемся умением SimpleXML изменять значения переменных на лету. Сделать это действительно просто:

<?php
$park = simplexml_load_file("squirrels.xml");
$park->squirrel[1]->nuts = 10;
print $park->asXML();
?>

Этот сценарий превращает парк с белками в обычный набор объектов и массивов, получает доступ к одной из белок (второй, так как элементы нумеруются начиная с нуля, то есть squirrel[1] — это Nick) и изменяет число её орехов. Важной частью является вызов asXML, так как он позволяет превратить наши объекты снова в XML, и это лучший XML чем тот, с которого мы начинали:

<?xml version=”1.0”?>
<park>
<squirrel name=”Squirly”>
<nuts>320</nuts>
</squirrel>
<squirrel name=”Nick”>
<nuts>10</nuts>
</squirrel>
</park>

Nick обзавёлся орехами, а весь файл — заголовком с обозначением версии, которого у нас не было раньше. Метод asXML доступен для любого объекта SimpleXMLElement, так что вы можете получить XML одного только Nick-а при помощи $park->squirrel[1]->asXML(). Обратите внимание: при использовании asXML для любого другого объекта, кроме корневого, XML-заголовок с версией не вставляется, вы получите только кусочек XML-кода. После получения текстовой строки в нужном формате воспользуйтесь функцией file_put_contents для сохранения её в файле по вашему выбору.

Для того, чтобы увеличить возможности XML, было разработано множество стандартов, самый известный из которых — Xpath. он делает XML более похожим на SQL — теперь вы можете не просто использовать его для хранения информации, но и выполнять запросы. По своей сути Xpath — это способ вытащить нужную часть XML, указав путь к ней (похожий на путь к файлу в файловой системе). В PHP это достигается возвращением массива объектов SimpleXMLElement, соответствующих вашему запросу.

первые шаги по Xpath

Представляю вам новый XML, books.xml:

<books>
<author nationality=”British” name=”Jane Austen”>
<book>Pride And Prejudice</book>
<book>Sense And Sensibility</book>
</author>
<author nationality=”Colombian” name=”Gabriel Garcia Marquez”>
<book>Cien anos de soledad</book>
<book>El coronel no tiene quien le escriba</book>
</author>
<author nationality=”British” name=”David Baddiel”>
<book>Time For Bed</book>
<book>The Secret Purposes</book>
</author>
</books>

он содержит трёх авторов (<author>), из которых двое англичане, и для всех троих указаны их книги. С помощью Xpath мы можем запросить список всех книг:

<?php
$authors = simplexml_load_file("books.xml");
$books = $authors->XPath("//book");
foreach($books as $book) {
echo $book, "\n";
}
?>

Запрос «//book» ищет элемент <book> в любом месте дерева XML. Часть запроса // значит «искать везде», что для функции xpath значит «найти все корневые элементы <book>, найти все <book> внутри элемента, содержащего <author>, найти все <book> внутри элементов <author>», то есть найти <book> вообще везде, и вернуть все найденные элементы в одном массиве. Затем мы передаём полученный массив в цикл foreach и печатаем его. Есть один любопытный момент: если вместо print использовать var_dump($book), вы увидите, что в переменной на самом деле хранится SimpleXMLElement. Это часть той же магии, которая позволяет работать с объектами SimpleXML как с массивами. В нашем случае это магия функции __tostring(), позволяющая использовать объект в операторе print как обычную строку.

Если вы хотите добыть конкретную книгу, Xpath поможет вам и в этом. Например, добавьте следующий код к books.xml сразу перед строкой </books>:

<library name=”British Library”>
<book>
<title>The Peloponnesian War</title>
<author>Donald Kagan</title>
</book>
<book>
<title>The Peloponnesian War</title>
<author>Thucidydes</author>
</book>
</library>

теперь элементов <book> у нас стало больше, но они разные — элементы внутри <author> обозначают, какие книги этими авторами были написаны, а <book> внутри < library> — это список книг, имеющихся в библиотеке. Если по-прежнему использовать строку поиска //book, то мы получим их все, вне зависимости от того, есть эта книга в библиотеке или нет.

Для выполнения более точного поиска можно воспользоваться иерархией XML:

$books = $authors->XPath("/books/library/book");

Этот запрос вернёт все книги в библиотеке, не затронув книги авторов. Помните, что он по прежнему возвращает массив из SimpleXMLElement, то есть вы получите объекты со свойствами title и author. Чтобы получить только заголовки, можно воспользоваться запросом /books/library/book/title, но если нам надо и заголовки и авторов, то лучше запросить именно книги, а затем воспользоваться объектно-ориентированным синтаксисом:

<?php
$all_books = simplexml_load_file("books.xml");
$library_books = $all_books->XPath("/books/library/book");
foreach($library_books as $book) {
echo "{$book->title} was written by {$book->author}\n";
}
?>

XML нельзя назвать быстрым, но XPath — уже можно, так как в момент его использования XML преобразован во внутреннее представление, поиск по которому происходит сравнительно легко.

разделяй и запрашивай

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

<?php
$all_books = simplexml_load_file("books.xml");
$british_authors = $all_books->XPath('/books/author[@nationality="British"]');
foreach($british_authors as $author) {
echo "{$author["name"]} is British.\n";
}
?>

ключевая часть запроса заключена в квадратные скобки. Мы указываем, что хотим получить элемент author, вложенный в books, а в квадратных скобках содержится фильтр, которому должны удовлетворять наши элементы. @nationality обоозначает «Выбрать только такие элементы, у которых параметр nationality…» и далее написано условие «=British». Это условие позволяет ограничить поиск только английскими авторами. Символ @ имеет важное значение, без него условие применялось бы к вложенным элементам, а не к атрибутам. Например, запрос books/author[book="Cien anos de soledad"] вернёт «Gabriel Garcia Marquez».

кроме проверки на равенство вы можете использовать стандартные условия <, >, <=, >= и !=, а так же соединять несколько условий при помощи or или and.

$club_1830_eligible = $holidaymakers->XPath('/books/author[@age>=18 or @age<=30]');

кроме того, условия можно заключать в скобки, примерно так: /books/author[@age>=18 and (@name="Jim" or @name="Bob")].

кроме операторов сравнения, в условиях XPath доступны простые математические действия. В их число входят +, -, *, div и mod. Вот несколько примеров Xpath-запросов с фильтрованием:

$blessed = $people->XPath('//person[nationality="Spanish"]');
$meaning_of_life = $earth->XPath('//monkeys[@favouritenumber = 7 * 6]');
$a_grade_freshmen = $university->XPath('/students/student[@year = 1 and grades > 80.0'];
$dangerous = $people->XPath('//adults[@iq = @shoesize]'); // обратите внимание, мы можем сравнивать значение атрибутов между собой
$squirrels_with_comedy _oversized_tails = $animals->XPath('//squirrels[@tail > @body-length * 6]');
$offenders = $people->XPath('//people/[@outstanding_penalty = true()]'); //возвращает записи, имеющие параметр outstanding_penalty, пустой или с любым значением
$good_wines = $wines->XPath('/drinks/alcoholic/wines/wine[@year mod 2 = 1 and (@country="Australia" or @country="France")]');

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

Кроссворды

теперь, когда мы представляем, как работает SimpleXML, самое время обратиться к задаче хранения кроссвордов. кроссворды можно рассматривать как большие сетки (как и Sudoku), в которых каждый элемент должен быть заполнен в процессе решения (опять же, полная аналогия с Sudoku). так что задачи практически совпадают.

Существует два способа хранения таких данных. «Чистое» решение - это хранение каждой клеточки в отдельном XML-элементе. Другой вариант — сделать один элемент в XML-файле, который состоит из символов, образующих сетку. Давайте рассмотрим сначала второй вариант как более простой:

<crossword>
<title>My Excellent Crossword</title>
<grid>
-l--t-
-linux
-a--l-
-m--i-
-agape
------
</grid>
</crossword>

Чтобы загрузить такой кроссворд, надо получить текст элемента grid и затем разобрать его посимвольно. Но это не очень хорошее решение. как хранить номера слов в кроссворде? как добавить вопросы? как сохранять состояние решения? Для решения всех этих проблем нам потребуется «правильный» XML, в котором каждый квадратик описывается отдельным элементом. Эти квадратные элементы могут иметь следующие атрибуты:

  • type- тип квадратика, black(чёрный) или white (белый).
  • number- номер слова (если есть)
  • direction- направление слова, down (вниз), across (вправо) или both (оба), если есть.
  • downclue- вопрос для слова, идущего вниз, если есть.
  • acrossclue- вопрос для слова, идущего вправо, если есть.
  • correctanswer- буква, которая должна быть тут.
  • currentanswer- буква, которую сюда написал отгадчик.
  • guessedanswer- буква, в которой отгадчик не уверен.

Мы отсортируем элементы так, что сначала укажем первую строчку, потом вторую и так далее. Элемент <grid> будет хранить свойства — автора, сложность (номер от 1 до 4, 4 — самый сложный), и размер сетки (6 значит шесть квадратиков вправо и вниз).

Чтобы сэкономить пространство в журнале, я приведу пример кроссворда 3x3:

<grid author=”Paul Hudson” difficulty=”1” size=”3”>
<square type=”white” number=”1” direction=”both” downclue=”Water stopper” acrossclue=”Four-legged cathater” correct=”d” current=”” guessed=”” />
<square type=”white” correct=”o” current=”” guessed=”” />
<square type=”white” number=”2” direction=”down” downclue=”Water stopper” correct=”g” current=”” guessed=”” />
<square type=”white” correct=”a” current=”” guessed=”” />
<square type=”black” />
<square type=”white” correct=”o” current=”” guessed=”” />
<square type=”white” number=”3” direction=”across” acrossclue=”Crazily annoyed” correct=”m” current=”” guessed=”” />
<square type=”white” correct=”a” current=”” guessed=”” />
<square type=”white” correct=”d” current=”” guessed=”” />
</grid>

Сохранив этот код в файле crossword.xml, мы можем использовать следующий PHP-код для печати всех правильных ответов:

<?php
$crossword = simplexml_load_file("crossword.xml");
$i = 0; // square counter
foreach($crossword->square as $square) {
if ($square["type"] == "white") {
print $square["correct"];
} else {
print " ";
}
++$i;
if ($i % $crossword['size'] == 0) print "\n";
}
?>

Вложенный в цикл второй оператор if отвечает за правильную расстановку символов перевода строки. Элементы XML загружаются в порядке очереди, так что нам нужно прервать строку после символа номер $crossword["size"] или любого, кратного ему.

И это всё? Да, нашей целью было только отображение кроссворда, а не программа для их решения или генерации (подсказка — для этого можно воспользоваться одним из множества бесплатных словарей в web), и у нас нет красивого графического интерфейса пользователя. я оставляю первые две задачи на ваше усмотрение, а вот с третьей постараюсь вам помочь в следующем выпуске.

До встречи!

Персональные инструменты
купить
подписаться
Яндекс.Метрика