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

LXF71:PHP

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
(Новая: {{Цикл/PHP}} == SimpleXML и XPath == '''''Пол Хадсон''' (Paul Hudson) вновь пытается помочь с решенем загадки Sudoku и погружае...)
 
Строка 2: Строка 2:
  
 
== SimpleXML и XPath ==
 
== SimpleXML и XPath ==
'''''Пол Хадсон''' (Paul Hudson) вновь пытается помочь с решенем загадки Sudoku и погружается в изучение XML для дела и удовольствия.''
+
'''''Пол Хадсон''' (Paul Hudson) вновь пытается помочь с решением загадки Sudoku и погружается в изучение XML для дела и удовольствия.''
  
 
одна из многих латинских поговорок, приписываемых юлию
 
одна из многих латинских поговорок, приписываемых юлию
Строка 10: Строка 10:
 
знает, что эта фраза очень похоже прозвучит в современной Испании.
 
знает, что эта фраза очень похоже прозвучит в современной Испании.
 
там вы можете сказать “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-файл.
 +
 
В общем, XML силён несмотря на все свои проблемы, потому что он
 
В общем, XML силён несмотря на все свои проблемы, потому что он
позволяет легко обмениваться данными между различными программа-
+
позволяет легко обмениваться данными между различными программами, в том числе и через Интернет. Используя его для сохранения кроссвордов, мы можем быть уверены что при желании другие программы
ми, в том числе и через Интернет. Используя его для сохранения крос-
+
смогу легко воспользоваться ими. Если вы читали о возможностях технологии Ajax, то вы уже знаете о возможностях, которые даёт XML при программировании на стороне клиента. Если у вас нет каких-то специфических требований, для большинства проектов XML подходит лучше всего.
свордов, мы можем быть уверены что при желании другие программы
+
смогу легко воспользоваться ими. Если вы читали о возможностях техно-
+
логии Ajax, то вы уже знаете о возможностях, которые даёт XML при про-
+
граммировании на стороне клиента. Если у вас нет каких-то специфичес-
+
ких требований, для большинства проектов XML подходит лучше всего.
+
  
 
=== создание простого XML ===
 
=== создание простого XML ===
 
Поддержка XML в PHP находится в очень неустойчивом положении. С
 
Поддержка XML в PHP находится в очень неустойчивом положении. С
 
момента её появления было создано множество разных реализаций,
 
момента её появления было создано множество разных реализаций,
несколько переработок и серия расширений, предназначенных для чте-
+
несколько переработок и серия расширений, предназначенных для чтения и записи XML. Последнее из них, SimpleXML (простой XML), так
ния и записи XML. Последнее из них, SimpleXML (простой XML), так
+
 
называется потому, что предназначено для представления XML-файла в
 
называется потому, что предназначено для представления XML-файла в
 
виде простых переменных PHP.
 
виде простых переменных PHP.
 +
 
Вот пример XML-файла под названием requiem.xml:
 
Вот пример XML-файла под названием requiem.xml:
<requiem>
+
<source lang="xml"><requiem>
 
<line>
 
<line>
 
<latin>Confutatis maledictis</latin>
 
<latin>Confutatis maledictis</latin>
Строка 65: Строка 51:
 
<english>Doomed to flames of woe unbounded</english>
 
<english>Doomed to flames of woe unbounded</english>
 
</line>
 
</line>
</requiem>
+
</requiem></source>
В этом примере корневым элементом является requiem. он содер-
+
В этом примере корневым элементом является requiem. он содержит два элемента line, в которых в свою очередь находятся latin и
жит два элемента line, в которых в свою очередь находятся latin и
+
 
english – как видите, тут нет ничего мудрёного. Мы можем разобрать
 
english – как видите, тут нет ничего мудрёного. Мы можем разобрать
 
этот файл и напечатать результат разбора с помощью двух строк на PHP:
 
