0% found this document useful (0 votes)
24 views

adv_xml_and_web_srv

The document provides an overview of advanced XML and web services in PHP, covering key concepts such as XML namespaces, validation methods (DTD, XML Schema, RelaxNG), and XPath for querying XML documents. It also discusses various PHP extensions for XML handling, including libxml, DOM, and SimpleXML, along with examples of their usage. Additionally, it highlights error handling in XML processing and demonstrates how to create and manipulate XML documents using PHP.

Uploaded by

amanvarma9996
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views

adv_xml_and_web_srv

The document provides an overview of advanced XML and web services in PHP, covering key concepts such as XML namespaces, validation methods (DTD, XML Schema, RelaxNG), and XPath for querying XML documents. It also discusses various PHP extensions for XML handling, including libxml, DOM, and SimpleXML, along with examples of their usage. Additionally, it highlights error handling in XML processing and demonstrates how to create and manipulate XML documents using PHP.

Uploaded by

amanvarma9996
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 151

Advanced XML

and Web Services


in PHP
March 29, 2006
Robert Richards
Agenda
 Introduction to Terms and Concepts
 Libxml
 DOM
 SimpleXML
 SAX (ext/xml)
 XMLReader
 XSL
 XMLWriter
 SOAP (ext/soap)
XML Namespaces
 An XML Namespace is a collection of names
identified by a URI.
 They are applicable to elements and attributes.
 Namespaces may or may not be associated
with a prefix.
 xmlns:rob="urn:rob"
 xmlns=http://www.example.com/rob
 Attributes never reside within a default
namespace.
 It is illegal to have two attributes with the
same localname and same namespace on the
same element.
XML Namespace
Example
<order num="1001">
<shipping>
<name type="care_of">John Smith</name>
<address>123 Here</address>
</shipping>
<billing>
<name type="legal">Jane Doe</name>
<address>456 Somewhere else</address>
</billing>
</order>
XML Namespace
Example
rder num="1001" xmlns="urn:order"
xmlns:ship="urn:shipping"
xmlns:bill="urn:billing">
<ship:shipping>
<ship:name type="care_of">John Smith</ship:nam
<ship:address>123 Here</ship:address>
</ship:shipping>
<bill:billing>
<bill:name type="legal">Jane Doe</bill:name>
<bill:address>456 Somewhere else</bill:address>
</bill:billing>
order>
Illegal Namespace Usage
<order num="1001" xmlns="urn:order"
xmlns:order="urn:order"
xmlns:ship="urn:order">
<shipping ship:type="fed_ex" type="fed_ex">
<name ship:type="care_of"
order:type="legal">John
Smith</ship:name>
</ship:shipping>
</order>
Illegal Namespace Usage
<order num="1001" xmlns="urn:order"
xmlns:order="urn:order"
xmlns:ship="urn:order">
<shipping ship:type="fed_ex" type="fed_ex">
<name ship:type="care_of"
order:type="legal">John
Smith</ship:name>
</ship:shipping>
</order>
<!-- attributes on shipping element are valid ! -->
Reserved Namespaces
and Prefixes
 The prefix xml is bound to
http://www.w3.org/XML/1998/names
pace.
 The prefix xmlns is bound to
http://www.w3.org/2000/xmlns/.
 Prefixes should also not begin with
the characters xml.
Schemas and Validation
 Validation insures an XML document
conforms to a set of defined rules.
 Multiple mechanisms exist to write
document rule sets:
 Document Type Definition (DTD)
 XML Schema

 RelaxNG
