XML and Web services

Document Sample
XML and Web services Powered By Docstoc
					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
<order num="1001" xmlns="urn:order" xmlns:ship="urn:shipping" xmlns:bill="urn:billing"> <ship:shipping> <ship:name type="care_of">John Smith</ship:name> <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/namespace. 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> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema>

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 LIBXML_DTDLOAD LIBXML_DTDATTR LIBXML_DTDVALID

Substitute entities with replacement content Load subsets but do not perform validation Create defaulted attributes defined in DTD Loads subsets and perform validation

LIBXML_NOERROR LIBXML_NOWARNING
LIBXML_NOBLANKS LIBXML_XINCLUDE LIBXML_NOCDATA LIBXML_NONET

Suppress parsing errors from libxml2 Suppress parser warnings from libxml2
Remove insignificant whitespace on parsing Perform XIncludes during parsing Merge CDATA nodes in Text nodes Disable network access when loading

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 DOMElement DOMAttr DOMComment DOMDocumentType DOMNotation DOMEntity

      

DOMEntityReference DOMProcessingInstruction DOMNameSpaceNode DOMDocumentFragment DOMCharacterData DOMText 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
dom/atom_feed_creation.php
define('ATOMNS', '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 entry element within the Atom document */ while ($entry->hasChildNodes()) { $entry->removeChild($entry->firstChild); } OR /* These will work */ $children = $entry->childNodes; $length = $children->length - 1; for ($x=$length; $x >=0; $x--) { $entry->removeChild($children->item($x)); }

$node = $entry->lastChild; while($node) { $prev = $node->previousSibling; $entry->removeChild($node); $node = $prev; }
/* This Will Not Work! */ foreach($entry->childNodes AS $node) { $entry->removeChild($node); }

OR
$elem = $entry->cloneNode(FALSE); $entry->parentNode->replaceChild($elem, $entry);

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 Total Books: 54

/* $xpath->evaluate("sum(//book/@qty)") */

/* $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> <classics>

<rare> <bk:book qty="4"> <bk:name>Cannery Row</bk:name> <bk:price>400.00</bk:price> <bk:edition>1</bk:edition> </bk:book> <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"

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

xmlns="urn:yahoo:srch" xsi:schemaLocation="urn:yahoo:srch http://api.search.yahoo.com/WebSearchService/V1/WebSearchResponse.xsd" totalResultsAvailable="374000" totalResultsReturned="5" firstResultPosition="1">

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) { foreach ($sxe->Result AS $result) { print 'Title: '.$result->Title."\n"; print 'Url: '.$result->Url."\n";

/* Loop through each result and output title, url and modification date */

}

}

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 {

} break; case XMLReader::PI:
} }

} while($reader->moveToNextAttribute());

print " ".$reader->name."=".$reader->value."\n";

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%20EtherF ast%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
xmlreader/validation/reader.php $objReader = 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:stylesheet>

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
xmlwriter/simple.php
<?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
xmlwriter/rest.php (startid and maxid)
<?php /* 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);
} } /* closes function and saves display space */

return sqlite_fetch_all($query, SQLITE_ASSOC);

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 85.6MB SimpleXML 85.6MB ext/xml 26KB XMLReader 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 for an Element:

DOM
6.623

SimpleXML
6.583

ext/xml
0.930

XMLReader
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* uri* style1 use1 trace (string) Location of Soap service (string) Target namespace for the SOAP server (int) Binding style for message (SOAP_DOCUMENT or SOAP_RPC) (int) Binding type for style (SOAP_ENCODED or SOAP_LITERAL) (bool) Enable / disable request/response tracing (default disabled)

exceptions

(bool) Turn Soap exceptions on / off (default on)

*Required in NON-WSDL mode 1 Only used in NON-WSDL mode

SOAP: The SoapClient
SoapClient::__construct ( mixed wsdl [, array options] )

Connection and security options for SoapClient:
login password proxy_host Login for HTTP authentication Password for HTTP authenication Host for Proxy server

proxy_port
proxy_login proxy_password local_cert passphrase

Port for Proxy server
Login for Proxy server Password for Proxy server Client certificate for HTTPS client authentication 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 {

boolean documentFiltering; string searchComments; int estimatedTotalResultsCount; boolean estimateIsExact; ResultElementArray resultElements; string searchQuery; int startIndex; int endIndex; string searchTips; DirectoryCategoryArray directoryCategories; double searchTime; }

struct ResultElement {
string summary; string URL; string snippet; string title; string cachedSize; boolean relatedInformationPresent; string hostName; DirectoryCategory directoryCategory; string directoryTitle; }

ResultElement ResultElementArray[ ]
DirectoryCategory DirectoryCategoryArray[ ]

struct DirectoryCategory {
string fullViewableName; string specialEncoding; }

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
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:SOAPENC="http://schemas.xmlsoap.org/soap/encoding/" SOAPENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAPENV:Body><ns1:doGetCachedPage><key xsi:type="xsd:string"></key><url xsi:type="xsd:string">http://www.google.com/</url></ns1:doGetCachedPage></SO AP-ENV:Body></SOAP-ENV:Envelope>

SOAP: Debugging Client Requests
RESULT Continued
Last Response 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 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.java: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 { string Keyword; string Field; string CurrentPage; string PageSize; }

struct SearchResponse { SearchResult SearchResult; } 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><CurrentPage>1</Curren tPage><PageSize>10</PageSize></ResultsInfo><SearchResult><cik>1002607</cik> <symbol/><comname>ATARI INC</comname><accessnumber>0000950123-05013898</accessnumber><formtype>10K/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><SearchResult> <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();

}


				
DOCUMENT INFO
Shared By:
Tags:
Stats:
views:1205
posted:9/4/2009
language:English
pages:151