этот файл и напечатать результат разбора с помощью двух строк на PHP:
<?php
+
<source lang="php"><?php
$file = simplexml_load_file(“requiem.xml”);
+
$file = simplexml_load_file("requiem.xml");
 
var_dump($file);
 
var_dump($file);
?>
+
?></source>
 
Функция var_dump даёт прекрасную возможность разобраться, как
 
Функция var_dump даёт прекрасную возможность разобраться, как
 
что-то работает в PHP, так как она позволяется наглядно увидеть данные,
 
что-то работает в PHP, так как она позволяется наглядно увидеть данные,
 
содержащиеся в переменной. Вот, что выводит наша программа:
 
содержащиеся в переменной. Вот, что выводит наша программа:
object(SimpleXMLElement)#1 (1) {
+
<source lang="php">object(SimpleXMLElement)#1 (1) {
[“line”]=>
+
["line"]=>
 
array(2) {
 
array(2) {
 
[0]=>
 
[0]=>
 
object(SimpleXMLElement)#2 (2) {
 
object(SimpleXMLElement)#2 (2) {
[“latin”]=>
+
["latin"]=>
string(21) “Confutatis maledictis”
+
string(21) "Confutatis maledictis"
[“english”]=>
+
["english"]=>
string(30) “When the wicked are confounded”
+
string(30) "When the wicked are confounded"
 
}
 
}
 
[1]=>
 
[1]=>
 
object(SimpleXMLElement)#3 (2) {
 
object(SimpleXMLElement)#3 (2) {
[“latin”]=>
+
["latin"]=>
string(25) “Flammis acribus addictis”
+
string(25) "Flammis acribus addictis"
[“english”]=>
+
["english"]=>
string(33) “Doomed to flames of woe unbounded”
+
string(33) "Doomed to flames of woe unbounded"
}
+
 
}
 
}
 
}
 
}
 +
}</source>
 
Понимание этой структуры – это ключ к использованию SimpleXML.
 
Понимание этой структуры – это ключ к использованию SimpleXML.
Корневой объект SimpleXMLElement содержит только одну пере-
+
 
менную, line. О на является массивом из двух элементов (с номерами 0
+
Корневой объект SimpleXMLElement содержит только одну переменную, line. О на является массивом из двух элементов (с номерами 0
 
и 1), соответствующих двум элементам <line> в исходном файле. Эти
 
и 1), соответствующих двум элементам <line> в исходном файле. Эти
 
элементы, в свою очередь, являются объектами SimpleXMLElement,
 
элементы, в свою очередь, являются объектами SimpleXMLElement,
содержащими строковые переменные latin и english с соответствующи-
+
содержащими строковые переменные latin и english с соответствующими данными из исходного XML. Итак, вызов функции simplexml_load_file вернул нам смесь из объектов и массивов, соответствующую структуре XML-файла и содержащий все его данные в обычных переменных
ми данными из исходного XML. Итак, вызов функции simplexml_load_
+
file вернул нам смесь из объектов и массивов, соответствующую струк-
+
туре XML-файла и содержащий все его данные в обычных переменных
+
 
php. Вывод var_dump познавателен, но непригоден для нормальной
 
php. Вывод var_dump познавателен, но непригоден для нормальной
работы, так что давайте перепишем нашу программу так, чтобы она печа-
+
работы, так что давайте перепишем нашу программу так, чтобы она печатала только английский текст:
тала только английский текст:
+
<source lang="php"><?php
<?php
+
$file = simplexml_load_file("requiem.xml");
$file = simplexml_load_file(“requiem.xml”);
+
 