Document Type
Definition (DTD)
validation/courses-dtd.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE courses [
<!ELEMENT courses (course+)>
<!ELEMENT course (title, description, credits, lastmodified)>
<!ATTLIST course cid ID #REQUIRED>
<!ELEMENT title (#PCDATA)>
<!ELEMENT description (#PCDATA)>
<!ELEMENT credits (#PCDATA)>
<!ELEMENT lastmodified (#PCDATA)>
]>
<courses>
<course cid="c1">
<title>Basic Languages</title>
<description>Introduction to Languages</description>
<credits>1.5</credits>
<lastmodified>2004-09-01T11:13:01</lastmodified>
</course>
<course cid="c2">
...
</course>
</courses>
DTD and IDs
validation/course-id.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE courses [
<!ATTLIST course cid ID #REQUIRED>
]>
<courses>
<course cid="c1">
<title xml:id="t1">Basic Languages</title>
<description>Introduction to Languages</description>
</course>
<course cid="c2">
<title xml:id="t3">French I</title>
<description>Introduction to French</description>
</course>
<course cid="c3">
<title xml:id="t3">French II</title>
<description>Intermediate French</description>
</course>
</courses>
XML Schema
validation/course.xsd
<?xml version="1.0"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="courses">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="course" minOccurs="1"
maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="description"
type="xsd:string"/>
<xsd:element name="credits"
type="xsd:decimal"/>
<xsd:element name="lastmodified"
type="xsd:dateTime"/>
</xsd:sequence>
<xsd:attribute name="cid" type="xsd:ID"/>
</xsd:complexType>
</xsd:element>
RelaxNG
validation/course.rng
grammar xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">

<start>
<element name="courses">
<zeroOrMore>
<element name="course">
<attribute name="cid"><data type="ID"/></attribute>
<element name="title"><text/></element>
<element name="description"><text/></element>
<element name="credits"><data type="decimal"/></element>
<element name="lastmodified"><data type="dateTime"/></element>
</element>
</zeroOrMore>
</element>
</start>

/grammar>
XPath
 Language to locate and retrieve
information from an XML document
 A foundation for XSLT
 An XML document is a tree
containing nodes
 The XML document is the root node
 Locations are addressable similar to
the syntax for a filesystem
XPath Reference
Document
xpath/courses.xml
courses xmlns:t="http://www.example.com/title">
<course xml:id="c1">
<t:title>Basic Languages</t:title>
<description>Introduction to Languages</description>
</course>
<course xml:id="c2">
<t:title>French I</t:title>
<description>Introduction to French</description>
</course>
<course xml:id="c3">
<t:title>French II</t:title>
<description>Intermediate French</description>
<pre-requisite cref="c2" />
<?phpx A PI Node ?>
<defns xmlns="urn:default">content</defns>
</course>
courses>
XPath Location Example
xpath/location.php

Expression:
/courses/course/description
//description
/courses/*/description
//description[ancestor::course]

Resulting Nodset:
<description>Introduction to Languages</description>
<description>Introduction to French</description>
<description>Intermediate French</description>
XPath Function Example
xpath/function.php

string(/courses/course/pre-
requisite[@cref="c2"]/..)

French II
Intermediate French

content
XPath and Namespaces
xpath/namespaces.php

title
Empty NodeSet

t:title
<t:title>Basic Languages</t:title>
<t:title>French I</t:title>
<t:title>French II</t:title>

defns
Empty NodeSet

*[local-name()="defns"]
<defns xmlns="urn:default">content</defns>
PHP and XML
 PHP 5 introduced numerous
interfaces for working with XML
 The libxml2 library
(http://www.xmlsoft.org/) was
chosen to provide XML support
 The sister library libxslt provides
XSLT support
 I/O is handled via PHP streams
XML Entensions for PHP
5
 ext/libxml
 ext/xml (SAX push parser)
 ext/dom
 ext/simplexml
 ext/xmlreader (pull parser)
 ext/xmlwriter
 ext/xsl
 ext/wddx
 ext/soap
Libxml
 Contains common functionality
shared across extensions.
 Defines constants to modify parse
time behavior.
 Provides access to streams context.
 Allows modification of error
handling behavior for XML based
extensions.
Libxml: Parser Options
LIBXML_NOENT Substitute entities with
replacement content
LIBXML_DTDLOAD Load subsets but do not perform
validation
LIBXML_DTDATTR Create defaulted attributes defined
in DTD
LIBXML_DTDVALID Loads subsets and perform
validation
LIBXML_NOERROR Suppress parsing errors from
libxml2
LIBXML_NOWARNI Suppress parser warnings from
NG libxml2
LIBXML_NOBLANK Remove insignificant whitespace
Libxml: Error Handling

bool libxml_use_internal_errors ([bool


use_errors])

void libxml_clear_errors ( void )

LibXMLError libxml_get_last_error ( void )

array libxml_get_errors ( void )


Libxml: LibXMLError
Class: LibXMLError
Properties (Read-Only):
(int) level
(int) code
(int) column
(string) message
(string) file
(int) line

LibXMLError::code Values:
LIBXML_ERR_NONE
LIBXML_ERR_WARNING
LIBXML_ERR_ERROR
LIBXML_ERR_FATAL
LibXMLError Example
libxml/error.php
<?php
/* Regular Error Handling */
$dom = new DOMDocument();
$dom->loadXML('<root>');

/* New Error Handling */


libxml_use_internal_errors(TRUE);

if (! $dom->loadXML('root')) {
$arrError = libxml_get_errors();
foreach ($arrError AS $xmlError) {
var_dump($xmlError);
}
} else {
print "Document Loaded";
}
?>
LibXMLError Result
PHP Warning: DOMDocument::loadXML(): Premature end of data in tag
root line 1 in Entity, line: 1 in
/home/rrichards/workshop/libxml/error.php on line 4

Warning: DOMDocument::loadXML(): Premature end of data in tag root


line 1 in Entity, line: 1 in /home/rrichards/workshop/libxml/error.php on
line 4

New Error Handling:


object(LibXMLError)#2 (6) {
["level"]=> int(3)
["code"]=> int(4)
["column"]=> int(1)
["message"]=> string(34) "Start tag expected, '<' not found"
["file"]=> string(0) ""
["line"]=> int(1)
}
DOM
 Tree based parser
 Allows for creation and editing of XML
documents
 W3C Specification with DOM Level 2/3
compliancy
 Provides XPath support
 Provides XInclude Support
 Ability to work with HTML documents
 Zero copy interoperability with SimpleXML
 Replacement for ext/domxml from PHP 4
DOMNode Classes
 DOMDocument  DOMEntityReference
 DOMElement  DOMProcessingInstru
 DOMAttr ction
 DOMComment
 DOMNameSpaceNode
 DOMDocumentFragm
 DOMDocumentTyp
ent
e
 DOMCharacterData
 DOMNotation
 DOMText
 DOMEntity
 DOMCdataSection
Additional DOM Classes
 DOMException
 DOMImplementation
 DOMNodeList
 DOMNamedNodeMap
 DOMXPath
DOM: Document
Navigation
dom/navigate.php
/* Find first description element in subtrees */
function locateDescription($nodeset) {
foreach ($nodeset AS $node) {
if ($node->nodeType == XML_ELEMENT_NODE && $node->nodeName
== 'description') {
$GLOBALS['arNodeSet'][] = $node;
return;
}
if ($node->hasChildNodes()) { locateDescription($node-
>childNodes); }
}
}

$dom = new DOMDocument();


$dom->load('course.xml');
$root = $dom->documentElement;

$arNodeSet = array();

if ($root->hasChildNodes()) { locateDescription($root->childNodes); }

foreach ($arNodeSet AS $key=>$node) { print "#$key: ".$node-


>nodeValue."\n"; }
DOM: Document
Navigation Results
#0: Introduction to Languages
#1: Introduction to French
#2: Intermediate French
DOM:Document
Navigation #2
dom/navigate-2.php
<?php
$dom = new DOMDocument();
$dom->load('course.xml');

$nodelist = $dom->getElementsByTagName('description');

foreach ($nodelist AS $key=>$node) {


print "#$key: ".$node->nodeValue."\n";
}
?>
Results:
#0: Introduction to Languages
#1: Introduction to French
#2: Intermediate French
DOM: Navigation
Optimized
dom/navigate-optimized.php
function locateDescription($node) {
while($node) {
if ($node->nodeType == XML_ELEMENT_NODE && $node->nodeName ==
'description') {
$GLOBALS['arNodeSet'][] = $node;
return;
}
locateDescription($node->firstChild);
$node = $node->nextSibling;
}
}

$dom = new DOMDocument();


$dom->load('course.xml');
$root = $dom->documentElement;
$arNodeSet = array();

locateDescription($root->firstChild);

foreach ($arNodeSet AS $key=>$node) {


print "#$key: ".$node->nodeValue."\n";
}
DOM: Creating a Simple
Tree
dom/create_simple_tree.php
$doc = new DOMDocument();

$root = $doc->createElement("tree");
$doc->appendChild($root);

$root->setAttribute("att1", "att1 value");

$attr2 = $doc->createAttribute("att2");
$attr2->appendChild($doc->createTextNode("att2 value"));
$root->setAttributeNode($attr2);

$child = $root->appendChild($doc->createElement("child"));

$comment = $doc->createComment("My first Document");


$doc->insertBefore($comment, $root);

$pi = $doc->createProcessingInstruction("php", 'echo "Hello World!"');


$root->appendChild($pi);

$cdata = $doc->createCdataSection("special chars: & < > '");


$child->appendChild($cdata);
DOM: Simple Tree
Output
<?xml version="1.0"?>
<!--My first Document-->
<tree att1="att1 value" att2="att2 value">
<child><![CDATA[special chars: & < > ']]></child>
<?php echo "Hello World!"?>
</tree>
DOM: Creating an Atom
Feed
define('ATOMNS',dom/atom_feed_creation.php
'http://www.w3.org/2005/Atom');
$feed_title = "Example Atom Feed";
$alt_url = "http://www.example.org/";
$feed = "http://www.example.org/atom/";

$doc = new DOMDocument("1.0", "UTF-8");

function create_append_Atom_elements($doc, $name, $value=NULL,


$parent=NULL) {
if ($value)
$newelem = $doc->createElementNS(ATOMNS, $name, $value);
else
$newelem = $doc->createElementNS(ATOMNS, $name);
if ($parent) { return $parent->appendChild($newelem); }
}

$feed = create_append_Atom_elements($doc, 'feed', NULL, $doc);


create_append_Atom_elements($doc, 'title', $feed_title, $feed);
create_append_Atom_elements($doc, 'subtitle', $feed_title, $feed);
create_append_Atom_elements($doc, 'id', $alt_url, $feed);
create_append_Atom_elements($doc, 'updated', date('c'), $feed);

$doc->formatOutput = TRUE;
print $doc->saveXML();
DOM: Creating an Atom
Feed Result (initial structure)
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Example Atom Feed</title>
<subtitle>Example Atom Feed</subtitle>
<id>http://www.example.org/</id>

<updated>2006-03-23T01:39:40-05:00</updated
>
</feed>
DOM: Creating an Atom
Feed
dom/atom_feed_creation.php
$entry = create_append_Atom_elements($doc, 'entry', NULL, $feed);

$title = create_append_Atom_elements($doc, 'title', 'My first entry', $entry);


$title->setAttribute('type', 'text');

$link = create_append_Atom_elements($doc, 'link', NULL, $entry);


$link->setAttribute('type', 'text/html');
$link->setAttribute('rel', 'alternate');
$link->setAttribute('href', 'http://www.example.org/entry-url');
$link->setAttribute('title', 'My first entry');

$author = create_append_Atom_elements($doc, 'author', NULL, $entry);


create_append_Atom_elements($doc, 'name', 'Rob', $author);

create_append_Atom_elements($doc, 'id', 'http://www.example.org/entry-guid',


$entry);
create_append_Atom_elements($doc, 'updated', date('c'), $entry);
create_append_Atom_elements($doc, 'published', date('c'), $entry);

$content = create_append_Atom_elements($doc, 'content', NULL, $entry);


$cdata = $doc->createCDATASection('This is my first Atom entry!<br />More
to follow');
$content->appendChild($cdata);

$doc->formatOutput = TRUE;
print $doc->saveXML();
DOM: Creating an Atom
Feed
Result
dom/atomoutput.xml
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Example Atom Feed</title>
<subtitle>Example Atom Feed</subtitle>
<id>http://www.example.org/</id>
<updated>2006-03-23T01:53:59-05:00</updated>
<entry>
<title type="text">My first entry</title>
<link type="text/html" rel="alternate"
href="http://www.example.org/entry-url" title="My first
entry"/>
<author>
<name>Rob</name>
</author>
<id>http://www.example.org/entry-guid</id>
<updated>2006-03-23T01:53:59-05:00</updated>
<published>2006-03-23T01:53:59-05:00</published>
<content><![CDATA[This is my first Atom entry!<br />More to
follow]]></content>
</entry>
</feed>
DOM: Document Editing
dom/editing.php
$dom->load('atomoutput.xml');

$child = $dom->documentElement->firstChild;
while($child && $child->nodeName != "entry") { $child = $child-
>nextSibling; }

if ($child && ($child = $child->firstChild)) {


while($child && $child->nodeName != "title") { $child = $child-
>nextSibling; }
if ($child) {
$child->setAttribute('type', 'html');
$text = $child->firstChild;
$text->nodeValue = "<em>My first entry</em>";

while($child) {
if ($child->nodeName == "updated") {
$text = $child->firstChild;
$text->nodeValue = date('c');
break;
}
$child = $child->nextSibling;
}
}
}
print $dom->saveXML();
DOM: Editing
dom/new_atomoutput.xml
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Example Atom Feed</title>
<subtitle>Example Atom Feed</subtitle>
<id>http://www.example.org/</id>
<updated>2006-03-23T01:53:59-05:00</updated>
<entry>
<title type="html">&lt;em&gt;My first entry&lt;/em&gt;</title>
<link type="text/html" rel="alternate"
href="http://www.example.org/entry-url" title="My first
entry"/>
<author>
<name>Rob</name>
</author>
<id>http://www.example.org/entry-guid</id>
<updated>2006-03-23T02:29:22-05:00</updated>
<published>2006-03-23T01:53:59-05:00</published>
<content><![CDATA[This is my first Atom entry!<br />More to
follow]]></content>
</entry>
</feed>
DOM: Document
Modification
dom/modify.php
/* Assume $entry refers to the first /* These will work */
entry element within the Atom
document */
$children = $entry->childNodes;
$length = $children->length - 1;
while ($entry->hasChildNodes()) {
$entry->removeChild($entry- for ($x=$length; $x >=0; $x--) {
>firstChild); $entry->removeChild($children-
}
>item($x));
OR }
$node = $entry->lastChild; OR
while($node) {
$prev = $node->previousSibling;
$entry->removeChild($node); $elem = $entry->cloneNode(FALSE);
$node = $prev; $entry->parentNode-
} >replaceChild($elem,
/* This Will Not Work! */
foreach($entry->childNodes AS $entry);
$node) {
$entry->removeChild($node);
}
DOM and Namespaces
<xsd:complexType

xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
name="ArrayOfint">
<xsd:complexContent>
<xsd:restriction base="soapenc:Array">
<xsd:attribute ref="soapenc:arrayType"
wsdl:arrayType="xsd:int[ ]"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
Dom and Namepsaces
dom/namespace.php

define("SCHEMA_NS", "http://www.w3.org/2001/XMLSchema");
define("WSDL_NS", "http://schemas.xmlsoap.org/wsdl/");
$dom = new DOMDocument();

$root = $dom->createElementNS(SCHEMA_NS, "xsd:complexType");


$dom->appendChild($root);

$root->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:wsdl",
WSDL_NS);
$root->setAttribute("name", "ArrayOfint");

$content = $root->appendChild(new DOMElement("xsd:complexContent", NULL,


SCHEMA_NS));

$restriction = $content->appendChild(new DOMElement("xsd:restriction", NULL,


SCHEMA_NS));
$restriction->setAttribute("base", "soapenc:Array");

$attribute = $restriction->appendChild(new DOMElement("xsd:attribute", NULL,


SCHEMA_NS));
$attribute->setAttribute("ref", "soapenc:arrayType");

$attribute->setAttributeNS(WSDL_NS, "wsdl:arrayType", "xsd:int[]");


DOM and Xpath
dom/xpath/dom-xpath.xml
<store>
<books>
<rare>
<book qty="4">
<name>Cannery Row</name>
<price>400.00</price>
<edition>1</edition>
</book>
</rare>
<classics>
<book qty="25">
<name>Grapes of Wrath</name>
<price>12.99</price>
</book>
<book qty="25">
<name>Of Mice and Men</name>
<price>9.99</price>
</book>
</classics>
</books>
</store>
DOM and Xpath
dom/xpath/dom-xpath.php
$doc = new DOMDocument();
$doc->load('dom-xpath.xml');
$xpath = new DOMXPath($doc);

$nodelist = $xpath->query("//name");
print "Last Book Title: ".$nodelist->item($nodelist->length - 1)-
>textContent."\n";

$nodelist = $xpath->query("//name[ancestor::rare]");
print "Last Rare Book Title: ".$nodelist->item($nodelist->length - 1)-
>nodeValue."\n";

$inventory = $xpath->evaluate("sum(//book/@qty)");
print "Total Books: ".$inventory."\n";

$inventory = $xpath->evaluate("sum(//classics/book/@qty)");
print "Total Classic Books: ".$inventory."\n";

$inventory = $xpath->evaluate("count(//book[parent::classics])");
print "Distinct Classic Book Titles: ".$inventory."\n";
DOM and Xpath Results
/* $nodelist = $xpath->query("//name")
$nodelist->item($nodelist->length - 1)->textContent */
Last Book Title: Of Mice and Men

/* $xpath->query("//name[ancestor::rare]");
$nodelist->item($nodelist->length - 1)->nodeValue */
Last Rare Book Title: Cannery Row

/* $xpath->evaluate("sum(//book/@qty)") */
Total Books: 54

/* $xpath->evaluate("sum(//classics/book/@qty)") */
Total Classic Books: 50

/* $xpath->evaluate("count(//book[parent::classics])") */
Distinct Classic Book Titles: 2
DOM and Xpath w/Namespaces
dom/xpath/dom-xpathns.xml
<store xmlns="http://www.example.com/store"
xmlns:bk="http://www.example.com/book">
<books>
<rare>
<bk:book qty="4">
<bk:name>Cannery Row</bk:name>
<bk:price>400.00</bk:price>
<bk:edition>1</bk:edition>
</bk:book>
</rare>
<classics>
<bk:book qty="25">
<bk:name>Grapes of Wrath</bk:name>
<bk:price>12.99</bk:price>
</bk:book>
<bk:book qty="25" xmlns:bk="http://www.example.com/classicbook">
<bk:name>Of Mice and Men</bk:name>
<bk:price>9.99</bk:price>
</bk:book>
</classics>
<classics xmlns="http://www.example.com/ExteralClassics">
<book qty="33">
<name>To Kill a Mockingbird</name>
<price>10.99</price>
</book>
</classics>
</books>
</store>
DOM and Xpath
w/Namespaces
dom/xpath/dom-xpathns.php
$nodelist = $xpath->query("//name");
print "Last Book Title: ".$nodelist->item($nodelist->length - 1)->textContent."\
n";
// Last Book Title: /* Why empty? */

$nodelist = $xpath->query("//bk:name");
print "Last Book Title: ".$nodelist->item($nodelist->length - 1)->textContent."\
n";
// Last Book Title: Grapes of Wrath /* Why not "Of Mice and Men" */

$nodelist = $xpath->query("//bk:name[ancestor::rare]");
print "Last Rare Book Title: ".$nodelist->item($nodelist->length - 1)-
>nodeValue."\n";
// Last Rare Book Title: /* Why empty? */

$xpath->registerNamespace("rt", "http://www.example.com/store");
$nodelist = $xpath->query("//bk:name[ancestor::rt:rare]");
print "Last Rare Book Title: ".$nodelist->item($nodelist->length - 1)-
>nodeValue."\n";
// Last Rare Book Title: Cannery Row

$xpath->registerNamespace("ext", "http://www.example.com/ExteralClassics");
$nodelist = $xpath->query("(//bk:name) | (//ext:name)");
print "Last Book Title: ".$nodelist->item($nodelist->length - 1)->textContent."\
n";
// Last Book Title: To Kill a Mockingbird
DOM and Xpath
w/Namespaces
dom/xpath/dom-xpathns.php
$xpath->registerNamespace("bk2",
"http://www.example.com/classicbook");
$nodelist = $xpath->query("//bk2:name");
print "Last Book Title (bk2): "
print $nodelist->item($nodelist->length - 1)->textContent."\n";
// Last Book Title (bk2): Of Mice and Men

Complete Results:
Last Book Title:
Last Book Title: Grapes of Wrath
Last Rare Book Title:
Last Rare Book Title: Cannery Row
Last Book Title: To Kill a Mockingbird
Last Book Title (bk2): Of Mice and Men
Performing Validation
dom/validation/validate.php

$doc = new DOMDocument();


print "DTD Validation:\n";
$doc->load('courses-dtd.xml', LIBXML_DTDVALID);
/* No errors means document is valid */

if ($doc->validate()) { print " Document Is Valid\n"; }

print "DTD Validation FAILURE:\n";


$doc->load('course-id.xml');
if ($doc->validate()) { print " Document Is Valid\n"; }

$doc->load('course.xml');
print "\nXML Schema Validation:\n";
if ($doc->schemaValidate('course.xsd') ) { print " Document
is valid\n"; }

$doc->load('course.xml');
print "\nRelaxNG Validation:\n";
if ($doc->relaxNGValidate('course.rng') ) { print " Document
is valid\n"; }
Performing Validation
Results
DTD Validation:
Document Is Valid

DTD Validation FAILURE:


Warning: DOMDocument::validate(): No declaration for element
courses in
/home/rrichards/workshop/dom/validation/validate.php on line
11
Warning: DOMDocument::validate(): No declaration for element
course in /home/rrichards/workshop/dom/validation/validate.php
on line 11
Warning: DOMDocument::validate(): No declaration for element
title in /home/rrichards/workshop/dom/validation/validate.php
on line 11
...

XML Schema Validation:


Document is valid

RelaxNG Validation:
Document is valid
Extending DOM Classes
 Overriding the constructor requires
the parent constructor to be called.
 Properties built into the DOM
classes cannot be overridden.
 Methods built into the DOM classes
may can be overridden.
 The lifespan of an extended object is
that of the object itself.
Extending DOM Classes
dom/extending/extending.php
class customElement extends DOMElement { }
class customDoc extends DOMDocument {
public $nodeName = "customDoc";

function __construct($rootName) {
parent::__construct();
if (! empty($rootName)) {
$element = $this->appendChild(new DOMElement($rootName)); }
}

function createElement($name, $value, $parent=NULL) {


$custom = new customElement($name, $value);
if ($parent && ($parent instanceof DOMElement)) {
$parent->appendChild($custom); }
return $custom;
}
}

$myc = new customDoc("root");


$myelement = $myc->createElement("myname", "myvalue", $myc-
>documentElement);
if ($myelement instanceof customElement) { print "This is a
customElement\n"; }
print $myc->nodeName."\n";
print $myc->saveXML();
DOM Object Scope
dom/extending/object_scope.php
class customElement extends DOMElement { }

function changeit($doc) {
$myelement = new customElement("custom", "element2");
$doc->replaceChild($myelement, $doc->documentElement);
print "Within changeit function: ".get_class($doc-
>documentElement)."\n";
}

$doc = new DOMDocument();

$myelement = $doc->appendChild(new customElement("custom",


"element"));
print "After Append: ".get_class($myelement)."\n";

unset($myelement);
print "After unset: ".get_class($doc->documentElement)."\n";

changeit($doc);
print "Outside changeit(): ".get_class($doc->documentElement)."\n";

After Append: customElement


After unset: DOMElement
Within changeit function: customElement
Outside changeit(): DOMElement
DOM:Common Issues
 DOM Objects and Sessions
 Removing Nodes while iterating a Nodeset
skips nodes
 XML Tree contains garbled characters
 Extended class is not returned from
property or method
 Elements not being returned by ID
 Entity errors are issues when loading a
document
 New DTD is not recognized by document
SimpleXML
 Provides simple access to XML documents
 Operates only on elements and attributes
 Contains XPath support
 Allows for modifications to the XML
 Zero copy interoperability with DOM
 New in PHP 5.1.3
 Elements and attributes can be added using
addChild() and addAttribute() methods.
 Node names can be retrieved by calling
getName().
SimpleXML: Consuming Yahoo
WebSearch
simplexml/yahoo_rest_results.xml
<ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:yahoo:srch"
xsi:schemaLocation="urn:yahoo:srch
http://api.search.yahoo.com/WebSearchService/V1/WebSearchResponse.xsd"
totalResultsAvailable="374000" totalResultsReturned="5"
firstResultPosition="1">
<Result>
<Title>Zend Technologies - PHP 5 In Depth - XML in PHP 5 -
What's New?</Title>
<Summary>XML in PHP 5 - What's New? By Christian Stocker.
March . . . </Summary>
<Url>http://www.zend.com/php5/articles/php5-xmlphp.php</Url>
<ClickUrl>http://uk.wrs.yahoo.com/_ylt=. . .</ClickUrl>
<ModificationDate>1143014400</ModificationDate>
<MimeType>text/html</MimeType>
<Cache>
<Url>http://uk.wrs.yahoo.com/...</Url>
<Size>112625</Size>
</Cache>
</Result>
...
</Results>
SimpleXML: Consuming Yahoo
WebSearch
simplexml/reading_rest.php
/* URL to Web Search service */
$url = 'http://api.search.yahoo.com/WebSearchService/V1/webSearch';

/* The query is separate here as the terms must be encoded. */


$url .= '?query='.rawurlencode('php5 xml');

/* Complete the URL adding App ID, limit to 5 results and only English results */
$url .= "&appid=zzz&results=5&language=en";

$sxe = simplexml_load_file($url);
/* Check for number of results returned */
if ((int)$sxe['totalResultsReturned'] > 0) {
/* Loop through each result and output title, url and modification
date */
foreach ($sxe->Result AS $result) {
print 'Title: '.$result->Title."\n";
print 'Url: '.$result->Url."\n";
print 'Mod Date: '.date ('M d Y', (int)$result-
>ModificationDate)."\n\n";
}
}
SimpleXML: Consuming Yahoo
WebSearch
RESULTS
Title: Zend Technologies - PHP 5 In Depth - XML in PHP 5 -
What's New?
Url: http://www.zend.com/php5/articles/php5-xmlphp.php
Mod Date: Mar 22 2006

Title: ONLamp.com -- Using PHP 5's SimpleXML


Url:
http://www.onlamp.com/pub/a/php/2004/01/15/simplexml.html
Mod Date: Mar 19 2006

Title: Zend Technologies - PHP 5 In Depth - SimpleXML


Url: http://www.zend.com/php5/articles/php5-simplexml.php
Mod Date: Mar 22 2006

Title: Workshop: XML in PHP5


Url: http://php5.bitflux.org/phpconf2004
Mod Date: Mar 05 2005

Title: PHP: Hypertext Preprocessor


Url: http://www.php.net/
Mod Date: Mar 13 2006
SimpleXML: Namespaces
simplexml/simplexml-namespace.php
$store = simplexml_load_file('simplexml-xpathns.xml');
$books = $store->books;

foreach ($books->classics AS $classic) {


if ($classic->book)
print $classic->book->name."\n\n";
}
/* Why only one result?: To Kill a Mockingbird */

$x = 0;
foreach ($books->classics AS $classics) {
if ($x++ == 0) {
$children =
$classics->children("http://www.example.com/classicbook");
/* Print name for the books where book element resides in a prefixed
namespace */
print $classics->children("http://www.example.com/book")->book-
>name."\n";
print $children->book->name."\n";
} else
print $classic->book->name."\n";
}
SimpleXML: Namespaces
Results
To Kill a Mockingbird

Grapes of Wrath
Of Mice and Men
To Kill a Mockingbird
SimpleXML: Xpath
simplexml/simplexml-xpathns.php

$sxe = simplexml_load_file('simplexml-xpathns.xml');

$nodelist = $sxe->xpath("//bk:name");
print "Last Book Title: ".$nodelist[count($nodelist) - 1]."\n";

$sxe->registerXPathNamespace("rt",
"http://www.example.com/store");

$nodelist = $sxe->xpath("//bk:name[ancestor::rt:rare]");
print "Last Rare Book Title: ".$nodelist[count($nodelist) - 1]."\n";

$sxe->registerXPathNamespace("ext",
"http://www.example.com/ExteralClassics");
$nodelist = $sxe->xpath("(//bk:name) | (//ext:name)");
print "Last Book Title: ".$nodelist[count($nodelist) - 1]."\n";

$sxe->registerXPathNamespace("bk2",
"http://www.example.com/classicbook");
$nodelist = $sxe->xpath("//bk2:name");
print "Last Book Title (bk2): ".$nodelist[count($nodelist) - 1]."\n";
SimpleXML: XPath
Results
Last Book Title: Grapes of Wrath
Last Rare Book Title: Cannery Row
Last Book Title: To Kill a Mockingbird
Last Book Title (bk2): Of Mice and
Men
SimpleXML: Advanced
Editing
simplexml/editing.php
$data = array(array('title'=>'Result 1', 'descript'=>'Res1 description'),
array('title'=>'Result 2', 'descript'=>'description of
Res2'),
array('title'=>'Result 3', 'descript'=>'This is result 3'));
class webservice extends simpleXMLElement {
public function appendElement($name, $value=NULL) {
$node = dom_import_simplexml($this);
$newnode = $value ? new DOMElement($name, $value) : new
DOMElement($name);
$node->appendChild($newnode);
return simplexml_import_dom($newnode, 'webservice');
}}

$rest = simplexml_load_string('<results num="0" />', 'webservice');


$rest['num'] = count($data);
foreach ($data AS $result_item) {
$result = $rest->appendElement('result');
$result->appendElement('title', $result_item['title']);
$result->appendElement('description');
$result->description = $result_item['descript']; }
print $rest->asXML();
SimpleXML: Advanced
Editing Results
<?xml version="1.0"?>
<results num="3">
<result>
<title>Result 1</title>
<description>Res1 description</description>
</result>
<result>
<title>Result 2</title>
<description>description of Res2</description>
</result>
<result>
<title>Result 3</title>
<description>This is result 3</description>
</result>
</results>
SimpleXML: Advanced Editing
PHP 5.1.3
simplexml/editing_php513.php
$data = array(array('title'=>'Result 1', 'descript'=>'Res1
description'),
array('title'=>'Result 2', 'descript'=>'description of
Res2'),
array('title'=>'Result 3', 'descript'=>'This is result 3'));

$rest = simplexml_load_string('<results num="0" />');


$rest['num'] = count($data);
foreach ($data AS $result_item) {
$result = $rest->addChild('result');
$result->addChild('title', $result_item['title']);
$result->addChild('description');
$result->description = $result_item['descript'];
}
$rest->asXML('editing_php513.xml');
SimpleXML: Removing
data
remove_data.php
<?php
$results = simplexml_load_file('editing_php513.xml');

/* Delete title from first result element */


unset($results->result->title);

/* Delete the 2nd result element - ONLY WORKS in


PHP 5.1.3 */
unset($results->result[1]);

print $results->asXML();
?>
SimpleXML: Removing
data
RESULTS
<?xml version="1.0"?>
<results num="3">
<result>
<description>Res1 description</description>
</result>
<result>
<title>Result 3</title>
<description>This is result 3</description>
</result>
</results>
Simple API for XML
(SAX)
 Event based push parser
 Low memory usage
 Works using function callbacks
 Almost completely compatible with
ext/xml from PHP 4
 Default encoding is UTF-8 rather
than ISO-8859-1 as it was in
PHP 4
SAX: Source Document
xml/xml_simple.xml

<?xml version='1.0'?>
<chapter xmlns:a="http://www.example.com/namespace-a"
xmlns="http://www.example.com/default">
<a:title>ext/xml</a:title>
<para>
First Paragraph
</para>
<a:section a:id="about">
<title>About this Document</title>
<para>
<!-- this is a comment -->
<?php echo 'Hi! This is PHP version ' . phpversion(); ?>
</para>
</a:section>
</chapter>
SAX: Simple Example
xml/xml_simple.php

<?php
function startElement($parser, $elementname, $attributes) {
print "* Start Element: $elementname \n";
foreach ($attributes as $attname => $attvalue) {
print " $attname => $attvalue \n";
}
}

function endElement($parser, $elementname) {


print "* End Element: $elementname\n";
}

function charDataHandler($parser,$data) {
if (trim($data) != "") print $data."\n";
}

function PIhandler ($parser, $target, $data) {


print "PI: $target -> $data\n";
}

function DefaultHandler($parser, $data) {


print "Default: $data\n";
}
SAX: Simple Example
xml/xml_simple.php

$parser = xml_parser_create();
/* Disable as case is significant in XML */
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING,
false);

xml_set_element_handler($parser,"startElement","endElement");
xml_set_character_data_handler($parser, "charDataHandler");
xml_set_processing_instruction_handler ($parser,
"PIhandler");
xml_set_default_handler ($parser, "DefaultHandler");

if (($fp = fopen("xml_simple.xml", "r"))) {


while ($data = fread($fp, 4096)) {
xml_parse($parser, $data, feof($fp));
}
}
?>
SAX: Simple Example
RESULTS
* Start Element: chapter
xmlns:a => http://www.example.com/namespace-a
xmlns => http://www.example.com/default
* Start Element: a:title
ext/xml
* End Element: a:title
* Start Element: para

First Paragraph
* End Element: para
* Start Element: a:section
a:id => about
* Start Element: title
About this Document
* End Element: title
* Start Element: para
Default: <!-- this is a comment -->
PI: php -> echo 'Hi! This is PHP version ' . phpversion();
* End Element: para
* End Element: a:section
* End Element: chapter
SAX: Error Handling
xml/xml_error.php
<?php
/* Malformed document */
$data = "<root>";

$parser = xml_parser_create();

if(! xml_parse($parser, $data, TRUE)) {


/* Normally die is or some other escape mechanism is also
called*/
printf("XML error: %s in line %d, column %d\n\n",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser),
xml_get_current_column_number($parser));
}

/* Magically you can also get a structured error */


$xmlError = libxml_get_last_error();
var_dump($xmlError);
?>
SAX: Error Handling
RESULTS

XML error: Invalid document end in line 1, column 7

object(LibXMLError)#1 (6) {
["level"]=>
int(3)
["code"]=>
int(5)
["column"]=>
int(7)
["message"]=>
string(41) "Extra content at the end of the document
"
["file"]=>
string(0) ""
["line"]=>
int(1)
}
SAX: Advanced Example
xml/xml_advanced.php

class cSax {
function startElement($parser, $elementname, $attributes) {
list($namespaceURI,$localName)= split("@",$elementname);
if (! $localName) {
$localName = $namespaceURI;
$namespaceURI = "";
}
print "* Start Element: $localName".
($namespaceURI ? " in $namespaceURI" : "")."\n";
foreach ($attributes as $attname => $attvalue) {
print " $attname => $attvalue \n";
}
}

function endElement($parser, $elementname) {


list($namespaceURI,$localName)= split("@",$elementname);
if (! $localName) {
$localName = $namespaceURI;
$namespaceURI = "";
}
print "* End Element: $localName".
($namespaceURI ? " in $namespaceURI" : "")."\n";
}
}
SAX: Advanced Example
xml/xml_advanced.php

$objcSax = new cSax();

$parser = xml_parser_create_ns("ISO-8859-1","@");

/* Disable as case is significant in XML */


xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING,
false);

xml_set_object($parser, $objcSax);

xml_set_element_handler($parser,"startElement","endElement");

if (($fp = fopen("xml_simple.xml", "r"))) {


while ($data = fread($fp, 4096)) {
if (! xml_parse($parser, $data, feof($fp))) {
$xmlError = libxml_get_last_error();
var_dump($xmlError);
exit;
}
}
}
SAX: Advanced Example
RESULTS

* Start Element: chapter in http://www.example.com/default


* Start Element: title in http://www.example.com/namespace-a
* End Element: title in http://www.example.com/namespace-a
* Start Element: para in http://www.example.com/default
* End Element: para in http://www.example.com/default
* Start Element: section in
http://www.example.com/namespace-a
http://www.example.com/namespace-a@id => about
* Start Element: title in http://www.example.com/default
* End Element: title in http://www.example.com/default
* Start Element: para in http://www.example.com/default
* End Element: para in http://www.example.com/default
* End Element: section in http://www.example.com/namespace-a
* End Element: chapter in http://www.example.com/default
XMLReader
 Forward moving stream based parser
 It is a Pull parser
 Based on the C# XmlTextReader API
 Advantages:
 Low memory footprint
 Namespace support
 Simple API
 Validation support
 Advanced Feature Set
 Faster Processing
XMLReader: Simple
Example
xmlreader/reader_simple.xml
<?xml version='1.0'?>
<chapter xmlns:a="http://www.example.com/namespace-a"
xmlns="http://www.example.com/default">
<a:title>XMLReader</a:title>
<para>
First Paragraph
</para>
<a:section a:id="about">
<title>About this Document</title>
<para>
<!-- this is a comment -->
<?php echo 'Hi! This is PHP version ' . phpversion(); ?>
</para>
</a:section>
</chapter>
XMLReader: Simple Example
xmlreader/reader_simple.php
$reader = new XMLReader();
$reader->open('reader_simple.xml');
$reader->read();

print "xmlns Attribute value: ".$reader->getAttributeNo(0)."\n\n";


while ($reader->read() && $reader->name != "a:title") { }
print "Local Name for Element: ".$reader->localName."\n";
print "Namespace URI for Element: ".$reader->namespaceURI."\n";

while($reader->read()) {
switch ($reader->nodeType) {
case XMLReader::ELEMENT:
print "Element: ".$reader->name."\n";
if ($reader->hasAttributes && $reader-
>moveToFirstAttribute()) {
do {
print " ".$reader->name."=".$reader->value."\n";
} while($reader->moveToNextAttribute());
}
break;
case XMLReader::PI:
print "PI Target: ".$reader->name."\n PI Data: ".$reader->value."\n";
}
}
XMLReader: Simple
Example
RESULTS
Local Name for Element: title
Namespace URI for Element:
http://www.example.com/namespace-a

Element: para
Element: a:section
a:id=about
Element: title
Element: para
PI Target: php
PI Data: echo 'Hi! This is PHP version ' .
phpversion();
XMLReader: Consuming Yahoo
Shopping
<?xml version="1.0" encoding="ISO-8859-1"?>
<ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:yahoo:prods"
xsi:schemaLocation="urn:yahoo:prods
http://api.shopping.yahoo.com/shoppingservice/v1/productsearch.xsd"
totalResultsAvailable="8850" firstResultPosition="2"
totalResultsReturned="2">
<Result>
<Catalog ID="1991433722">
<Url><![CDATA[http://shopping.yahoo.com/p:Linksys. . .2]]></Url>
<ProductName><![CDATA[Linksys WRT5. . .r Broadband
Router]]></ProductName>
<PriceFrom>59.99</PriceFrom>
<PriceTo>100.00</PriceTo>
<Thumbnail /><!-- child elements Url (CDATA), Height, Width -->
<Description><![CDATA[The Wireless-G . . .ces.]]></Description>
<Summary><![CDATA[IEEE 802.3, ...]]></Summary>
<UserRating /><!-- Rating sub elements -->
<SpecificationList /><!-- 0+ Specification child elements -->
</SpecificationList>
</Catalog>
</Result>
</ResultSet>
XMLReader: Consuming Yahoo
Shopping
xmlreader/rest_yahoo_shopping.php
function getTextValue($reader) { ... }
function processCatalog($reader) { ... }
function processResult($reader) { ... }

/* URL to Product Search service */


$url = 'http://api.shopping.yahoo.com/ShoppingService/V1/productSearch';

/* The query is separate here as the terms must be encoded. */


$url .= '?query='.rawurlencode('linksys');

/* Complete the URL with App ID, limit to 1 result and start at second record */
$url .= "&appid=zzz&results=2&start=2";

$reader = new XMLReader();

if (! $reader->open($url)) { print "Cannot access Webservice\n";


exit; }

while($reader->name != "Result") { $reader->read(); }


do {
processResult($reader);
} while($reader->next('Result'));
XMLReader: Consuming Yahoo
Shopping
xmlreader/rest_yahoo_shopping.php
function getTextValue($reader) {
if ($reader->nodeType != XMLReader::ELEMENT || $reader-
>isEmptyElement
|| ($reader->read() && $reader->nodeType ==
XMLReader::END_ELEMENT))
return;
$retVal = $reader->value;
$reader->read();
return $retVal;
}

function processResult($reader) {
$depth = $reader->depth;
if ($reader->isEmptyElement || ($reader->read() &&
$reader->nodeType == XMLReader::END_ELEMENT))
return;

while($depth < $reader->depth && $reader->name != "Catalog")


{ $reader->read(); };
processCatalog($reader);
/* Read until </Result> is encountered */
while($depth < $reader->depth) { $reader->read(); }
}
XMLReader: Consuming Yahoo
Shopping
xmlreader/rest_yahoo_shopping.php
function processCatalog($reader) {
$depth = $reader->depth;
print "Catalog ID".$reader->getAttribute('ID')."\n";
if ($reader->isEmptyElement || ($reader->read() &&
$reader->nodeType == XMLReader::END_ELEMENT))
return;
while($depth < $reader->depth) {
switch ($reader->name) {
case "ProductName":
case "PriceFrom":
case "PriceTo":
case "Description":
case "Url":
print $reader->name.": ".getTextValue($reader)."\
n";
}
$reader->next();
}
}
XMLReader: Consuming Yahoo
Shopping
RESULTS (Abbreviated)
Catalog ID1990338714
Url: http://shopping.yahoo.com/p:Linksys%20Instant
%20Broadband%20EtherFast%20Cable%2FDSL
%20Router:1990338714
ProductName: Linksys Instant Broadband EtherFast Cable/DSL
Router
PriceFrom: 39.99
PriceTo: 72.71
Description: <P>Linksys, a provider of networking hardware for
the small/medium business (SMB), small office/home office
(SOHO), and enterprise markets and broadband networking
hardware for the home, has announced the new EtherFast
Cable/DSL Router. The first in the new Instant Broadband
series, this Linksys broadband router will enable home or office
users to connect their computers to a cable or DSL modem and
securely share Internet access and perform networking tasks
such as file and printer sharing. The built-in hardware firewall
gives users the security of sharing files without fear of
intruders hacking into the network. </P>
XMLReader: DTD
Validation
xmlreader/validation/reader.xml
<!DOCTYPE chapter [
<!ELEMENT chapter (title, para, section)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT para ANY>
<!ATTLIST para name CDATA "default">
<!ELEMENT section (#PCDATA)>
<!ATTLIST section id ID #REQUIRED>
]>
<chapter>
<title>XMLReader</title>
<para>
First Paragraph
</para>
<section id="about">
<title>About this Document</title>
<para>content</para>
</section>
</chapter>
XMLReader: DTD
Validation
$objReader =xmlreader/validation/reader.php
XMLReader::open('reader.xml');
$objReader->setParserProperty(XMLReader::VALIDATE,
TRUE);

libxml_use_internal_errors(TRUE);

while ($objReader->read()) {
if (! $objReader->isValid()) {
print "NOT VALID\n";
break;
}
}

$arErrors = libxml_get_errors();
foreach ($arErrors AS $xmlError) {
print $xmlError->message;
}
XMLReader: DTD
Validation
RESULTS

NOT VALID
Element section was declared #PCDATA but
contains non text nodes
XMLReader: Relax NG
Validation
xmlreader/validation/reader.rng
<?xml version="1.0" encoding="utf-8" ?>
<element name="chapter"

xmlns="http://relaxng.org/ns/structure/1.0">
<element name="title">
<text/>
</element>
<element name="para">
<text/>
</element>
<element name="section">
<attribute name="id" />
<text/>
</element>
</element>
XMLReader: Relax NG
Validation
xmlreader/validation/reader-rng.php
$objReader = XMLReader::open('reader.xml');
$objReader->setRelaxNGSchema('reader.rng');

libxml_use_internal_errors(TRUE);

while ($objReader->read()) {
if (! $objReader->isValid()) {
print "NOT VALID\n";
break;
}
}

$arErrors = libxml_get_errors();
foreach ($arErrors AS $xmlError) {
print $xmlError->message;
}
XMLReader: Relax NG
Validation RESULTS
NOT VALID
Did not expect element title there
XSL
 Used to transform XML data
 XSLT based on XPath
 Works with DOM and SimpleXML, although
the DOM extension is required.
 Provides the capability of calling PHP
functions during a transformation
 DOM nodes may be returned from PHP
functions
 The LIBXML_NOCDATA and LIBXML_NOENT
constants are your friends.
 libxslt 1.1.5+ is recommended to avoid
problems when using xsl:key
XSL: XML Input Data
xsl/sites.xml

<?xml version="1.0"?>
<sites>
<site xml:id="php-gen">
<name>PHP General</name>
<url>http://news.php.net/group.php?
group=php.general&amp;format=rss</url>
</site>
<site xml:id="php-pear">
<name>PHP Pear Dev</name>
<url>http://news.php.net/group.php?
group=php.pear.dev&amp;format=rss</url>
</site>
<site xml:id="php-planet">
<name>Planet PHP</name>
<url>http://www.planet-php.org/rss/</url>
</site>
</sites>
XSL: Simple
Transformation
xsl/simple_stylesheet.xsl
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="/sites/site"/>
</body>
</html>
</xsl:template>

<xsl:template match="/sites/site">
<p><xsl:value-of select="./name"/> : <xsl:value-of
select="./url"
disable-output-
escaping="yes"/></p>
</xsl:template>
XSL: Simple
Transformation
xsl/simple_stylesheet.php
/* Load Stylesheet */
$stylesheet = new DOMDocument();
$stylesheet->load('simple_stylesheet.xsl');

/* Create XSL Processor */


$proc = new xsltprocessor();
$proc->importStylesheet($stylesheet);

/* Load XML Data */


$dom = new DOMDocument();
$dom->load('sites.xml');

print $proc->transformToXML($dom);
XSL: Simple
Transformation
RESULTS
<html>
<body>
<p>PHP General : http://news.php.net/group.php?
group=php.general&format=rss</p>
<p>PHP Pear Dev : http://news.php.net/group.php?
group=php.pear.dev&format=rss</p>
<p>Planet PHP :
http://www.planet-php.org/rss/</p>
</body>
</html>
XSL: Advanced
Transformation
xsl/advanced_stylesheet.php
function initReader($url) {
$GLOBALS['reader'] = new XMLReader();
if ($GLOBALS['reader']->open($url)) {
while ($GLOBALS['reader']->read() && $GLOBALS['reader']->name != 'item')
{}

if ($GLOBALS['reader']->name == 'item')
return 1;
}
$GLOBALS['reader'] = NULL;
return 0;
}

function readNextItem() {
if ($GLOBALS['reader'] == NULL)
return NULL;
if ($GLOBALS['beingProc'])
$GLOBALS['reader']->next('item');
else
$GLOBALS['beingProc'] = TRUE;
if ($GLOBALS['reader']->name == 'item')
return $GLOBALS['reader']->expand();
return NULL;
XSL: Advanced
Transformation
xsl/advanced_stylesheet.php
$beingProc = FALSE;
$reader = NULL;

/* Load Stylesheet */
$stylesheet = new DOMDocument();
$stylesheet->load('advanced_stylesheet.xsl');

/* Create XSL Processor */


$proc = new xsltprocessor();
$proc->importStylesheet($stylesheet);

/* Load XML Data */


$dom = new DOMDocument();
$dom->load('sites.xml');

$proc->setParameter(NULL, 'siteid', 'php-gen');


$proc->registerPHPFunctions('initReader');
$proc->registerPHPFunctions('readNextItem');
print $proc->transformToXML($dom);
/* END */
XSL: Advanced
Transformation
xsl/advanced_stylesheet.xsl
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="http://php.net/xsl" version="1.0">
<xsl:output method="html"/>
<xsl:param name="siteid" select="0" />

<xsl:template match="/">
<html><body>
<xsl:apply-templates select="id($siteid)"/>
</body></html>
</xsl:template>

<xsl:template match="/sites/site">
<xsl:variable name="itemnum"
select="php:functionString('initReader', ./url)" />
<xsl:if test="number($itemnum) > 0">
<xsl:call-template name="itemproc" />
</xsl:if>
</xsl:template>
XSL: Advanced
Transformation
xsl/advanced_stylesheet.xsl
<xsl:template match="item">
<p>
Title: <b><xsl:value-of select="./title" /></b><br/><br/>
URL: <xsl:value-of select="./link" /><br/>
Published: <xsl:value-of select="./pubDate" /><br/>
</p>
</xsl:template>

<xsl:template name="itemproc">
<xsl:variable name="nodeset"
select="php:functionString('readNextItem')" />
<xsl:if test="boolean($nodeset)">
<xsl:apply-templates select="$nodeset"/>
<xsl:call-template name="itemproc" />
</xsl:if>
</xsl:template>

</xsl:stylesheet>
XSL: Advanced
Transformation
Results viewed through a browser
xsl/advanced_stylesheet.html
Title: Re: How to ping a webserver with php?

URL: http://news.php.net/php.general/232552
Published: Fri, 24 Mar 2006 11:42:04 -0500

Title: Re: Parents constructor

URL: http://news.php.net/php.general/232553
Published: Fri, 24 Mar 2006 11:52:14 -0500

Title: Re: Parents constructor

URL: http://news.php.net/php.general/232554
Published: Fri, 24 Mar 2006 12:58:51 -0500
XMLWriter
 Lightweight and forward-only API for
generating well formed XML
 Automatically escapes data
 Works with PHP 4.3+ available at
http://pecl.php.net/package/xmlwriter
 Object Oriented API available for PHP
5+
 Part of core PHP distribution since PHP
5.1.2
XMLWriter: Simple
Example
<?php xmlwriter/simple.php
$xw = new XMLWriter();
$xw->openMemory();

/* Turn on indenting to make output look pretty and set string


used for indenting as teh default space is too short*/
$xw->setIndent(TRUE);
$xw->setIndentString(' ');

/* Write out the optional XML declaration only specifying version */


$xw->startDocument('1.0');

/* Create the opening document element, which is namespaced */


$xw->startElementNs(NULL, "chapter",
"http://www.example.com/default");

/* Write out an xml namespace declaration that is used later in the


document */
$res = $xw->writeAttribute('xmlns:a',
'http://www.example.com/namespace-a');

/* Write complete elements with text content */


$xw->writeElement('a:title', 'XMLReader');
$xw->writeElement('para', 'spec chars < > & " inside para element');
XMLWriter: Simple
Example
xmlwriter/simple.php
/* start an element and add an attribute to it */
$xw->startElement('a:section');
$xw->writeAttribute('a:id', 'about');

/* Write out an element with special characters */


$xw->writeElement('title', 'Pro PHP XML & Webservices');

$xw->startElement('para'); /* This opens the para element */

$xw->writeComment("this is a comment");
$xw->text(" ");
$xw->writePi("php", "echo 'Hi! This is PHP version ' . phpversion(); ");
$xw->text("\n ");

$xw->endElement(); /* This will close the open para element */

$xw->endDocument();

/* Flush and clear the buffer */


echo $xw->flush(true);
?>
XMLWriter: Simple
Example
xmlwriter/simple.php
/* start an element and add an attribute to it */
$xw->startElement('a:section');
$xw->writeAttribute('a:id', 'about');

/* Write out an element with special characters */


$xw->writeElement('title', 'Pro PHP XML & Webservices');

$xw->startElement('para'); /* This opens the para element */

$xw->writeComment("this is a comment");
$xw->text(" ");
$xw->writePi("php", "echo 'Hi! This is PHP version ' . phpversion();
");
$xw->text("\n ");

$xw->endElement(); /* This will close the open para element */

$xw->endDocument();

/* Flush and clear the buffer */


echo $xw->flush(true);
?>
XMLWriter: Creating a Rest
Service
<?php xmlwriter/rest.php (startid and maxid)
/* If the database does not exist, then create it and populate it with some data */
if (! file_exists('xmlwriterdb')) {
if ($dbhandle = sqlite_open('xmlwriterdb', 0666)) {
sqlite_query($dbhandle, 'CREATE TABLE xmlwriter (id int, name
varchar(15))');
for ($x=1; $x< 11; $x++) {
sqlite_query($dbhandle,
"INSERT INTO xmlwriter VALUES (".$x.", 'Data Num: ".
$x."')");
}
sqlite_close($dbhandle);
} } /* closes function and saves display space */

/* Retrieve record based on id(s) */


function getDBData($min, $max) {
$results = NULL;
if ($dbhandle = sqlite_open('xmlwriterdb')) {
$strSQL = 'SELECT id, name FROM xmlwriter where id>='.$min.'
and id <='.$max;
$query = sqlite_query($dbhandle,$strSQL);

return sqlite_fetch_all($query, SQLITE_ASSOC);


} } /* closes function and saves display space */
XMLWriter: Creating a Rest
Service
xmlwriter/rest.php
/* Setup defaults */
$recid = 0;
$minid = 0;
$maxid = 0;

/* Retrieve requested record id(s) and insure $maxid is never less


than $minid */
if (! empty($_GET['startid'])) {
$minid = (int) $_GET['startid'];
$maxid = $minid;
if (! empty($_GET['maxid'])) {
$maxid = (int) $_GET['maxid'];
if ($minid > $maxid) $maxid = $minid;
}
}

/* Retrieve the requested records from the database */


$arResults = getDBData($minid, $maxid);
XMLWriter: Creating a Rest
Service
xmlwriter/rest.php
/* Process the resulting records if any */
header('Content-Type: text/xml');
$xw = new XMLWriter();

/* Send the XML document directly to output as it is written */


$xw->openUri('php://output');

$xw->startDocument('1.0', 'UTF-8');
$xw->startElement('Results');

foreach ($arResults AS $result) {


$xw->startElement('Result');
foreach ($result AS $field_name => $field_value) {
$xw->writeElement($field_name, $field_value);
}
$xw->endElement();
/* Progressively send the output */
$xw->flush();
}
$xw->endDocument();
/* Flush and clear the buffer */
$xw->flush();
XMLWriter: Creating a Rest
Service
xmlwriter/rest.php
/* Process the resulting records if any */
header('Content-Type: text/xml');
$xw = new XMLWriter();

/* Send the XML document directly to output as it is written */


$xw->openUri('php://output');

$xw->startDocument('1.0', 'UTF-8');
$xw->startElement('Results');

foreach ($arResults AS $result) {


$xw->startElement('Result');
foreach ($result AS $field_name => $field_value) {
$xw->writeElement($field_name, $field_value);
}
$xw->endElement();
/* Progressively send the output */
$xw->flush();
}
$xw->endDocument();
/* Flush and clear the buffer */
$xw->flush();
Tree Parsers
 Pros:
 Full navigation and modification of the
XML document
 Navigating and searching are extremely

fast once the tree is loaded into memory


 Cons:
 Must wait until entire tree is loaded to
begin working with the XML.
 Memory intensive
Streaming Parsers
 Pros:
 Uses minimal memory
 Processing takes place immediately

while the document is parsed


 Cons:
 Minimal to no navigation support
(forward only)
 No document editing capabilities
Raw Test Data
<books>
<book id="1"><title>1</title><pages>1</pages></book>
<book id="2"><title>2</title><pages>2</pages></book>
<!-- Remaining book elements for a total of 200,000 -->
</books>

Memory Usage:
DOM SimpleXML ext/xml XMLReader
85.6MB 85.6MB 26KB 177KB

Using every optimization possible the following results show


the time in seconds to locate the book element having
id="5000".

Average Time in Seconds for Optimized Search


DOM SimpleXML ext/xml XMLReader
for an Element:
6.623 6.583 0.930 0.238
SOAP
 An XML-based protocol for exchanging
information between applications
 It allows for remote invocation of methods
in a distributed environment
 Uses existing transport protocols such as
HTTP
 Can operate with or without a Web Service
Definition Language (WSDL) document
 A W3C standard and the core component to
the Web Services Interoperability
Organization (WS-I) Basic Profile
SOAP: Basic WSDL
Structure
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/">

<types><!-- definition of types used in WSDL --></types>

<message><!-- abstract definition of the data being transmitted --


></message>

<portType>
<!-- a set of abstract operations refrring to input and output
messages -->
</portType>

<binding><!-- concrete protocol and data format specs


--></binding>

<service><!-- specifies locations and bindings for a service


--></service>

</definitions>
SOAP: Basic Message
Structure
<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

<soap:Header>
<!-- Information to extend message -->
<!-- For example WS-Security or transaction information -->
</soap:Header>

<soap:Body>
<!-- Either the message contents or soap:Fault -->
<soap:Fault>
<!-- SOAP Fault structure and data -->
</soap:Fault>
</soap:Body>

</soap:Envelope>
SOAP: The SoapClient
SoapClient::__construct ( mixed wsdl [, array options] )

Some SoapClient options:


location* (string) Location of Soap service
uri* (string) Target namespace for the SOAP
server
style1 (int) Binding style for message
(SOAP_DOCUMENT or SOAP_RPC)
use1 (int) Binding type for style
(SOAP_ENCODED or SOAP_LITERAL)
trace (bool) Enable / disable request/response
tracing (default disabled)
exceptions (bool) Turn Soap exceptions on / off
*Required in NON-WSDL mode
(default on)
1
Only used in NON-WSDL mode
SOAP: The SoapClient
SoapClient::__construct ( mixed wsdl [, array options] )

Connection and security options for SoapClient:


login Login for HTTP authentication
password Password for HTTP authenication
proxy_host Host for Proxy server
proxy_port Port for Proxy server
proxy_login Login for Proxy server
proxy_password Password for Proxy server
local_cert Client certificate for HTTPS client
authentication
passphrase Passphrase for client certificate
SOAP: Function Query a
WSDL
soap/google/google_get_functions.php
<?php
try{
/* Create the SoapClient and load the WSDL */
$GoogleClient = new
SoapClient('GoogleSearch.wsdl');
/* Retrieve all defined functions into an array */
$google_funcs = $GoogleClient->__getFunctions();
foreach($google_funcs AS $function) {
echo $function."\n\n";
}
} catch (SoapFault $e) {
var_dump($e);
}
?>
SOAP: Function Query a
WSDL
Google Function RESULTS
base64Binary doGetCachedPage(string $key, string
$url)

string doSpellingSuggestion(string $key, string


$phrase)

GoogleSearchResult doGoogleSearch(string $key,


string $q, int $start, int $maxResults, boolean $filter,
string $restrict, boolean $safeSearch, string $lr, string
$ie, string $oe)

$key refers to a Google license key, which may be


obatined from:
http://www.google.com/apis/index.html
SOAP: Type Query a
WSDL
soap/google/google_get_types.php
<?php
try{
/* Create the SoapClient and load the WSDL */
$GoogleClient = new
SoapClient('GoogleSearch.wsdl');
/* Retrieve all defined types into an array */
$types = $GoogleClient->__getTypes();
foreach($ types AS $type) {
echo $type."\n\n";
}
} catch (SoapFault $e) {
var_dump($e);
}
?>
SOAP: Type Query a
WSDL
Google Type RESULTS
struct GoogleSearchResult { struct ResultElement {
boolean documentFiltering;
string summary;
string searchComments;
int estimatedTotalResultsCount; string URL;
boolean estimateIsExact; string snippet;
ResultElementArray resultElements; string title;
string searchQuery; string cachedSize;
int startIndex;
int endIndex; boolean
string searchTips; relatedInformationPresent;
DirectoryCategoryArray string hostName;
directoryCategories; DirectoryCategory
double searchTime;
} directoryCategory;
string directoryTitle;
ResultElement ResultElementArray[ }
]
DirectoryCategory struct DirectoryCategory
DirectoryCategoryArray[ ] {
string fullViewableName;
SOAP: Retrieving from Google
Cache
soap/google/google_cache_client.php
<?php
/* The following file holds your registered Google key */
require('google_key.php');

try {
/* Create the Soap Client */
$client = new
SoapClient('http://api.google.com/GoogleSearch.wsdl');

$cached = $client->doGetCachedPage($key,
'http://www.google.com/');

/* display first 200 characters of cached page */


echo substr($cached, 0, 500);

} catch (SoapFault $e) {


var_dump($e);
}
?>
SOAP: Retrieving from Google
Cache
RESULTS
<meta http-equiv="Content-Type" content="text/html;
charset=US-ASCII">
<BASE HREF="http://www.google.com/"><table
border=1 width=100%><tr><td><table border=1
bgcolor=#ffffff cellpadding=10 cellspacing=0
width=100% color=#ffffff><tr><td><font face=""
color=black size=-1>This is <b><font
color=#0039b6>G</font> <font
color=#c41200>o</font> <font
color=#f3c518>o</font> <font
color=#0039b6>g</font> <font
color=#30a72f>l</font> <font
color=#c41200>e</font></b>'s <a
href="http://www.google.com/help/fea
SOAP: Google Search Client
soap/google/google_search_client.php
<?php
/* The following file holds your registered Google key */
require('google_key.php');
/* Define search criteria */
$search_terms = "PHP XML Web Services book";
$start = 0;
$maxResults = 10;
$filter = FALSE;
$safeSearch = TRUE;
$restrict = $lr = $ie = $oe = "";
/* Within Try/Catch block – omitted to save space */
/* Create the Soap Client */
$client = new SoapClient('http://api.google.com/GoogleSearch.wsdl');
$results = $client->doGoogleSearch($key, $search_terms, $start,
$maxResults,
$filter, $restrict, $safeSearch,
$lr, $ie, $oe));
var_dump($results);
?>
SOAP: Google Search Client
RESULTS
object(stdClass)#2 (11) {
["documentFiltering"] => bool(false)
["searchComments"] => string(0) ""
["estimatedTotalResultsCount"]=> int(2160000)
["estimateIsExact"] => bool(false)
["resultElements"] => array(10) {
....
[4] => object(stdClass)#11 (9) {
["summary"] => string(0) ""
["URL"]=> string(65) "http://www.amazon.com/exec/obidos/tg/detail/-/1590596331?
v=glance"
["snippet"] => string(116) "Amazon.com: Pro <b>PHP XML</b> and <b>Web
Services</b> (Pro):
<b>Books</b>: Robert Richards by Robert<br> Richards."
["title"] => string(91) "Amazon.com: Pro <b>PHP XML</b> and <b>Web Services</b>
(Pro):
<b>Books</b>: Robert Richards"
["cachedSize"] => string(3) "74k"
["relatedInformationPresent"] => bool(true)
["hostName"] => string(0) ""
["directoryCategory"] => object(stdClass)#12 (2) {
["fullViewableName"] => string(0) ""
["specialEncoding"] => string(0) ""
}
["directoryTitle"] => string(0) ""
}
....
}
SOAP: Client Headers
soap/headers.php
soapHeader::__construct ( string namespace, string name [,
mixed data [, bool mustUnderstand [, mixed
actor]]] )
<?php
/* Create and authentication object with username/password */
class authentication {
public $username;
public $password;
}

$auth = new authentication();


$auth->username = 'username';
$auth->password = 'password';

/* You MUST encode the object */


$authVar = new SoapVar($auth, SOAP_ENC_OBJECT);

$header = new SoapHeader('urn:ExampleAPI', "Authentication",


$authVar, TRUE, SOAP_ACTOR_NEXT);

/* Set the new headers to use when creating SOAP messages */


$sClient->__setSoapHeaders(array($header));
?>
SOAP: Client Request
Modification
soap/google/request_modification.php
soap/google/request_modification.php
require('google_key.php');

Class mySoapClient extends SoapClient {


function __doRequest($request, $location, $action, $version) {
/* Load the request into a DOMDocument */
$dom = new DOMDocument();
$dom->loadXML($request);

/* Find the url element and set url to http://www.php.net/ */


$nodeList = $dom->getElementsByTagName('url');
if ($nodeList->length == 1) {
$nodeList->item(0)->firstChild->nodeValue =
"http://www.php.net/";
}

/* Serialize the tree and send modified request to parent method */


$request = $dom->saveXML();
return parent::__doRequest($request, $location, $action, $version);
}
}

$sClient = new mySoapClient('GoogleSearch.wsdl');


$cached = $sClient->doGetCachedPage($key, ' http://www.google.com/');

echo substr($cached, 1500, 700)."\n";


SOAP: Client Request
Modification
RESULTS (soap/google/request_modification.php)
ont><br><br><center><font size=-2><i>Google is neither
affiliated with the authors of this page nor responsible for its
content.</i></font></center></td></tr></table></td></tr
></table>
<hr>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN">
<html>
<head>
<title>PHP: Hypertext Preprocessor</title>
<link rel="stylesheet"
href="http://static.php.net/www.php.net/style.css" />
<link rel="stylesheet"
href="http://static.php.net/www.php.net/styles/phpnet.css" />
<link rel="shortcut icon"
href="http://static.php.net/www.php.net/favicon.ico" />
<link rel="alternate" type="application/rss+xml" title="PHP:
Hypertext Preprocessor"
href="http://www.php.net/news.rss" />
<script language
SOAP: Debugging Client
Requests
soap/google/debug_client.php
/* Empty key so function will throw SoapFault */
$key = "";
$client_options = array ('trace'=>1);

try {
/* Create the Soap Client with debug option */
$client = new SoapClient('http://api.google.com/GoogleSearch.wsdl',
$client_options);
$cached = $client->doGetCachedPage($key,
'http://www.google.com/');
} catch (SoapFault $e) {
print "Last Request Headers: \n".$client-
>__getLastRequestHeaders()."\n\n";
print "Last Request: \n".$client->__getLastRequest()."\n\n";
print "Last Response Headers: \n".$client-
>__getLastRequestHeaders()."\n\n";
print "Last Response: \n".$client->__getLastResponse()."\n";
}
SOAP: Debugging Client
Request
RESULT
Last Request Headers:
POST /search/beta2 HTTP/1.1
Host: api.google.com
Connection: Keep-Alive
User-Agent: PHP SOAP 0.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "urn:GoogleSearchAction"
Content-Length: 554

Last Request:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:GoogleSearch"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-
ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-
ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-
ENV:Body><ns1:doGetCachedPage><key
xsi:type="xsd:string"></key><url
xsi:type="xsd:string">http://www.google.com/</url></ns1:doGetCachedPa
ge></SOAP-ENV:Body></SOAP-ENV:Envelope>
SOAP: Debugging Client
Requests
Last Response Headers: RESULT Continued
POST /search/beta2 HTTP/1.1
Host: api.google.com
Connection: Keep-Alive
User-Agent: PHP SOAP 0.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "urn:GoogleSearchAction"
Content-Length: 554

Last Response:
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Exception from service object: Invalid authorization key: </faultstring>
<faultactor>/search/beta2</faultactor>
<detail>
<stackTrace>com.google.soap.search.GoogleSearchFault: Invalid authorization key:
at
com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(QueryLimits.jav
a:213)
. . .</stackTrace>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SOAP: Client and
Document/Literal
soap/docliteral/compsearch_funcs.php
<?php
$wsdl = "http://ws.invesbot.com/companysearch.asmx?WSDL";
try {
/* Create the Soap Client to a Company Search */
$client = new SoapClient($wsdl);

print "Functions: \n";


$functions = $client->__getFunctions();
foreach($functions AS $function) {
echo $function."\n";
}

print "\nTypes: \n";


$types = $client->__getTypes();
foreach($types AS $type) {
echo $type."\n\n";
}
} catch (SoapFault $e) {
var_dump($e);
}
?>
SOAP: Client and
Document/Literal
RESULTS (soap/docliteral/compsearch.php)
Functions:
SearchResponse Search(Search $parameters)
GetSearchTermsResponse GetSearchTerms(GetSearchTerms $parameters)
GetCompanyProfileResponse GetCompanyProfile(GetCompanyProfile
$parameters)
GetAllIndustriesResponse GetAllIndustries(GetAllIndustries $parameters)

Types:
struct Search { struct
string Keyword; SearchResponse {
string Field; SearchResult
string CurrentPage; SearchResult;
string PageSize; }
}
struct SearchResult {
<anyXML> any;
}
...
SOAP: Client and
Document/Literal
soap/docliteral/compsearch.php
class Search {
public $Keyword;
public $CurrentPage = 1;
public $PageSize = 10;
}

$wsdl = "http://ws.invesbot.com/companysearch.asmx?WSDL";
/* Create the Soap Client to a Company Search */
$client = new SoapClient($wsdl);

/* Call the Search function which takes a struct parameter with*/


$results = $client->Search(array('Keyword'=>'microsoft',
'PageSize'=>10));

/* OR Call using a class */


// $mySearch = new Search();
// $mySearch->Keyword = "Microsoft";
// $results = $client->Search($mySearch);
/* End Call using Class */

var_dump($results);
SOAP: Client and
Document/Literal
RESULTS (soap/docliteral/compsearch.php)
object(stdClass)#2 (1) {
["SearchResult"]=>
object(stdClass)#3 (1) {
["any"]=>
string(6392) "<SearchResults
xmlns=""><ResultsInfo><TotalResults>777</TotalResults><CurrentPa
ge>1</CurrentPage><PageSize>10</PageSize></
ResultsInfo><SearchResult><cik>1002607</cik><symbol/
><comname>ATARI INC</comname><accessnumber>0000950123-05-
013898</accessnumber><formtype>10-K/A</
formtype><filingdate>20051121</filingdate><result>, 2005 (the
"Amendment Effective Date") by and between
&lt;B&gt;Microsoft&lt;/B&gt; Licensing, GP, a Nevada general
partnership ("&lt;B&gt;Microsoft&lt;/B&gt;"), and Atari, Inc. ("Licensee"
or "Publisher"), and supplements that certain ... (the "PLA). RECITALS A.
&lt;B&gt;Microsoft&lt;/B&gt; and Publisher entered into the PLA to
establish the terms under which
</result><time/><price/><change/><marketcap/></SearchResult><S
earchResult><cik>865570</cik><symbol/><comname>THQ . . . .
SOAP: Server WSDL (using

Document/Literal)
soap/server/exampleapi.wsdl
<xsd:element name="getPeopleByFirstLastName">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="first" type="xsd:string"/>
<xsd:element name="last" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>

<xsd:complexType name="Person">
<xsd:all>
<xsd:element name="id" type="xsd:int"/>
<xsd:element name="lastName" type="xsd:string"/>
<xsd:element name="firstName" type="xsd:string"/>
</xsd:all>
</xsd:complexType>

<xsd:element name="getPeopleByFirstLastNameResponse"
type="tns:ArrayOfPerson"/>

<message name="getPeopleByFirstLastName">
<part name="parameters" element="tns:getPeopleByFirstLastName"/>
</message>
<message name="getPeopleByFirstLastNameResponse">
<part name="result" element="tns:getPeopleByFirstLastNameResponse"/>
</message>
SOAP: Server
soap/server/soap_server.php
<?php
/* System status - TRUE indicates normal operation /
FALSE indicates down for maintenance */
$SYS_STATUS = TRUE;

function findPeople($firstName, $lastName) {


/* Initialize the Person Records */
$matching = array();

$people = array(array('id'=>1, 'firstName'=>'John', 'lastName'=>'Smith'),


array('id'=>2, 'firstName'=>'Jane', 'lastName'=>'Doe'));
foreach($people AS $person) {
/* Check if match on first name */
if (empty($firstSearch) || preg_match('/^'.$firstSearch.'$/i',
$person['firstName'])) {
/* Check if match on last name */
if (empty($lastSearch) || preg_match('/^'.$lastSearch.'$/i',
$person['lastName'])) {
$matching[ ] = $person;
}
}
}
return $matching;
}
SOAP: Server
soap/server/soap_server.php
function getPeopleByFirstLastName($getPeopleByFirstLastName) {
/* If system is down throw SOAP fault */
if (isset($GLOBALS['SYS_STATUS']) && $GLOBALS['SYS_STATUS'] ==
FALSE) {
$details = array("SysMessage"=>"Sys Error", "RetryInMinutes"=>60);

/* SoapFault::__construct ( string faultcode, string faultstring [, string faultactor [,


mixed detail [, string faultname [, SoapHeader
headerfault]]]] ) */
throw new SoapFault("SYSError", "System Unavailable",
"urn:ExampleAPI",
$details, "sysmaint");
}
$firstSearch = str_replace('*', '([a-z]*)', $getPeopleByFirstLastName->first);
$lastSearch = str_replace('*', '([a-z]*)', $getPeopleByFirstLastName->last);
$retval = array();
$results = findPeople($firstSearch, $lastSearch);
foreach($results AS $result) {
/* Add matching records as an encoded SoapVar */
$retval[] = new SoapVar($result, SOAP_ENC_ARRAY, "Person",
"urn:ExampleAPI");
}
return $retval;
}
SOAP: Server
soap/server/soap_server.php

/* Create the server using WSDL and specify the actor URI */
$sServer = new SoapServer("exampleapi.wsdl",
array('actor'=>'urn:ExampleAPI'));

/* Register the getPeopleByFirstLastName function */


$sServer->addFunction("getPeopleByFirstLastName");

/* Handle the SOAP request */


$sServer->handle();
?>
SOAP: Calling our Server
soap/server/soap_client.php

<?php
try {
$sClient = new SoapClient('exampleapi.wsdl');

/* Set search parameters */


$params = array('first'=>'jo*', 'last'=>'*');

/* Make request and dump response */


$response = $sClient-
>getPeopleByFirstLastName($params);

var_dump($response);

} catch (SoapFault $e) {


/* Dump any caught SoapFault exceptions */
var_dump($e);
}
?>
SOAP: Calling our Server
RESULTS

array(1) {
[0]=>
object(stdClass)#2 (3) {
["id"]=>
int(1)
["lastName"]=>
string(5) "Smith"
["firstName"]=>
string(4) "John"
}
}
Questions?
XML Security
 Provides possible detection of altered
documents
 Can be used to prevent attacks that
alter a document yet maintain integrity
 Depending upon cryptographic
algorithms used can also provide
authenticity of document author
 Provides the capabilities of protecting
data from unauthorized access using
encryption
XML Security:Basic
Integrity
xmlsecurity/basic_message_integrity.php
/* Generate SHA1 and MD5 hash */
$sha1hash = sha1_file('xmlsec.xml');
$md5hash = md5_file('xmlsec.xml');
/* Print resulting hashes */
print $sha1hash."\n";
print $md5hash."\n";

/* Create and Verify Integrity */


if (sha1_file('xmlsec.xml') == $sha1hash) {
/* Open and modify the XML document */
$dom = new DOMDocument();
$dom->load('xmlsec.xml');
$root = $dom->documentElement;
$root->appendChild($dom->createElement('data', 'More data'));
$dom->save('xmlsec.xml');

/* Create and store a new hash for the next time document is accessed */
$sha1hash = sha1_file('xmlsec.xml');
print 'New Hash: '.$sha1hash."\n";
} else {
print 'File has been altered!';
}
XML Security:Basic
Integrity HMAC
xmlsecurity/basic_message_integrity_hmac.php
$secret_key = 'secret';

if (isset($_POST['xmldoc']) && isset($_POST['hmac'])) {


$xmldata = base64_decode($_POST['xmldoc']);

/* Generate the expected HMAC */


$hmac_sha1hash = bin2hex(mhash(MHASH_SHA1, $xmldata,
$secret_key));

/* Verify message integrity and authenticity */


if ($hmac_sha1hash == $_POST['hmac']) {
$dom = new DOMDocument();
$dom->loadXML($xmldata);
print $dom->saveXML();
} else {
print 'DATA HAS BEEN ALTERED!!!';
}
} else {
print 'Missing Arguments';
}
XML Security: Basic
Encryption
xmlsecurity/basic_encryption.php
$secret_key = 'secret';

/* Encrypt Data */
$orderxml = file_get_contents('order.xml');

$dom = new DOMDocument();


$dom->loadXML($orderxml);
$order = $dom->documentElement;

/* continued on next slide */


XML Security: Basic
Encryption
xmlsecurity/basic_encryption.php
foreach ($order->childNodes AS $node) {
if ($node->nodeName == 'creditcard') {
/* Get serialized creditcard node */
$data = $dom->saveXML($node);

/* Encrypt the serialized node */


$td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, $secret_key, $iv);
$encrypted_data = rtrim(mcrypt_generic($td, $data));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);

/* Create a new replacement node containing encrpyted encrypted data */


$encNode = $dom->createElement('encrypted',
base64_encode($encrypted_data));
$order->replaceChild($encNode, $node);

/* Add the Initialization Vector as an attribute */


$encNode->setAttribute('iv', base64_encode($iv));
break;
}
}

$enc_document = $dom->saveXML();
XML Security: Basic
Decryption
xmlsecurity/basic_encryption.php
/* De-Crypt Data */
$dom = new DOMDocument();
$dom->loadXML($enc_document);
$order = $dom->documentElement;
foreach ($order->childNodes AS $node) {
if ($node->nodeName == 'encrypted') {
/* Get Initialization Vector */
$iv = base64_decode($node->getAttribute('iv'));
/* Get data, and decode it */
$data = base64_decode($node->nodeValue);

/* Decrypt $data with mcrypt (omitted for brevity) with


MCRYPT_3DES/MCRYPT_MODE_CBC */
$decrypted_data = rtrim(mdecrypt_generic($td, $data));

$frag = $dom->createDocumentFragment();
/* Functionality available in PHP 5.1 */
$frag->appendXML($decrypted_data);

/* Replacement node */
$order->replaceChild($frag, $node);
break;
}
}
print $dom->saveXML();

You might also like