【IT168 专稿】简单,相当简单
除非你最近几年一直隐藏在洞穴里,否则你应该听说过XML(它 是越来越多的Web发布者为内容标记所求助的工具箱)了。你或许甚至已经在实际中看见过包含用户自定义标签和标记的XML文档了,而且你也许会想知道一个 人到底是如何将复杂的混乱的代码转化为人类可阅读的内容。
答案是,不容易。
虽然 PHP自版本4.0以来已经包含了对两种标准的解析(读取:变得有意义)XML方法(SAX和DOM)的支持,这些方法的复杂性和与生俱来的古怪性经常使 得除了最专业的XML开发人员外的其他开发人员放弃。然而,所有的这一切都随着PHP 5.0发生了变化,PHP 5.0介绍了一种全新的名为SimpleXML的XML扩展,该扩展消除了处理XML文档的所有(我确实是指全部)痛苦。继续阅读然后找出其是如何实现 的。
令人不愉快的旧时光
为了理解为什么SimpleXML这么酷,该到一个简短的历史课的时间了。
在 SimpleXML之前,有两种处理XML文档的方法。第一个就是SAX或者XML的简单API,它涉及遍历一个XML文档然后在解析器遇到不同类型的标 签时调用特定的函数。例如,你可能已经调用了一个函数来处理一个起始标签,另外一个函数来处理结束标签而第三个函数处理之间的数据。第二种方法是DOM或 者文档对象模型,其涉及在内存中创建一个代表XML文档的树,然后使用树遍历方法来操纵它。一旦到达树的一个特定节点,那么对应的内容就可以被检索和使用。
这 两种方法都不是特别的易于用户使用:SAX需要开发人员为XML文件中遇到的每种类型的元素定制事件处理器,而DOM方法使用面向对象模式,该模式除了内 存密集且因此对大型XML文档效率低之外而且趋向不需要开发人员。在更大的上下文中,PHP 4为其不同的XML扩展使用了大量的不同的支撑库,导致了不同的XML扩展工作方式的不一致,然后因此产生了互操作性的担忧(以及对开发人员带来的大量困 惑)。
在PHP 5.0中,通过采用libxml2库(http://www.xmlsoft.org/)作为所有XML扩展的标准库且通过使得不同的XML扩展操作更加 一致这种协调努力来修复这个问题。虽然PHP 5中XML最大的变化是由Sterling Hughes、Rob Richards和Marcus Börger开发的SimpleXML扩展,但该扩展试图使在PHP 5中解析XML文档比在PHP 4中解析XML文档更加友好。
SimpleXML 通过将XML文档转化为一个对象然后将该文档内元素转变为可以使用标准对象符号来访问的对象属性而工作。这使得向下获取XML层次结构中的任意一层的元素 以访问其内容变得容易。文档树的同一层次处的重复元素被表示为数组,同时,可定制的元素集合可以使用XPath(稍后详细介绍)位置路径来创建;这些集合 然后可以使用PHP的标准循环结构来处理。访问元素属性和访问关联数组的关键字一样简单(没有新的内容需要学习,而且也没有特殊的代码要编写)。
为 了一起使用SimpleXML和PHP,你的PHP建构必须包含对SimpleXML的支持。这种支持在PHP 5的UNIX和Windows平台的版本中默认是被激活的。阅读更多关于这方面的内容,请到http://www.php.net/manual/en /ref.simplexml.php。如果你是PHP 4的用户,那么你运气不好,SimpleXML只在PHP 5中可用。
儿童动物园
为了弄明白SimpleXML是如何工作的,请考虑下面的XML文件:
?
xml version
=
"
1.0
"
?>
<
pet
>
<
name
>
Polly Parrot
</
name
>
<
age
>
3
</
age
>
<
species
>
parrot
</
species
>
<
parents
>
<
mother
>
Pia Parrot
</
mother
>
<
father
>
Peter Parrot
</
father
>
</
parents
>
</
pet
>
现在,你需要一种方法来获得包含在<name>、<age>、<species>和<parents>元素之间的内容。使用SimpleXML就太容易了:
<?
php
//
set name of XML file
$file
=
"
pet.xml
"
;
//
load file
$xml
=
simplexml_load_file($file) or die (
"
Unable to load XML file!
"
);
//
access XML data
echo
"
Name:
"
. $xml
->
name .
"
\n
"
; echo
"
Age:
"
. $xml
->
age .
"
\n
"
; echo
"
Species:
"
. $xml
->
species .
"
\n
"
; echo
"
Parents:
"
. $xml
->
parents
->
mother .
"
and
"
. $xml
->
parents
->
father .
"
\n
"
;
?>
行动首先以simplexml_load_file()函数开始,该函数接受被解析的XML文件的路径和名称。文件解析的结果是一个PHP对象,该对象的 属性对应于根元素之下的元素。一个元素内的字符数据可以因而使用标准的对象->属性符号来访问,访问首先从根元素开始然后沿着文件的层次路径向下移 动。
正如你可以读取操作一样,因此你也可以写操作。SimpleXML使得修改特定XML元素的内容变得容易(简单的给对应的对象属性赋一个新值)。下面是一个例子:
<?
php
//
set name of XML file
$file
=
"
pet.xml
"
;
//
load file
$xml
=
simplexml_load_file($file) or die (
"
Unable to load XML file!
"
);
//
modify XML data
$xml
->
name
=
"
Sammy Snail
"
; $xml
->
age
=
4
; $xml
->
species
=
"
snail
"
; $xml
->
parents
->
mother
=
"
Sue Snail
"
; $xml
->
parents
->
father
=
"
Sid Snail
"
;
//
write new data to file
file_put_contents($file, $xml
->
asXML());
?>
这里,原始XML文件首先被读入,然后其每个元素内部的字符数据通过给对应的对象属性赋新值来修改。通常情况下被用于将XML树输出到标准输出设备上的asXML()方法在这个实例中结合file_put_contents()函数以使用新的数据覆盖原先的XML文档。
罪恶之城
在XML层次结构中同一层次的重复元素被用数组元素表示因而可以使用数字索引来访问。为了明白这是如何工作的,考虑下面的XML文件:
<?
xml version
=
"
1.0
"
?>
<
sins
>
<
sin
>
pride
</
sin
>
<
sin
>
envy
</
sin
>
<
sin
>
anger
</
sin
>
<
sin
>
greed
</
sin
>
<
sin
>
sloth
</
sin
>
<
sin
>
gluttony
</
sin
>
<
sin
>
lust
</
sin
>
</
sins
>
下面是读取该XML文件然后从中取得数据的PHP脚本:
<?
php
//
set name of XML file
$file
=
"
sins.xml
"
;
//
load file
$xml
=
simplexml_load_file($file) or die (
"
Unable to load XML file!
"
);
//
access each <sin>
echo $xml
->
sin[
0
] .
"
\n
"
; echo $xml
->
sin[
1
] .
"
\n
"
; echo $xml
->
sin[
2
] .
"
\n
"
; echo $xml
->
sin[
3
] .
"
\n
"
; echo $xml
->
sin[
4
] .
"
\n
"
; echo $xml
->
sin[
5
] .
"
\n
"
; echo $xml
->
sin[
6
] .
"
\n
"
;
?>
如果你喜欢,那么你甚至可以使用foreach()循环对该集合重复访问,正如下一个等价的列表:
<?
php
//
set name of XML file
$file
=
"
sins.xml
"
;
//
load file
$xml
=
simplexml_load_file($file) or die (
"
Unable to load XML file!
"
);
//
iterate over <sin> element collection
foreach
($xml
->
sin
as
$sin) { echo
"
$sin\n
"
; }
?>
未来事物的样子
SimpleXML可以像其操作元素一样透明地操作元素属性和它们的内容。属性-值对被表示为PHP关联数组的成员,而且可以像常规的数组元素那样被访问。为了明白这是如何工作的,请看下面的脚本:
<?
php
//
create XML string
$str
=
<<<
XML
<?
xml version
=
"
1.0
"
?>
<
shapes
>
<
shape type
=
"
circle
"
radius
=
"
2
"
/>
<
shape type
=
"
rectangle
"
length
=
"
5
"
width
=
"
2
"
/>
<
shape type
=
"
square
"
length
=
"
7
"
/>
</
shapes
>
XML;
//
load string
$xml
=
simplexml_load_string($str) or die (
"
Unable to load XML string!
"
);
//
for each shape
//
calculate area
foreach
($xml
->
shape
as
$shape) {
if
($shape[
'
type
'
]
==
"
circle
"
) { $area
=
pi()
*
$shape[
'
radius
'
]
*
$shape[
'
radius
'
]; } elseif ($shape[
'
type
'
]
==
"
rectangle
"
) { $area
=
$shape[
'
length
'
]
*
$shape[
'
width
'
]; } elseif ($shape[
'
type
'
]
==
"
square
"
) { $area
=
$shape[
'
length
'
]
*
$shape[
'
length
'
]; } echo $area.
"
\n
"
; }
?>
和之前使用外部的XMl文件的例子不同,该例子动态创建一个XML文档然后使用simplexml_load_string()方法将其加载到 SimpleXML中。然后XML使用foreach()循环来解析且其每个形状的面积在每个<shape>元素的类型属性值的基础上被计 算。上述列表展示了属性值如何作为与每个元素属性相关联的属性数组的关键字被访问。
X表示点
SimpleXML 也支持通过XPath位置路径来定制元素集合。对于那些XML新手来说,XPath是一种标准的XML文档寻址机制,该机制允许开发人员访问文档内的元 素、属性或者文本节点的集合。阅读更多关于XPath的内容,请访问http://www.w3.org/TR/xpath.html和http: //www.melonfire.com/community/columns/trog/article.php?id=83
为了明白它是如何工作的,考虑下面的XML文档:
<?
xml version
=
"
1.0
"
?>
<
ingredients
>
<
item
>
<
desc
>
Boneless chicken breasts
</
desc
>
<
quantity
>
2
</
quantity
>
</
item
>
<
item
>
<
desc
>
Chopped onions
</
desc
>
<
quantity
>
2
</
quantity
>
</
item
>
<
item
>
<
desc
>
Ginger
</
desc
>
<
quantity
>
1
</
quantity
>
</
item
>
<
item
>
<
desc
>
Garlic
</
desc
>
<
quantity
>
1
</
quantity
>
</
item
>
<
item
>
<
desc
>
Red chili powder
</
desc
>
<
quantity
>
1
</
quantity
>
</
item
>
<
item
>
<
desc
>
Coriander seeds
</
desc
>
<
quantity
>
1
</
quantity
>
</
item
>
<
item
>
<
desc
>
Lime juice
</
desc
>
<
quantity
>
2
</
quantity
>
</
item
>
</
ingredients
>
现在,让我假定你想打印所有的<desc>元素。你可以像之前所讨论的那样,通过枚举<item>元素数组来打 印<desc>元素,或者你可以使用xpath()方法只创建一个定制的只有<desc>元素的集合,然后对其进行反复:
<?
php
//
set name of XML file
$file
=
"
ingredients.xml
"
;
//
load file
$xml
=
simplexml_load_file($file) or die (
"
Unable to load XML file!
"
);
//
get all the <desc> elements and print
foreach
($xml
->
xpath(
'
//desc
'
)
as
$desc) { echo
"
$desc\n
"
; }
?>
使用XPath,你甚至可以(举例而言)通过创建一个只有<desc>元素的集合(其对应的数量是2或者更多)而比这个更富有想象力。
<?
php
//
set name of XML file
$file
=
"
ingredients.xml
"
;
//
load file
$xml
=
simplexml_load_file($file) or die (
"
Unable to load XML file!
"
);
//
get all the <desc> elements and print
foreach
($xml
->
xpath(
'
//item[quantity > 1]/desc
'
)
as
$desc) { echo
"
$desc\n
"
; }
?>
没有XPath,完成这个功能将会比上述五行代码复杂得多。你自己试验一下然后看看是否是这样!
红磨坊的一夜
既然你已经明白了XPath所能做的事情,那么让我们以一个你实际上可能会如何使用XPath的例子作为结尾。
让我们假定你具有一系列的以XML标示的电影评论,如下所示:
<?
xml version
=
"
1.0
"
?>
<
review id
=
"
57
"
category
=
"
2
"
>
<
title
>
Moulin Rouge
</
title
>
<
teaser
>
Baz Luhrmann
'
s over-the-top vision of Paris at the turn of the century
is
witty, sexy...and completely unforgettable
</
teaser
>
<
cast
>
<
person
>
Nicole Kidman
</
person
>
<
person
>
Ewan McGregor
</
person
>
<
person
>
John Leguizamo
</
person
>
<
person
>
Jim Broadbent
</
person
>
<
person
>
Richard Roxburgh
</
person
>
</
cast
>
<
director
>
Baz Luhrmann
</
director
>
<
duration
>
120
</
duration
>
<
genre
>
Romance
/
Comedy
</
genre
>
<
year
>
2001
</
year
>
<
body
>
A stylishly spectacular extravaganza, Moulin Rouge
is
hard to categorize; it
is
, at different times, a love story, a costume drama, a musical, and a comedy. Director Baz Luhrmann (well
-
known
for
the very hip William Shakespeare
'
s Romeo + Juliet) has taken some simple
themes
-
love, jealousy and obsession
-
and done something completely
new
and different with them by setting them to music.
</
body
>
<
rating
>
5
</
rating
>
</
review
>
现在,你打算在你的Web站点上显示该评论。因此你需要一个PHP脚本以从该文件中提取数据然后将其放置在HTML模板中的适当位置。就你目前所学到的内容而言,这很容易,就像如下代码所演示的那样:
<?
php
//
set name of XML file
//
normally this would come through GET
//
it's hard-wired here for simplicity
$file
=
"
57.xml
"
;
//
load file
$xml
=
simplexml_load_file($file) or die (
"
Unable to load XML file!
"
);
?>
<
html
>
<
head
><
basefont face
=
"
Arial
"
></
head
>
<
body
>
<!--
title and year
-->
<
h1
><?
php echo $xml
->
title;
?>
(
<?
php echo $xml
->
year;
?>
)
</
h1
>
<!--
slug
-->
<
h3
><?
php echo $xml
->
teaser;
?></
h3
>
<!--
review body
-->
<?
php echo $xml
->
body;
?>
<!--
director, cast, duration and rating
-->
<
p align
=
"
right
"
/>
<
font size
=
"
-2
"
>
Director:
<
b
><?
php echo $xml
->
director;
?></
b
>
<
br
/>
Duration:
<
b
><?
php echo $xml
->
duration;
?>
min
</
b
>
<
br
/>
Cast:
<
b
><?
php
foreach
($xml
->
cast
->
person
as
$person)
{ echo "$person "; }
?></
b
>
<
br
/>
Rating:
<
b
><?
php echo $xml
->
rating;
?></
b
>
</
font
>
</
body
>
</
html
>