foreach($file->line as $line) {
 
foreach($file->line as $line) {
echo $line->english, \n”;
+
echo $line->english, "\n";
 
}
 
}
?>
+
?></source>
Обратите внимание на то, как мы используем $file->line и $line-
+
Обратите внимание на то, как мы используем $file->line и $line->english. Т ак как $file и $line – объекты, мы можем получить доступ к
>english. Т ак как $file и $line – объекты, мы можем получить доступ к
+
их переменным при помощи оператора ->. Ничего не мешает вам трактовать объекты как массивы. Например, использовав их внутри цикла
их переменным при помощи оператора ->. Ничего не мешает вам тракто-
+
foreach, можно перебирать все поля объекта по одному, словно это элементы массива. Но лучше всё же мысленно различать объекты и массивы, так как SimpleXML использует «массивоподобный» метод для представления атрибутов. Например, если изменить первую строчку нашего
вать объекты как массивы. Например, использовав их внутри цикла
+
foreach, можно перебирать все поля объекта по одному, словно это эле-
+
менты массива. Но лучше всё же мысленно различать объекты и масси-
+
вы, так как SimpleXML использует «массивоподобный» метод для пред-
+
ставления атрибутов. Например, если изменить первую строчку нашего
+
 
XML следующим образом:
 
XML следующим образом:
<requiem key=”D”>
+
<source lang="xml"><requiem key=”D”></source>
 
то теперь с разобранным файлом вы можете сделать следующее:
 
то теперь с разобранным файлом вы можете сделать следующее:
$file = simplexml_load_file(“requiem.xml”);
+
<source lang="php">$file = simplexml_load_file("requiem.xml");
print $file[“key”];
+
print $file["key"];</source>
 
Этот код отобразит значение атрибута key корневого элемента.
 
Этот код отобразит значение атрибута key корневого элемента.
 
Синтаксис $file->key не сработает, так как он обозначает обращение ко
 
Синтаксис $file->key не сработает, так как он обозначает обращение ко
Строка 137: Строка 113:
 
Как вы только что могли убедиться, SimpleXML позволяет вам работать с
 
Как вы только что могли убедиться, SimpleXML позволяет вам работать с
 
XML-файлами так же, как с объектами и массивами, так что вы можете
 
XML-файлами так же, как с объектами и массивами, так что вы можете
игнорировать семантику XML и сосредоточиться на правильной обработ-
+
игнорировать семантику XML и сосредоточиться на правильной обработке данных. Эта простота использования простирается вплоть до возможности изменять значения переменных, поскольку раз уж у вас есть объекты и массивы php, то вы можете делать с ними всё, что пожелаете. Что
ке данных. Эта простота использования простирается вплоть до возмож-
+
ности изменять значения переменных, поскольку раз уж у вас есть объекты и массивы php, то вы можете делать с ними всё, что пожелаете. Что
+
 
более интересно, после внесения изменений вы можете экспортировать
 
более интересно, после внесения изменений вы можете экспортировать
 
результат обратно в XML. Для демонстрации этого мы используем
 
результат обратно в XML. Для демонстрации этого мы используем
 
новый XML файл:
 
новый XML файл:
<park>
+
<source lang="xml"><park>
 
<squirrel name=”Squirly”>
 
<squirrel name=”Squirly”>
 
<nuts>320</nuts>
 
<nuts>320</nuts>
Строка 150: Строка 124:
 
<nuts>0</nuts>
 
<nuts>0</nuts>
 
</squirrel>
 
</squirrel>
</park>
+
</park></source>
 
И так, в нашем парке (park) есть две белки (squirrel) по имени Squirly
 
И так, в нашем парке (park) есть две белки (squirrel) по имени Squirly
 
и Nick. У Squirly много орехов (nuts), а у Nick’а ничего нет. Чтобы это
 
и Nick. У Squirly много орехов (nuts), а у Nick’а ничего нет. Чтобы это
исправить, воспользуемся умением SimpleXML изменять значения пере-
+
исправить, воспользуемся умением SimpleXML изменять значения переменных на лету. Сделать это действительно просто:
менных на лету. Сделать это действительно просто:
+
<source lang="php"><?php
<?php
+
$park = simplexml_load_file("squirrels.xml");
$park = simplexml_load_file(“squirrels.xml”);
+
 
