|
|
CIII. XML parser functions
XML (eXtensible Markup Language) is a data format for structured
document interchange on the Web. It is a standard defined by
The World Wide Web consortium (W3C). Information about XML and
related technologies can be found at .
This extension uses expat, which can
be found at . The
Makefile that comes with expat does not build a library by
default, you can use this make rule for that:
libexpat.a: $(OBJS)
ar -rc $@ $(OBJS)
ranlib $@ |
A source RPM package of expat can be found at .
Note that if you are using Apache-1.3.7 or later, you already
have the required expat library. Simply configure PHP using
--with-xml (without any
additional path) and it will automatically use the expat library
built into Apache.
On UNIX, run configure with the --with-xml option. The
expat library should be installed
somewhere your compiler can find it. If you compile PHP as a
module for Apache 1.3.9 or later, PHP will automatically use the
bundled expat library from Apache.
You may need to set CPPFLAGS and
LDFLAGS in your environment before running
configure if you have installed expat somewhere exotic.
Build PHP. Tada! That should be it.
This PHP extension implements support for James Clark's
expat in PHP. This toolkit lets you
parse, but not validate, XML documents. It supports three
source character encodings
also provided by PHP: US-ASCII,
ISO-8859-1 and UTF-8.
UTF-16 is not supported.
This extension lets you create XML parsers
and then define handlers for different XML
events. Each XML parser also has a few parameters you
can adjust.
The XML event handlers defined are:
Taulu 1. Supported XML handlers PHP function to set handler | Event description |
---|
xml_set_element_handler() |
Element events are issued whenever the XML parser
encounters start or end tags. There are separate handlers
for start tags and end tags.
|
xml_set_character_data_handler()
|
Character data is roughly all the non-markup contents of
XML documents, including whitespace between tags. Note
that the XML parser does not add or remove any whitespace,
it is up to the application (you) to decide whether
whitespace is significant.
|
xml_set_processing_instruction_handler()
|
PHP programmers should be familiar with processing
instructions (PIs) already. <?php ?> is a processing
instruction, where php is called
the "PI target". The handling of these are
application-specific, except that all PI targets starting
with "XML" are reserved.
| xml_set_default_handler() |
What goes not to another handler goes to the default
handler. You will get things like the XML and document
type declarations in the default handler.
|
xml_set_unparsed_entity_decl_handler()
|
This handler will be called for declaration of an unparsed
(NDATA) entity.
|
xml_set_notation_decl_handler()
|
This handler is called for declaration of a notation.
|
xml_set_external_entity_ref_handler()
|
This handler is called when the XML parser finds a
reference to an external parsed general entity. This can
be a reference to a file or URL, for example. See the external entity
example for a demonstration.
|
The element handler functions may get their element names
case-folded. Case-folding is defined by
the XML standard as "a process applied to a sequence of
characters, in which those identified as non-uppercase are
replaced by their uppercase equivalents". In other words, when
it comes to XML, case-folding simply means uppercasing.
By default, all the element names that are passed to the handler
functions are case-folded. This behaviour can be queried and
controlled per XML parser with the
xml_parser_get_option() and
xml_parser_set_option() functions,
respectively.
The following constants are defined for XML error codes (as
returned by xml_parse()):
XML_ERROR_NONE | XML_ERROR_NO_MEMORY | XML_ERROR_SYNTAX | XML_ERROR_NO_ELEMENTS | XML_ERROR_INVALID_TOKEN | XML_ERROR_UNCLOSED_TOKEN | XML_ERROR_PARTIAL_CHAR | XML_ERROR_TAG_MISMATCH | XML_ERROR_DUPLICATE_ATTRIBUTE | XML_ERROR_JUNK_AFTER_DOC_ELEMENT | XML_ERROR_PARAM_ENTITY_REF | XML_ERROR_UNDEFINED_ENTITY | XML_ERROR_RECURSIVE_ENTITY_REF | XML_ERROR_ASYNC_ENTITY | XML_ERROR_BAD_CHAR_REF | XML_ERROR_BINARY_ENTITY_REF | XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF | XML_ERROR_MISPLACED_XML_PI | XML_ERROR_UNKNOWN_ENCODING | XML_ERROR_INCORRECT_ENCODING | XML_ERROR_UNCLOSED_CDATA_SECTION | XML_ERROR_EXTERNAL_ENTITY_HANDLING |
PHP's XML extension supports the character set through
different character encodings. There are
two types of character encodings, source
encoding and target encoding.
PHP's internal representation of the document is always encoded
with UTF-8.
Source encoding is done when an XML document is parsed. Upon creating an XML
parser, a source encoding can be specified (this encoding
can not be changed later in the XML parser's lifetime). The
supported source encodings are ISO-8859-1,
US-ASCII and UTF-8. The
former two are single-byte encodings, which means that each
character is represented by a single byte.
UTF-8 can encode characters composed by a
variable number of bits (up to 21) in one to four bytes. The
default source encoding used by PHP is
ISO-8859-1.
Target encoding is done when PHP passes data to XML handler
functions. When an XML parser is created, the target encoding
is set to the same as the source encoding, but this may be
changed at any point. The target encoding will affect character
data as well as tag names and processing instruction targets.
If the XML parser encounters characters outside the range that
its source encoding is capable of representing, it will return
an error.
If PHP encounters characters in the parsed XML document that can
not be represented in the chosen target encoding, the problem
characters will be "demoted". Currently, this means that such
characters are replaced by a question mark.
Here are some example PHP scripts parsing XML documents.
This first example displays the stucture of the start elements in
a document with indentation.
Esimerkki 1. Show XML Element Structure $file = "data.xml";
$depth = array();
function startElement($parser, $name, $attrs) {
global $depth;
for ($i = 0; $i < $depth[$parser]; $i++) {
print " ";
}
print "$name\n";
$depth[$parser]++;
}
function endElement($parser, $name) {
global $depth;
$depth[$parser]--;
}
$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
if (!($fp = fopen($file, "r"))) {
die("could not open XML input");
}
while ($data = fread($fp, 4096)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
xml_parser_free($xml_parser); |
|
Esimerkki 2. Map XML to HTML
This example maps tags in an XML document directly to HTML
tags. Elements not found in the "map array" are ignored. Of
course, this example will only work with a specific XML
document type.
$file = "data.xml";
$map_array = array(
"BOLD" => "B",
"EMPHASIS" => "I",
"LITERAL" => "TT"
);
function startElement($parser, $name, $attrs) {
global $map_array;
if ($htmltag = $map_array[$name]) {
print "<$htmltag>";
}
}
function endElement($parser, $name) {
global $map_array;
if ($htmltag = $map_array[$name]) {
print "</$htmltag>";
}
}
function characterData($parser, $data) {
print $data;
}
$xml_parser = xml_parser_create();
// use case-folding so we are sure to find the tag in $map_array
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
if (!($fp = fopen($file, "r"))) {
die("could not open XML input");
}
while ($data = fread($fp, 4096)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
xml_parser_free($xml_parser); |
|
This example highlights XML code. It illustrates how to use an
external entity reference handler to include and parse other
documents, as well as how PIs can be processed, and a way of
determining "trust" for PIs containing code.
XML documents that can be used for this example are found below
the example (xmltest.xml and
xmltest2.xml.)
Esimerkki 3. External Entity Example <?php
$file = "xmltest.xml";
function trustedFile($file) {
// only trust local files owned by ourselves
if (!eregi("^([a-z]+)://", $file)
&& fileowner($file) == getmyuid()) {
return true;
}
return false;
}
function startElement($parser, $name, $attribs) {
print "<<font color=\"#0000cc\">$name</font>";
if (sizeof($attribs)) {
while (list($k, $v) = each($attribs)) {
print " <font color=\"#009900\">$k</font>=\"<font
color=\"#990000\">$v</font>\"";
}
}
print ">";
}
function endElement($parser, $name) {
print "</<font color=\"#0000cc\">$name</font>>";
}
function characterData($parser, $data) {
print "<b>$data</b>";
}
function PIHandler($parser, $target, $data) {
switch (strtolower($target)) {
case "php":
global $parser_file;
// If the parsed document is "trusted", we say it is safe
// to execute PHP code inside it. If not, display the code
// instead.
if (trustedFile($parser_file[$parser])) {
eval($data);
} else {
printf("Untrusted PHP code: <i>%s</i>",
htmlspecialchars($data));
}
break;
}
}
function defaultHandler($parser, $data) {
if (substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") {
printf('<font color="#aa00aa">%s</font>',
htmlspecialchars($data));
} else {
printf('<font size="-1">%s</font>',
htmlspecialchars($data));
}
}
function externalEntityRefHandler($parser, $openEntityNames, $base, $systemId,
$publicId) {
if ($systemId) {
if (!list($parser, $fp) = new_xml_parser($systemId)) {
printf("Could not open entity %s at %s\n", $openEntityNames,
$systemId);
return false;
}
while ($data = fread($fp, 4096)) {
if (!xml_parse($parser, $data, feof($fp))) {
printf("XML error: %s at line %d while parsing entity %s\n",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser), $openEntityNames);
xml_parser_free($parser);
return false;
}
}
xml_parser_free($parser);
return true;
}
return false;
}
function new_xml_parser($file) {
global $parser_file;
$xml_parser = xml_parser_create();
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 1);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
xml_set_processing_instruction_handler($xml_parser, "PIHandler");
xml_set_default_handler($xml_parser, "defaultHandler");
xml_set_external_entity_ref_handler($xml_parser, "externalEntityRefHandler");
if (!($fp = @fopen($file, "r"))) {
return false;
}
if (!is_array($parser_file)) {
settype($parser_file, "array");
}
$parser_file[$xml_parser] = $file;
return array($xml_parser, $fp);
}
if (!(list($xml_parser, $fp) = new_xml_parser($file))) {
die("could not open XML input");
}
print "<pre>";
while ($data = fread($fp, 4096)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
die(sprintf("XML error: %s at line %d\n",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
print "</pre>";
print "parse complete\n";
xml_parser_free($xml_parser);
?> |
|
Esimerkki 4. xmltest.xml <?xml version='1.0'?>
<!DOCTYPE chapter SYSTEM "/just/a/test.dtd" [
<!ENTITY plainEntity "FOO entity">
<!ENTITY systemEntity SYSTEM "xmltest2.xml">
]>
<chapter>
<TITLE>Title &plainEntity;</TITLE>
<para>
<informaltable>
<tgroup cols="3">
<tbody>
<row><entry>a1</entry><entry morerows="1">b1</entry><entry>c1</entry></row>
<row><entry>a2</entry><entry>c2</entry></row>
<row><entry>a3</entry><entry>b3</entry><entry>c3</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>
&systemEntity;
<section id="about">
<title>About this Document</title>
<para>
<!-- this is a comment -->
<?php print 'Hi! This is PHP version '.phpversion(); ?>
</para>
</section>
</chapter> |
|
This file is included from xmltest.xml:
Esimerkki 5. xmltest2.xml <?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY testEnt "test entity">
]>
<foo>
<element attrib="value"/>
&testEnt;
<?php print "This is some more PHP code being executed."; ?>
</foo> |
|
User Contributed Notes XML parser functions |
|
12-Jan-1999 10:05 |
|
In the installation section, be sure that the indents in the code you add
to the Makefile are tabs, not spaces. make will choke otherwise.
Additionally, you need to copy two header files into your
/usr/local/include or equivalent:
$EXPAT_SRC/xmltok/xmltok.h
$EXPAT_SRC/xmlparse/xmlparse.h
|
|
07-Jul-1999 04:21 |
|
When using the XML parser, make sure you're not using the magic quotes
option (e.g. use set_magic_quotes_runtime(0) if it's not the compiled
default), otherwise you'll get 'not well-formed' errors when dealing with
tags with attributes set in them.
|
|
16-Aug-1999 10:13 |
|
"The expat library should be installed somewhere your compiler can
find it." is a little vague. Here is a quick install guide:
1) download expat.zip
2) mv expat.zip /dir/of/expat
3) unzip expat.zip
4) cd expat
5) Add these lines to Makefile:
libexpat.a: $(OBJS)
(tab) ar -rc $@ $(OBJS)
(tab) ran lib $@
6) make
7) cp libexpat.a /usr/local/lib
8) mkdir /usr/local/include/xml
9) cd xmlparse
10) cp *.h /usr/local/include/xml
11) cd ../xmltok
12) cp *.h /usr/local/include/xml
13) cd ../xmlwf
14) cp *.h /usr/local/include/xml
Then reconfigure and make php, then apache.
|
|
30-Oct-1999 04:40 |
|
I'm not sure if anyone else has had or will have this problem, but here is
my solution. For some reason, the PHP compile did recognise the Apache
1.3.9 support for XML. I circumvented this by creating a link from
/usr/local/include/xml to the Apache src/lib/expat-lite directory. I then
however ran into problems with the Apache compile. It was looking for
libexpat.a which hadn't been created. I tried to create this by
downloading the source and modifying the Makefile, but it wasnt created
for some reason. Finally, I resorted to creating the libexpat.a file by
hand:
ar -rc xmltok/xmltok.o
ar -q xmltok/xmlrole.o
ar -q xmlwf/xmlwf.o
ar -q xmlwf/xmlfile.o
ar -q xmlwf/codepage.o
ar -q xmlparse/xmlparse.o
ar -q xmlparse/hashtable.o
I hope this helps someone, because it would have saved me a lot of time.
Also note that I was getting an ANSI non-compliance error in the Apache
build process. After added the -v swicth to line 2141 as pointed out in
the FAQ, I found out it was the missing libexpat.a package.
|
|
15-Dec-1999 03:25 |
|
Remember, that when adding the lines for making:
libexpat.a: $(OBJS)
<tab> ar -rc $@ $(OBJS)
<tab> ranlib $@
,you'll have to do a "make libexpat.a" after running
"make".
|
|
17-Feb-2000 05:31 |
|
The link
is no active. For the expat.zip do a FTP file search.
|
|
28-Sep-2000 01:39 |
|
I've discovered some unusual behaviour in this API when ampersand entities
are parsed in cdata; for some reason the parser breaks up the section
around the entities, and calls the handler repeated times for each of the
sections. If you don't allow for this oddity and you are trying to put the
cdata into a variable, only the last part will be stored.
You can get around this with a line like:
$foo .= $cdata;
If the handler is called several times from the same tag, it will append
them, rather than rewriting the variable each time. If the entire cdata
section is returned, it doesn't matter.
May happen for other entities, but I haven't investigated.
Took me a while to figure out what was happening; hope this saves someone
else the trouble.
|
|
06-Oct-2000 07:37 |
|
There's a really good article on XML parsing with PHP at
|
|
31-Mar-2001 12:35 |
|
Excellent IMHO XPath library for XML manipulation. Doesn't requires the
XML libraries to be installed. Take a look:
|
|
24-Jan-2002 02:43 |
|
I had to TRIM the data when I passed one large String containig a
wellformed XML-File to xml_parse. The String was read by CURL, which
aparently put a BLANK at the end of the String. This BLANK produced a
"XML not wellformed"-Error in xml_parse!
|
|
26-Feb-2002 10:11 |
|
For newbies wanting a good tutorial on how to actually get started and
where to go from this listing of functions, then visit:
It shows an excellent example of how to read the XML data into a class
file so you can actually process it, not just display it all pretty-like,
like many tutorials on PHP/XML seem to be doing.
|
|
22-Mar-2002 07:16 |
|
In reference to the note made by [email protected] about parsing entities:
I could be wrong, but since it is possible to define your own entities
within an XML DTD, the cdata handler function parses these individually to
allow for your own implementation of those entities within your cdata
handler.
|
|
15-Apr-2002 08:23 |
|
I put up a good, simple, real world example of how to parse XML documents.
While the sample grabs stock quotes off of the web, you can tweak it to do
whatever you need.
|
|
|
| |