XL. XML Parser Functions

Introduction

About XML

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 http://www.w3.org/XML/.

Installation

This extension uses expat, which can be found at http://www.jclark.com/xml/. 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 http://www.guardian.no/~ssb/phpxml.html.

On UNIX, run configure with the --with-xml option. The expat library should be installed somewhere your compiler can find it. 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.

About This Extension

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:

Table 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

PHP function to set handler

Event description

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_handl er

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.

Case Folding

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.

Error Codes

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

Character Encoding

PHP's XML extension supports the Unicode 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.

Some Examples

Here are some example PHP scripts parsing XML documents.

XML Element Structure Example

This first example displays the stucture of the start elements in a document with indentation.

Example 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, $attrs)

{

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);

XML Tag Mapping Example

Example 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, $attrs)

{

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);

XML External Entity Example

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.)

Example 3. External Entity Example

$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);

?>

Example 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;

<sect1 id="about">

<title>About this Document</title>

<para>

<!-- this is a comment -->

<?php print 'Hi! This is PHP version '.phpversion(); ?>

</para>

</sect1>

</chapter>

This file is included from xmltest.xml:

Example 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>

XML Parser Functions

xml_parser_create

Name

xml_parser_create — create an XML parser

Description

int xml_parser_create(string [encoding]);

encoding (optional)

Which character encoding the parser should use. The following character encodings are supported:

-8859-1 (default)

ASCII

-8

This function creates an XML parser and returns a handle for use by other XML functions. Returns

false on failure.

xml_set_element_handler

Name

xml_set_element_handler — set up start and end element handlers

Description

int xml_set_element_handler(int parser, string startElementHandler, string

endElementHandler);

Sets the element handler functions for the XML parser parser. startElementHandler and endElementHandler are strings containing the names of functions that must exist when xml_parse is called for parser.

The function named by startElementHandler must accept three parameters:

startElementHandler(int parser, string name, string attribs);

parser

The first parameter, parser, is a reference to the XML parser calling the handler.

name

The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters.

attribs

The third parameter, attribs, contains an associative array with the element's attributes (if any). The keys of this array are the attribute names, the values are the attribute values. Attribute names are case-folded on the same criteria as element names. Attribute values are not case-folded.

The original order of the attributes can be retrieved by walking through attribs the normal way, using each. The first key in the array was the first attribute, and so on.

The function named by endElementHandler must accept two parameters:

endElementHandler(int parser, string name);

parser

The first parameter, parser, is a reference to the XML parser calling the handler.

name

The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters.

If a handler function is set to an empty string, or false, the handler in question is disabled. True is returned if the handlers are set up, false if parser is not a parser.

There is currently no support for object/method handlers.

xml_set_character_data_handler

Name

xml_set_character_data_handler — set up character data handler

Description

int xml_set_character_data_handler(int parser, string handler);

Sets the character data handler function for the XML parser parser. handler is a string containing the name of a function that must exist when xml_parse is called for parser.

The function named by handler must accept two parameters:

handler(int parser, string data);

parser

The first parameter, parser, is a reference to the XML parser calling the handler.

data

The second parameter, data, contains the character data as a string.

If a handler function is set to an empty string, or false, the handler in question is disabled. True is returned if the handler is set up, false if parser is not a parser.

There is currently no support for object/method handlers.

xml_set_processing_instruction_handler

Name

xml_set_processing_instruction_handler — set up processing instruction (PI) handler

Description

int xml_set_processing_instruction_handler(int parser, string handler);

Sets the processing instruction (PI) handler function for the XML parser parser. handler is a string containing the name of a function that must exist when xml_parse is called for parser.

A processing instruction has the following format:

<?target data?>

You can put PHP code into such a tag, but be aware of one limitation: in an XML PI, the PI end tag (?>) can not be quoted, so this character sequence should not appear in the PHP code you embed with PIs in XML documents. If it does, the rest of the PHP code, as well as the "real" PI end tag, will be treated as character data.

The function named by handler must accept three parameters:

handler(int parser, string target, string data);

parser

The first parameter, parser, is a reference to the XML parser calling the handler.

target

The second parameter, target, contains the PI target.

data

The third parameter, data, contains the PI data.

If a handler function is set to an empty string, or false, the handler in question is disabled. True is returned if the handler is set up, false if parser is not a parser.

There is currently no support for object/method handlers.

xml_set_default_handler

Name

xml_set_default_handler — set up default handler

Description

int xml_set_default_handler(int parser, string handler);