$park->squirrel[1]->nuts = 10;
 
$park->squirrel[1]->nuts = 10;
 
print $park->asXML();
 
print $park->asXML();
?>
+
?></source>
Этот сценарий превращает парк с белками в обычный набор объек-
+
Этот сценарий превращает парк с белками в обычный набор объектов и массивов, получает доступ к одной из белок (второй, так как элементы нумеруются начиная с нуля, то есть squirrel[1] – это Nick) и изменяет число её орехов. Важной частью является вызов asXML, так как он
тов и массивов, получает доступ к одной из белок (второй, так как эле-
+
менты нумеруются начиная с нуля, то есть squirrel[1] – это Nick) и изме-
+
няет число её орехов. Важной частью является вызов asXML, так как он
+
 
позволяет превратить наши объекты снова в XML, и это лучший XML чем
 
позволяет превратить наши объекты снова в XML, и это лучший XML чем
 
тот, с которого мы начинали:
 
тот, с которого мы начинали:
<?xml version=”1.0”?>
+
<source lang="xml"><?xml version=”1.0”?>
 
<park>
 
<park>
 
<squirrel name=”Squirly”>
 
<squirrel name=”Squirly”>
Строка 174: Строка 144:
 
<nuts>10</nuts>
 
<nuts>10</nuts>
 
</squirrel>
 
</squirrel>
</park>
+
</park></source>
 
Nick обзавёлся орехами, а весь файл – заголовком с обозначением
 
Nick обзавёлся орехами, а весь файл – заголовком с обозначением
 
версии, которого у нас не было раньше. Метод asXML доступен для
 
версии, которого у нас не было раньше. Метод asXML доступен для
Строка 189: Строка 159:
 
XML более похожим на SQL – теперь вы можете не просто использовать
 
XML более похожим на SQL – теперь вы можете не просто использовать
 
его для хранения информации, но и выполнять запросы. По своей сути
 
его для хранения информации, но и выполнять запросы. По своей сути
Xpath – это способ вытащить нужную часть XML, указав путь к ней (похо-
+
Xpath – это способ вытащить нужную часть XML, указав путь к ней (похожий на путь к файлу в файловой системе). В PHP это достигается возвращением массива объектов SimpleXMLElement, соответствующих
жий на путь к файлу в файловой системе). В PHP это достигается воз-
+
вращением массива объектов SimpleXMLElement, соответствующих
+
 
вашему запросу.
 
вашему запросу.
  
 
=== первые шаги по Xpath ===
 
=== первые шаги по Xpath ===
 
Представляю вам новый XML, books.xml:
 
Представляю вам новый XML, books.xml:
<books>
+
<source lang="xml"><books>
 
<author nationality=”British” name=”Jane Austen”>
 
<author nationality=”British” name=”Jane Austen”>
 
<book>Pride And Prejudice</book>
 
<book>Pride And Prejudice</book>
Строка 209: Строка 177:
 
<book>The Secret Purposes</book>
 
<book>The Secret Purposes</book>
 
</author>
 
</author>
</books>
+
</books></source>
 
он содержит трёх авторов (<author>), из которых двое англичане, и
 
он содержит трёх авторов (<author>), из которых двое англичане, и
 
для всех троих указаны их книги. С помощью Xpath мы можем запросить
 
для всех троих указаны их книги. С помощью Xpath мы можем запросить
 
список всех книг:
 
список всех книг:
<?php
+
<source lang="php"><?php
$authors = simplexml_load_file(“books.xml”);
+
$authors = simplexml_load_file("books.xml");
$books = $authors->XPath(//book”);
+
$books = $authors->XPath("//book");
 
foreach($books as $book) {
 
foreach($books as $book) {
echo $book, \n”;
+
echo $book, "\n";
 
}
 
}
?>
+
?></source>
 
Запрос «//book» ищет элемент <book> в любом месте дерева XML.
 
Запрос «//book» ищет элемент <book> в любом месте дерева XML.
 
Часть запроса // значит “искать везде”, что для функции xpath значит
 
Часть запроса // значит “искать везде”, что для функции xpath значит
«найти все корневые элементы <book>, найти все <book> внутри элемен-
+
«найти все корневые элементы <book>, найти все <book> внутри элемента, содержащего <author>, найти все <book> внутри элементов <author>»,
та, содержащего <author>, найти все <book> внутри элементов <author>»,
+
 
то есть найти <book> вообще везде, и вернуть все найденные элементы
 
то есть найти <book> вообще везде, и вернуть все найденные элементы
 
в одном массиве. Затем мы передаём полученный массив в цикл
 
в одном массиве. Затем мы передаём полученный массив в цикл
Строка 230: Строка 197:
 
самом деле хранится SimpleXMLElement. Это часть той же магии,
 
самом деле хранится SimpleXMLElement. Это часть той же магии,
 
которая позволяет работать с объектами SimpleXML как с массивами. В
 
которая позволяет работать с объектами SimpleXML как с массивами. В
нашем случае это магия функции __tostring(), позволяющая использо-
+
нашем случае это магия функции __tostring(), позволяющая использовать объект в операторе print как обычную строку.
вать объект в операторе print как обычную строку.
+
 
 
Если вы хотите добыть конкретную книгу, Xpath поможет вам и в
 
Если вы хотите добыть конкретную книгу, Xpath поможет вам и в
этом. Например, добавьте следующий код к books.xml сразу перед стро-
+
этом. Например, добавьте следующий код к books.xml сразу перед строкой </books>:
кой </books>:
+
<source lang="xml"><library name=”British Library”>
<library name=”British Library”>
+
 
<book>
 
<book>
 
<title>The Peloponnesian War</title>
 
<title>The Peloponnesian War</title>
Строка 244: Строка 210:
 
<author>Thucidydes</author>
 
<author>Thucidydes</author>
 
</book>
 
</book>
</library>
+
</library></source>
теперь элементов <book> у нас стало больше, но они разные – эле-
+
теперь элементов <book> у нас стало больше, но они разные – элементы внутри <author> обозначают, какие книги этими авторами были
менты внутри <author> обозначают, какие книги этими авторами были
+
написаны, а <book> внутри < library> - это список книг, имеющихся в библиотеке. Если по-прежнему использовать строку поиска //book, то мы
написаны, а <book> внутри < library> - это список книг, имеющихся в биб-
+
лиотеке. Если по-прежнему использовать строку поиска //book, то мы
+
 
получим их все, вне зависимости от того, есть эта книга в библиотеке
 
получим их все, вне зависимости от того, есть эта книга в библиотеке
 
или нет.
 
или нет.
 +
 
Для выполнения более точного поиска можно воспользоваться
 
Для выполнения более точного поиска можно воспользоваться
 
иерархией XML:
 
иерархией XML:
$books = $authors->XPath(/books/library/book”);
+
<source lang="php">$books = $authors->XPath("/books/library/book");</source>
Этот запрос вернёт все книги в библиотеке, не затронув книги авто-
+
Этот запрос вернёт все книги в библиотеке, не затронув книги авторов. Помните, что он по прежнему возвращает массив из
ров. Помните, что он по прежнему возвращает массив из
+
 
SimpleXMLElement, то есть вы получите объекты со свойствами title и
 
SimpleXMLElement, то есть вы получите объекты со свойствами title и
author. Чтобы получить только заголовки, можно воспользоваться запро-
+
author. Чтобы получить только заголовки, можно воспользоваться запросом /books/library/book/title, но если нам надо и заголовки и авторов,
сом /books/library/book/title, но если нам надо и заголовки и авторов,
+
то лучше запросить именно книги, а затем воспользоваться объектно-ориентированным синтаксисом:
то лучше запросить именно книги, а затем воспользоваться объектно-
+
<source lang="php"><?php
ориентированным синтаксисом:
+
$all_books = simplexml_load_file("books.xml");
<?php
+
$library_books = $all_books->XPath("/books/library/book");
$all_books = simplexml_load_file(“books.xml”);
+
$library_books = $all_books->XPath(/books/library/book”);
+
 
foreach($library_books as $book) {
 
foreach($library_books as $book) {
echo {$book->title} was written by {$book->author}\n”;
+
echo "{$book->title} was written by {$book->author}\n";
 
}
 
}
?>
+
?></source>
 
XML нельзя назвать быстрым, но XPath – уже можно, так как в
 
XML нельзя назвать быстрым, но XPath – уже можно, так как в
момент его использования XML преобразован во внутреннее представле-
+
момент его использования XML преобразован во внутреннее представление, поиск по которому происходит сравнительно легко.
ние, поиск по которому происходит сравнительно легко.
+
  
 
=== разделяй и запрашивай ===
 
=== разделяй и запрашивай ===
Строка 276: Строка 237:
 
небольшого набора условий. Например, мы можем запросить список
 
небольшого набора условий. Например, мы можем запросить список
 
только английских авторов.
 
только английских авторов.
<?php
+
<source lang="php"><?php
$all_books = simplexml_load_file(“books.xml”);
+
$all_books = simplexml_load_file("books.xml");
$british_authors = $all_books->XPath(/books/author[@nationality=”British”
+
$british_authors = $all_books->XPath('/books/author[@nationality="British"]');
]);
+
 
foreach($british_authors as $author) {
 
foreach($british_authors as $author) {
echo {$author[“name”]} is British.\n”;
+
echo "{$author["name"]} is British.\n";
 
}
 
}
?>
+
?></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”.
+
  
 
кроме проверки на равенство вы можете использовать стандартные
 
кроме проверки на равенство вы можете использовать стандартные
 
условия <, >, <=, >= и !=, а так же соединять несколько условий при
 
условия <, >, <=, >= и !=, а так же соединять несколько условий при
 
помощи or или and.
 
помощи or или and.
<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 @
+
/books/author[@age>=18 and (@name=”Jim” or @name=”Bob”)].
name=”Bob”)].
+
  
 
кроме операторов сравнения, в условиях XPath доступны простые
 