Sets the default handler function for the XML parser parser. handler is a string containing the name of a function that must exist when xml_parse is called for parser.

The function named by handler must accept two parameters:

handler(int parser, string data);

parser

The first parameter, parser, is a reference to the XML parser calling the handler.

data

The second parameter, data, contains the character data. This may be the XML declaration, document type declaration, entities or other data for which no other handler exists.

If a handler function is set to an empty string, or false, the handler in question is disabled. True is returned if the handler is set up, false if parser is not a parser.

There is currently no support for object/method handlers.

xml_set_unparsed_entity_decl_handler

Name

xml_set_unparsed_entity_decl_handler — set up unparsed entity declaration handler

Description

int xml_set_unparsed_entity_decl_handler(int parser, string handler);

Sets the unparsed entity declaration handler function for the XML parser parser. handler is a string containing the name of a function that must exist when xml_parse is called for parser.

This handler will be called if the XML parser encounters an external entity declaration with an NDATA declaration, like the following:

<!ENTITY name {publicId | systemId} NDATA notationName>

See section 4.2.2 of the XML 1.0 spec for the definition of notation declared external entities. The function named by handler must accept six parameters:

handler(int parser, string entityName, string base, string systemId, string

publicId, string notationName);

parser

The first parameter, parser, is a reference to the XML parser calling the handler.

entityName

The name of the entity that is about to be defined.

base

This is the base for resolving the system identifier (systemId) of the external entity. Currently this parameter will always be set to an empty string.

systemId

System identifier for the external entity.

publicId

Public identifier for the external entity.

notationName

Name of the notation of this entity (see xml_set_notation_decl_handler).

If a handler function is set to an empty string, or false, the handler in question is disabled. True is returned if the handler is set up, false if parser is not a parser.

There is currently no support for object/method handlers.

xml_set_notation_decl_handler

Name

xml_set_notation_decl_handler — set up notation declaration handler

Description

int xml_set_notation_decl_handler(int parser, string handler);

Sets the notation declaration handler function for the XML parser parser. handler is a string containing the name of a function that must exist when xml_parse is called for parser.

A notation declaration is part of the document's DTD and has the following format:

<!NOTATION name {systemId | publicId}>

See section 4.7 of the XML 1.0 spec for the definition of notation declarations. The function named by handler must accept five parameters:

handler(int parser, string notationName, string base, string systemId, string

publicId);

parser

The first parameter, parser, is a reference to the XML parser calling the handler.

notationName

This is the notation's name, as per the notation format described above.

base

This is the base for resolving the system identifier (systemId) of the notation declaration. Currently this parameter will always be set to an empty string.

systemId

System identifier of the external notation declaration.

publicId

Public identifier of the external notation declaration.

If a handler function is set to an empty string, or false, the handler in question is disabled. True is returned if the handler is set up, false if parser is not a parser.

There is currently no support for object/method handlers.

xml_set_external_entity_ref_handler

Name

xml_set_external_entity_ref_handler — set up external entity reference handler

Description

int xml_set_external_entity_ref_handler(int parser, string handler);

Sets the notation declaration handler function for the XML parser parser. handler is a string containing the name of a function that must exist when xml_parse is called for parser.

The function named by handler must accept five parameters, and should return an integer value. If the value returned from the handler is false (which it will be if no value is returned), the XML parser will stop parsing and xml_get_error_code will return XML_ERROR_EXTERNAL_ENTITY_HANDLING.

int handler(int parser, string openEntityNames, string base, string systemId, string publicId);

parser

The first parameter, parser, is a reference to the XML parser calling the handler.

openEntityNames

The second parameter, openEntityNames, is a space-separated list of the names of the entities that are open for the parse of this entity (including the name of the referenced entity).

base

This is the base for resolving the system identifier (systemid) of the external entity. Currently this parameter will always be set to an empty string.

systemId

The fourth parameter, systemId, is the system identifier as specified in the entity declaration.

publicId

The fifth parameter, publicId, is the public identifier as specified in the entity declaration, or an empty string if none was specified; the whitespace in the public identifier will have been normalized as required by the XML spec.

If a handler function is set to an empty string, or false, the handler in question is disabled. True is returned if the handler is set up, false if parser is not a parser.

There is currently no support for object/method handlers.

xml_parse

Name

xml_parse — start parsing an XML document

Description

int xml_parse(int parser, string data, int [isFinal]);

parser

A reference to the XML parser to use.

data