кроме операторов сравнения, в условиях XPath доступны простые
Строка 315: Строка 269:
 
$good_wines = $wines->XPath('/drinks/alcoholic/wines/wine[@year mod 2 = 1 and (@country="Australia" or @country="France")]');
 
$good_wines = $wines->XPath('/drinks/alcoholic/wines/wine[@year mod 2 = 1 and (@country="Australia" or @country="France")]');
 
</source>
 
</source>
я думаю, посмотрев на этот код вы согласитесь, что сложные выра-
+
я думаю, посмотрев на этот код вы согласитесь, что сложные выражения в XML тяжело читаются и поэтому их лучше избегать.
жения в XML тяжело читаются и поэтому их лучше избегать.
+
  
 
=== Кроссворды ===
 
=== Кроссворды ===
Строка 356: Строка 309:
 
значит шесть квадратиков вправо и вниз).
 
значит шесть квадратиков вправо и вниз).
  
Чтобы сэкономить пространство в журнале, я приведу пример крос-
+
Чтобы сэкономить пространство в журнале, я приведу пример кроссворда 3x3:
сворда 3x3:
+
 
<source lang="xml"><grid author=”Paul Hudson” difficulty=”1” size=”3”>
 
<source lang="xml"><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” number=”1” direction=”both” downclue=”Water stopper” acrossclue=”Four-legged cathater” correct=”d” current=”” guessed=”” />

Версия 19:57, 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), и у нас нет красивого графического интерфейса пользователя. я оставляю первые две задачи на ваше усмотрение, а вот с третьей постараюсь вам помочь в следующем выпуске.

До встречи!

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