Chunk of data to parse. A document may be parsed piece-wise by calling xml_parse several times with new data, as long as the isFinal parameter is set and true when the last data is parsed.

isFinal (optional)

If set and true, data is the last piece of data sent in this parse.

When the XML document is parsed, the handlers for the configured events are called as many times as necessary, after which this function returns true or false.

True is returned if the parse was successful, false if it was not successful, or if parser does not refer to a valid parser. For unsuccessful parses, error information can be retrieved with xml_get_error_code, xml_error_string, xml_get_current_line_number, xml_get_current_column_number and xml_get_current_byte_index.

xml_get_error_code

Name

xml_get_error_code — get XML parser error code

Description

int xml_get_error_code(int parser);

parser

A reference to the XML parser to get error code from.

This function returns false if parser does not refer to a valid parser, or else it returns one of the error codes listed in the error codes section.

xml_error_string

Name

xml_error_string — get XML parser error string

Description

string xml_error_string(int code);

code

An error code from xml_get_error_code.

Returns a string with a textual description of the error code code, or false if no description was found.

xml_get_current_line_number

Name

xml_get_current_line_number — get current line number for an XML parser

Description

int xml_get_current_line_number(int parser);

parser

A reference to the XML parser to get line number from.

This function returns false if parser does not refer to a valid parser, or else it returns which line the parser is currently at in its data buffer.

xml_get_current_column_number

Name

xml_get_current_column_number — get current column number for an XML parser

Description

int xml_get_current_column_number(int parser);

parser

A reference to the XML parser to get column number from.

This function returns false if parser does not refer to a valid parser, or else it returns which column on the current line (as given by xml_get_current_line_number) the parser is currently at.

xml_get_current_byte_index

Name

xml_get_current_byte_index — get current byte index for an XML parser

Description

int xml_get_current_byte_index(int parser);

parser

A reference to the XML parser to get byte index from.

This function returns false if parser does not refer to a valid parser, or else it returns which byte index the parser is currently at in its data buffer (starting at 0).

xml_parser_free

Name

xml_parser_free — free an XML parser

Description

string xml_parser_free(int parser);

parser

A reference to the XML parser to free.

This function returns false if parser does not refer to a valid parser, or else it frees the parser and returns true.

xml_parser_set_option

Name

xml_parser_set_option — set options in an XML parser

Description

int xml_parser_set_option(int parser, int option, mixed value);

parser

A reference to the XML parser to set an option in.

option

Which option to set. See below.

value

The option's new value.

This function returns false if parser does not refer to a valid parser, or if the option could not be set. Else the option is set and true is returned.

The following options are available:

Table 1. XML parser options

Option constant

Data type

Description

XML_OPTION_CASE_FOLDI NG integer Controls whether case-folding is enabled for this XML parser. Enabled by default.
XML_OPTION_TARGET_EN CODING string

Sets which target encoding to use in this XML parser. By default, it is set to the same as the source encoding used by xml_parser_create.

Supported target encodings are ISO-8859-1, US-ASCII and UTF-8.

xml_parser_get_option

Name

xml_parser_get_option — get options from an XML parser

Description

mixed xml_parser_get_option(int parser, int option);

parser

A reference to the XML parser to get an option from.

option

Which option to fetch. See xml_parser_set_option for a list of options.

This function returns false if parser does not refer to a valid parser, or if the option could not be set. Else the option's value is returned.

See xml_parser_set_option for the list of options.

utf8_decode

Name

utf8_decode — converts a UTF-8 encoded string to ISO-8859-1

Description

string utf8_decode(string data);

This function decodes data, assumed to be UTF-8 encoded, to ISO-8859-1. See utf8_encode for an explaination of UTF-8 encoding.

utf8_encode

Name

utf8_encode — encodes an ISO-8859-1 string to UTF-8

Description

string utf8_encode(string data);

This function encodes the string data to UTF-8, and returns the encoded version. UTF-8 is a standard mechanism used by Unicodefor encoding wide character values into a byte stream. UTF-8 is transparent to plain ASCII characters, is self-synchronized (meaning it is possible for a program to figure out where in the bytestream characters start) and can be used with normal string comparison functions for sorting and such. PHP encodes UTF-8 characters in up to four bytes, like this:

Table 1. UTF-8 encoding

bytes

bits

representation

1 7 0bbbbbbb
2 11 110bbbbb 10bbbbbb
3 16 1110bbbb 10bbbbbb 10bbbbbb
4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb

Each b represents a bit that can be used to store character data.