Source for file common.php
Documentation is available at common.php
// +----------------------------------------------------------------------+
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2001 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Dan Allen <dan@mojavelinux.com> |
// +----------------------------------------------------------------------+
// $Id: common.php,v 1.23 2007/08/04 20:24:41 cweiske Exp $
// Core DOM and internal pointer methods for the Xpath/DOM XML manipulation and query interface.
* is_a is only defined in php for user functions,
* so I implemented its functionality for php objects
* until they fix (or never fix) this problem
* @param object $class the class to check
* @param string $match class name you are looking for
* @return boolean whether the class is of the class type or a descendent of the class
// {{{ class XML_XPath_common
* The XML_XPath_common class contains the DOM functions used to manipulate
* and maneuver through the xml tree. The main thing to understand is
* that all operations work around a single pointer. This pointer is your
* place holder within the document. Each function you run assumes the
* node in reference is your pointer. However, every function can take
* an xpath query or DOM object reference, so that the pointer can be set
* before working on the node, and can retain this position if specified.
* Every DOM function call has a init() and shutdown() call. This function
* prepares the pointer to the requested location in the tree if an xpath query
* or pointer object is provided. In addition, the init() function checks to
* see that the node type is acceptable for the method, and if not throws an
* XML_XPath_Error exception. If you want to execute a function and then remain
* in the location of your query, then you specify that you want to move the pointer.
* For the DOM step functions, this is the default action.
* Note: All offsets in the CharacterData interface start from 0.
* The object model of XML_XPath is as follows (indentation means inheritance):
* XML_XPath_common The main functionality of the XML_XPath class is here. This
* | holds all the DOM functions for manipulating and maneuvering
* | through the DOM tree.
* +-XML_XPath The frontend for the XML_XPath implementation. Provides default
* | functions for preparing the main document, running xpath queries
* | and handling errorMessages.
* +-Result Extended from the XML_XPath_common class, this object is returned when
* an xpath query is executed and can be used to cycle through the
* @author Dan Allen <dan@mojavelinux.com>
* domxml node of the current location in the xml document
* domxml node bookmark used for holding a place in the xml document
* when working with the xml document, ignore the presence of blank nodes (white space)
* @var boolean $skipBlanks
* path to xmllint used for reformating the xml output
* [!] should be using System_Command for this [!]
* Return the name of this node, depending on its type, according to the DOM recommendation
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer with quick xpath query
* @return string name of node corresponding to DOM recommendation {or XML_XPath_Error exception}
function nodeName($in_xpathQuery = null , $in_movePointer = false )
if ($hasQuery = !is_null($in_xpathQuery) && XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer))) {
$nodeName = $this->pointer->node_name ();
if ($hasQuery && !$in_movePointer) {
$this->_restore_bookmark ();
* Returns the integer value constant corresponding to the DOM node type
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer with quick xpath query
* @return int DOM type of the node {or XML_XPath_Error exception}
function nodeType($in_xpathQuery = null , $in_movePointer = false )
if ($hasQuery = !is_null($in_xpathQuery) && XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer))) {
$nodeType = $this->pointer->node_type ();
if ($hasQuery && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ object childNodes()
* Retrieves the child nodes from the element node as an XML_XPath_result object
* Similar to an xpath query, this function will grab all the first descendant child
* nodes of the element node at the current position and will create an XML_XPath_result
* object of type nodeset with each of the child nodes as the nodes.
* DOM query functions do not take an xpathQuery argument
* @return object XML_XPath_result object of type nodeset
* [!] important note: since we had to hack the result object a bit, you cannot sort the
* result object when generated in this manner right now [!]
foreach($this->pointer->child_nodes () as $childNode) {
// if this is a blank node and we are skipping blank nodes...skip to next child
if ($childNode->is_blank_node () && $this->skipBlanks) {
// {{{ object getElementsByTagName()
* Create an XML_XPath_result object with the elements with the specified tagname
* DOM query functions do not take an xpathQuery argument
* @param string $in_tagName
* @return object XML_XPath_result object of matching nodes
// since we can't do an actual xpath query, we need to create a pseudo xpath result
$nodeset = $this->xml->get_elements_by_tagname ($in_tagName);
return new XML_XPath_result($nodeset, XPATH_NODESET , array (null , '//' . $in_tagName), $this->ctx, $this->xml);
// {{{ boolean documentElement()
* Move to the document element
* @param boolean $in_movePointer (optional) move the internal pointer or return reference
* @return boolean whether pointer was moved or object pointer to document element
$documentElement = $this->xml->document_element ();
// {{{ boolean parentNode()
* Moves the internal pointer to the parent of the current node or returns the pointer.
* Step functions do not take an xpathQuery argument
* @param boolean $in_movePointer (optional) move the internal pointer or return reference
* @return boolean whether pointer was moved or object pointer to parent
$parent = $this->pointer->parent_node ();
// {{{ boolean nextSibling()
* Moves the internal pointer to the next sibling of the current node, or returns the pointer.
* If the flag is on to skip blank nodes then the first non-blank node is used.
* Step functions do not take an xpathQuery argument
* @param boolean $in_movePointer (optional) move the internal pointer or return reference
* @return boolean whether the pointer was moved or object pointer to next sibling
if (!$this->pointer->next_sibling ()) {
$next = $this->pointer->next_sibling ();
// make sure we are not already at the end
// we have found a non-blank node
elseif (!$next->is_blank_node ()) {
// we found a blank node at the very end
elseif (!$next = $next->next_sibling ()) {
// {{{ boolean previousSibling()
* Moves the internal pointer to the previous sibling
* of the current node or returns the pointer.
* If the flag is on to skip blank nodes then the first non-blank node is used.
* Step functions do not take an xpathQuery argument
* @param boolean $in_movePointer (optional) move the internal pointer or return reference
* @return boolean whether the pointer was moved or object pointer to previous sibling
if (!$this->pointer->previous_sibling ()) {
$previous = $this->pointer->previous_sibling ();
// we have found a non-blank node
if (!$previous->is_blank_node ()) {
// we have arrived at the beginning
elseif (!$previous = $previous->previous_sibling ()) {
// {{{ boolean firstChild()
* Moves the pointer to the first child of this node or returns the first node.
* If the flag is on to skip blank nodes then the first non-blank node is used.
* Step functions do not take an xpathQuery argument
* @param boolean $in_movePointer (optional) move the internal pointer or return reference
* @return boolean whether the pointer was moved to the first child or returns the first child
if (!$this->pointer->has_child_nodes ()) {
$first = $this->pointer->first_child ();
// we have found a non-blank node
if (!$first->is_blank_node ()) {
// we have arrived at the end
elseif (!$first = $first->next_sibling ()) {
// {{{ boolean lastChild()
* Moves the pointer to the last child of this node or returns the last child.
* If the flag is on to skip blank nodes then the first non-blank node is used.
* Step functions do not take an xpathQuery argument
* @param boolean $in_movePointer (optional) move the internal pointer or return reference
* @return boolean whether the pointer was moved to the last child or returns the last child
if (!$this->pointer->has_child_nodes ()) {
$last = $this->pointer->last_child ();
// we have found a non-blank node
if (!$last->is_blank_node ()) {
// we have arrived at the beginning
elseif (!$last = $last->previous_sibling ()) {
// {{{ boolean hasChildNodes()
* Returns whether this node has any children.
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return boolean has child nodes {or XML_XPath_Error exception}
function hasChildNodes($in_xpathQuery = null , $in_movePointer = false )
if ($hasQuery = !is_null($in_xpathQuery) && XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer))) {
$hasChildNodes = $this->pointer->has_child_nodes ();
if ($hasQuery && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ boolean hasAttributes()
* Returns whether this node has any attributes.
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return boolean attributes exist {or XML_XPath_Error exception}
function hasAttributes($in_xpathQuery = null , $in_movePointer = false )
if ($hasQuery = !is_null($in_xpathQuery) && XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer))) {
$hasAttributes = $this->pointer->has_attributes ();
if ($hasQuery && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ boolean hasAttribute()
* Returns true when an attribute with a given name is specified on this element
* @param string $in_name name of attribute
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return boolean existence of attribute {or XML_XPath_Error exception}
function hasAttribute($in_name, $in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE )))) {
$hasAttribute = $this->pointer->has_attribute ($in_name) ? true : false;
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ array getAttributes()
* Return an associative array of attribute names as the keys and attribute values as the
* values. This is not a DOM function, but is a convenient addition.
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return array associative array of attributes {or XML_XPath_Error exception}
function getAttributes($in_xpathQuery = null , $in_movePointer = false )
if ($hasQuery = !is_null($in_xpathQuery) && XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer))) {
foreach($attributeNodes as $attributeNode) {
$return[$attributeNode->name ] = $attributeNode->value;
if ($hasQuery && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ string getAttribute()
* Retrieves an attribute value by name from the element node at the current pointer.
* Grab the attribute value if it exists and return it. If the attribute does not
* exist, this function will return the boolean 'false', so be sure to check properly
* if the value is "" or if the attribute doesn't exist at all. This function is the
* only attribute function which allows you to step onto the attribute node.
* @param string $in_name Name of the attribute
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move the internal pointer with quick xpath query
* @return string value of attribute or false if attribute DNE {or XML_XPath_Error exception}
function getAttribute($in_name, $in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE )))) {
$result = $this->pointer->get_attribute ($in_name);
// if we found the attribute, move to it if we are moving the pointer
if ($result && $in_movePointer) {
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ boolean setAttribute()
* Adds a new attribute. If an attribute with that name is already present
* in the element, its value is changed to be that of the value parameter.
* Invalid characters are escaped.
* @param string $in_name name of the attribute to be set
* @param string $in_value new attribute value
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return boolean success {or XML_XPath_Error exception}
function setAttribute($in_name, $in_value, $in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE )))) {
$result = $this->pointer->set_attribute ($in_name, $in_value);
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ void removeAttribute()
* Remove the attribute by name.
* @param string $in_name name of the attribute
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return boolean success {or XML_XPath_Error exception}
function removeAttribute($in_name, $in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE )))) {
$result = $this->pointer->remove_attribute ($in_name);
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ string substringData()
* Extracts a range of data from the node. Takes an offset and a count, which are optional
* and will default to retrieving the whole string. If an XML_ELEMENT_NODE provided, then
* it first concats all the adjacent text nodes recusively and works on those.
* ??? implement wholeText() which concats all text nodes adjacent to a text node ???
* @param int $in_offset offset of substring to extract
* @param int $in_count length of substring to extract
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return string substring of the character data {or XML_XPath_Error exception}
function substringData($in_offset = 0 , $in_count = 0 , $in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_TEXT_NODE , XML_ELEMENT_NODE , XML_CDATA_SECTION_NODE , XML_COMMENT_NODE )))) {
if (!is_int($in_count) || $in_count < 0 ) {
$return = PEAR ::raiseError (null , XML_XPATH_INDEX_SIZE, null , E_USER_WARNING , " Count: $in_offset" , 'XML_XPath_Error', true );
elseif (!is_int($in_offset) || $in_offset < 0 || $in_offset > strlen($content = $this->pointer->get_content ())) {
$return = PEAR ::raiseError (null , XML_XPATH_INDEX_SIZE, null , E_USER_WARNING , " Offset: $in_offset" , 'XML_XPath_Error', true );
$return = $in_count ? substr($content, $in_offset, $in_count) :
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
* Will insert data at offset for a text node.
* @param string $in_content content to be inserted
* @param int $in_offset offset to insert data
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return void {or XML_XPath_Error exception}
function insertData($in_content, $in_offset = 0 , $in_xpathQuery = null , $in_movePointer = false )
return $this->_set_content ($in_content, $in_xpathQuery, $in_movePointer, false , $in_offset);
* Will delete data at offset and for count for a text node.
* @param int $in_offset (optional) offset to delete data
* @param int $in_count (optional) number of characters to delete
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return void {or XML_XPath_Error exception}
function deleteData($in_offset = 0 , $in_count = 0 , $in_xpathQuery = null , $in_movePointer = false )
return $this->_set_content (null , $in_xpathQuery, $in_movePointer, true , $in_offset, $in_count);
// {{{ void replaceData()
* Will replace data at offset and for count with content
* @param string $in_content content to insert
* @param int $in_offset (optional) offset to replace data
* @param int $in_count (optional) number of characters to replace
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return void {or XML_XPath_Error exception}
function replaceData($in_content, $in_offset = 0 , $in_count = 0 , $in_xpathQuery = null , $in_movePointer = false )
return $this->_set_content ($in_content, $in_xpathQuery, $in_movePointer, true , $in_offset, $in_count);
* Will append data to end of text node.
* @param string $in_content content to append
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return void {or XML_XPath_Error exception}
function appendData($in_content, $in_xpathQuery = null , $in_movePointer = false )
return $this->_set_content ($in_content, $in_xpathQuery, $in_movePointer, false , null );
// {{{ object replaceChild()
* Replaces the old child with the new child. If the new child is already in the document,
* it is first removed (not implemented yet). If the new child is a document fragment, then
* all of the nodes are inserted in the location of the old child.
* @param mixed $in_xmlData document fragment or node
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return object pointer to old node {or XML_XPath_Error exception}
function replaceChild($in_xmlData, $in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE , XML_TEXT_NODE , XML_COMMENT_NODE , XML_CDATA_SECTION_NODE , XML_PI_NODE )))) {
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
$parent = $this->pointer->parent_node ();
$lastNodeIndex = sizeOf ($importedNodes) - 1;
// run through all the new nodes...on the last new node, run replace_child()
foreach($importedNodes as $index => $importedNode) {
if ($index == $lastNodeIndex) {
$oldNode = $parent->replace_child ($importedNode, $this->pointer);
$parent->insert_before ($importedNode, $this->pointer);
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
return new XML_XPath_result(array ($oldNode), XPATH_NODESET , null , $this->ctx, $this->xml);
// {{{ object appendChild()
* Adds the node or document fragment to the end of the list of children. If the node
* is already in the tree it is first removed (not sure if this works yet)
* @param mixed $in_xmlData string document fragment or node
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer move internal pointer
* @return object pointer to the first of the nodes appended {or XML_XPath_Error exception}
function appendChild($in_xmlData, $in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE , XML_DOCUMENT_NODE )))) {
// if this is a document node, make sure no root exists
if ($this->pointer->node_type () == XML_DOCUMENT_NODE && $this->xml->document_element ()) {
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
return PEAR ::raiseError (null , XML_DUPLICATE_ROOT, null , E_USER_WARNING , null , 'XML_XPath_Error', true );
foreach($importedNodes as $index => $importedNode) {
$node = $this->pointer->append_child ($importedNode);
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ object insertBefore()
* Inserts the node before the current pointer.
* @param mixed $in_xmlData either a document fragment xml string or a node
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return object pointer to the first of the new inserted nodes
function insertBefore($in_xmlData, $in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE )))) {
// we do some fance stuff here to make this general...make a fake node
$importedNodes = $this->_build_fragment ($in_xmlData);
$parent = $this->pointer->parent_node ();
foreach($importedNodes as $index => $importedNode) {
$node = $parent->insert_before ($importedNode, $this->pointer);
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ object removeChild()
* Removes the child node at the current pointer and returns it.
* This function will remove a child from the list of children and will
* move the pointer to the parent node.
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return object cloned node of the removed node, ready to be put in another document
function removeChild($in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE , XML_TEXT_NODE , XML_COMMENT_NODE , XML_PI_NODE , XML_CDATA_SECTION_NODE )))) {
$parent = $this->pointer->parent_node ();
$removedNode = $parent->remove_child ($this->pointer);
// set pointer to the parent
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
return new XML_XPath_result(array ($removedNode), XPATH_NODESET , null , $this->ctx, $this->xml);
// {{{ object cloneNode()
* Clones the node and return the node as a result object
* @param bool $in_deep (optional) clone node children
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return object cloned node of the current node, ready to be put in another document
function cloneNode($in_deep = false , $in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer))) {
$clonedNode = $this->pointer->clone_node ($in_deep);
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
return new XML_XPath_result(array ($clonedNode), XPATH_NODESET , null , $this->ctx, $this->xml);
// {{{ void replaceChildren()
* Not in the DOM specification, but certainly a convenient function. Allows you to pass
* in an xml document fragment which will be parsed into an xml object and merged into the
* xml document, replacing all the previous children of the node. It does this by shallow
* cloning the node, restoring the attributes and then adding the parsed children.
* @param string $in_fragment xml fragment which will be merged into tree
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return void {or XML_XPath_Error exception}
function replaceChildren($in_xmlData, $in_xpathQuery = null , $in_movePointer = false )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE )))) {
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
// nix all the children by overwriting node and fixing attributes
$attributes = $this->pointer->has_attributes () ? $this->pointer->attributes () : array ();
foreach($attributes as $attributeNode) {
// waiting on set_attribute_node() to work here
$this->pointer->set_attribute ($attributeNode->node_name (), $attributeNode->value ());
foreach ($importedNodes as $importedNode) {
$this->pointer->append_child ($importedNode);
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ string dumpChildren()
* Returns all the contents of an element node, regardless of type, as is.
* @param string $in_xpathQuery quick xpath query
* @param boolean $in_movePointer move internal pointer
* @return string xml string, a concatenation of all the children of the element node
function dumpChildren($in_xpathQuery = null , $in_movePointer = false , $in_format = true )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE )))) {
$xmlString = trim($this->xml->dump_node ($this->pointer, $in_format));
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
* Exports the xml document to a file. Only works for the whole document right now.
* @param file $in_file file to export the xml to
* @param int $in_compression (optional) ratio of compression using zlib (0-9)
* @return void {or XML_XPath_Error exception}
function toFile($in_file, $in_compression = 0 )
// If the file does not exist, make sure we can write in this directory
// If the file exists, make sure we can overwrite it
if (!(is_int($in_compression) && $in_compression >= 0 && $in_compression <= 9 )) {
$this->xml->dump_mem_file ($in_file, $in_compression);
* Export the xml document to a string, beginning from the pointer.
* @param string $in_xpathQuery quick xpath query
* @param boolean $in_movePointer move internal pointer
* @param boolean $in_format reformat using xmllint --format
* @return string xml string, starting at pointer
function toString($in_xpathQuery = null , $in_movePointer = false , $in_format = true )
if ($hasQuery = !is_null($in_xpathQuery) && XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer))) {
if ($this->nodeType() == XML_DOCUMENT_NODE ) {
$xmlString = $this->xml->dump_mem ($in_format);
$xmlString = $this->xml->dump_node ($this->pointer, $in_format);
$xmlString = escapeshellarg($xmlString);
$xmlString = `echo $xmlString | {$this->xmllint} --format - 2>&1`;
if ($hasQuery && !$in_movePointer) {
$this->_restore_bookmark ();
// {{{ object getPointer()
* Get the current pointer in the xml document.
* @return object current pointer object
// {{{ object setPointer()
* Set the pointer in the xml document
* @param object $in_node node to move to in the xml document
* @return void {or XML_XPath_Error exception}
// if this is an error object, just return it
elseif (!$this->_is_dom_node ($in_node)) {
// we are okay, set the node and return true
// {{{ string getNodePath()
* Resolve the xpath location of the current node
* @param string $in_xpathQuery (optional)
* @param boolean $in_movePointer (optional)
* @return string xpath location query
elseif (!$this->_is_dom_node ($in_node)) {
if (($type = $cur->node_type ()) == XML_DOCUMENT_NODE ) {
else if ($type == XML_ATTRIBUTE_NODE ) {
$name = $cur->node_name ();
$next = $cur->parent_node ();
$name = $cur->node_name ();
$next = $cur->parent_node ();
// now figure out the index
$tmp = $cur->previous_sibling ();
if ($name == $tmp->node_name ()) {
$tmp = $tmp->previous_sibling ();
if ($type == XML_ELEMENT_NODE ) {
// this is a hack and only works for some cases
// we can have to nodes with the same name but different namespace,
// so this should actually go above
if (($prefix = $cur->prefix ()) != '') {
$name = $prefix . ':' . $name;
// fix the names for those nodes where xpath query and dom node name don't match
elseif ($type == XML_COMMENT_NODE ) {
elseif ($type == XML_PI_NODE ) {
$name = 'processing-instruction()';
elseif ($type == XML_TEXT_NODE ) {
// anything left here has not been coded yet (cdata is broken)
$buffer = $sep . $name . $buffer;
$buffer = $sep . $name . '[' . $occur . ']' . $buffer;
* A quick version of the evaluate, where the results are returned immediately. This
* function is equivalent to xsl:value-of select in every way.
* @param string $in_xpathQuery (optional) quick xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return mixed number of nodes or value of scalar result {or XML_XPath_Error exception}
function getOne($in_xpathQuery, $in_movePointer = false )
// Execute the xpath query and return the results, then reset the result index
return $result->getData ();
* Evaluate the xpath expression on the loaded xml document.
* The xpath query provided is evaluated and either an XML_XPath_result object is
* returned, or, if the pointer is being moved, it acts like a glorified step function
* and moves the pointer to the specified node (or first node if it is a set) and returns
* @param string $in_xpathQuery xpath query
* @param boolean $in_movePointer (optional) move internal pointer
* @return mixed result object or boolean success (for move pointer)
* @throws XML_XPath_error XML_XPATH_NOT_LOADED
function &evaluate($in_xpathQuery, $in_movePointer = false )
// Make sure we have loaded an xml document and were able to create an xpath context
return PEAR ::raiseError (null , XML_XPATH_NOT_LOADED, null , E_USER_ERROR , null , 'XML_XPath_Error', true );
// enable relative xpath queries (I don't check a valid dom object yet)
if (isset ($in_xpathQuery[1 ])) {
// those double slashes cause an anomally
if (substr($in_xpathQuery[1 ], 0 , 2 ) == '//') {
if ($in_xpathQuery[0 ] == 'current()' || $in_xpathQuery[0 ] == '.') {
elseif ($in_xpathQuery[0 ] == 'parent::node()' || $in_xpathQuery[0 ] == '..') {
if ($this->pointer->node_type () != XML_DOCUMENT_NODE ) {
$in_xpathQuery[0 ] = $this->getNodePath($in_xpathQuery[0 ]);
// handle or statements and construct query
$parts = explode('|', $in_xpathQuery[1 ]);
$xpathQuery = $in_xpathQuery[0 ] . $sep . implode('|' . $in_xpathQuery[0 ] . $sep, $parts);
$xpathQuery = reset($in_xpathQuery);
// we don't care if this messes up, we will let the result object handle no results
$result = @xpath_eval ($this->ctx, $xpathQuery);
// if we are moving the pointer, return boolean success just like the step functions
if ($result->type == XPATH_NODESET && !empty ($result->nodeset )) {
$result->type == XPATH_NODESET ? $result->nodeset : $result->value ,
* For functions which take a document fragment I have a general way to import the data
* into a nodeset and then I return the nodeset. If the xml data was already a node, I
* just cast it to a single element array so the return type is consistent.
* @param mixed $in_xmlData either document fragment string or dom node
* @return array nodeset array
function _build_fragment ($in_xmlData)
if ($this->_is_dom_node ($in_xmlData)) {
$fakeChildren = array ($in_xmlData);
$fake = @domxml_open_mem ('<fake>'. $in_xmlData. '</fake>');
return PEAR ::raiseError (null , XML_PARSE_ERROR, null , E_USER_WARNING , $in_xmlData, 'XML_XPath_Error', true );
$fakeRoot = $fake->document_element ();
$fakeChildren = $fakeRoot->has_child_nodes () ? $fakeRoot->child_nodes () : array ();
* Generic function to handle manipulation of a data string based on manipulation parameters.
* @param string $in_content data to be added
* @param boolean $in_replace method of manipulation
* @param int $in_offset offset of manipulation
* @param int $in_count length of manipulation
* @return object XML_XPath_Error on fail
function _set_content ($in_content, $in_xpathQuery, $in_movePointer, $in_replace, $in_offset = 0 , $in_count = 0 )
if (XML_XPath::isError($result = $this->_quick_evaluate_init ($in_xpathQuery, $in_movePointer, array (XML_ELEMENT_NODE , XML_TEXT_NODE , XML_CDATA_SECTION_NODE , XML_COMMENT_NODE , XML_PI_NODE )))) {
$data = $this->pointer->get_content ();
// little hack to get appendData to use this function here...special little exception
if (!is_int($in_offset) || $in_offset < 0 || $in_offset > strlen($data)) {
$return = PEAR ::raiseError (null , XML_XPATH_INDEX_SIZE, null , E_USER_WARNING , " Offset: $in_offset" , 'XML_XPath_Error', true );
elseif (!is_int($in_count) || $in_count < 0 ) {
$return = PEAR ::raiseError (null , XML_XPATH_INDEX_SIZE, null , E_USER_WARNING , " Count: $in_offset" , 'XML_XPath_Error', true );
$data = $in_count ? substr($data, 0 , $in_offset) . $in_content . substr($data, $in_offset + $in_count) : substr($data, 0 , $in_offset) . $in_content;
$data = substr($data, 0 , $in_offset) . $in_content . substr($data, $in_offset);
if ($this->pointer->node_type () == XML_ELEMENT_NODE ) {
$this->pointer->replace_node ($this->xml->create_text_node ($data));
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
* Determines if the provided object is a domnode descendent.
* @param object $in_object object in question
* @return boolean whether the object is a domnode
function _is_dom_node ($in_object)
// {{{ _restore_bookmark()
* Restore the internal pointer after a quick query operation
function _restore_bookmark ()
// {{{ _quick_evaluate_init()
* The function will allow an on the quick xpath query to move the internal pointer before
* invoking the xmldom function. The requirements are that the xpath query must return
* an XPATH_NODESET and have at least one node. If not, an XML_XPath_Error will be returned
* ** In addition this function does a check on the correct nodeType to run the caller method
* @param string $in_xpathQuery optional xpath query to move the internal pointer
* @param boolean $in_movePointer move the pointer temporarily or permanently
* @param array $in_nodeTypes required nodeType list for the caller method
* @return boolean true on success, XML_XPath_Error on error
function _quick_evaluate_init ($in_xpathQuery = null , $in_movePointer = false , $in_nodeTypes = null )
// we don't need to check for null, since false or 0 is not a valid query anyway
// doing the following manually (without evaluate()) is critical for speed
// Make sure we have an xpath context (mildly costly)
return PEAR ::raiseError (null , XML_XPATH_NOT_LOADED, null , E_USER_ERROR , null , 'XML_XPath_Error', true );
// enable relative xpath queries (I don't check a valid dom object yet)
if (isset ($in_xpathQuery[1 ])) {
// those double slashes cause an anomally
if (substr($in_xpathQuery[1 ], 0 , 2 ) == '//') {
if ($in_xpathQuery[0 ] == 'current()' || $in_xpathQuery[0 ] == '.') {
elseif ($in_xpathQuery[0 ] == 'parent::node()' || $in_xpathQuery[0 ] == '..') {
if ($this->pointer->node_type () != XML_DOCUMENT_NODE ) {
$in_xpathQuery[0 ] = $this->getNodePath($in_xpathQuery[0 ]);
$xpathQuery = $in_xpathQuery[0 ] . $sep . $in_xpathQuery[1 ];
$xpathQuery = reset($in_xpathQuery);
if (!$result = @xpath_eval ($this->ctx, $xpathQuery)) {
return PEAR ::raiseError (null , XML_XPATH_INVALID_QUERY, null , E_USER_WARNING , " XML_XPath query: $xpathQuery" , 'XML_XPath_Error', true );
if (empty ($result->nodeset ) || $result->type != XPATH_NODESET ) {
return PEAR ::raiseError (null , XML_XPATH_INVALID_NODESET, null , E_USER_WARNING , " XML_XPath query: $xpathQuery" , 'XML_XPath_Error', true );
// this takes the first result (too bad if you had more)
$tmpPointer = reset($result->nodeset );
// a bit costly, so we put it second
elseif ($this->_is_dom_node ($in_xpathQuery)) {
$tmpPointer = $in_xpathQuery;
return PEAR ::raiseError (null , XML_XPATH_INVALID_QUERY, null , E_USER_WARNING , " XML_XPath query: $in_xpathQuery" , 'XML_XPath_Error', true );
// if we are moving the internal pointer, then do it now
// set the bookmark if we only want to temporarily move the pointer
// this area is too critical to call class methods...we have to be nasty
// see if we have a restricted nodeType requirement (negligible time)
if (!is_null($in_xpathQuery) && !$in_movePointer) {
$this->_restore_bookmark ();
return PEAR ::raiseError (null , XML_XPATH_INVALID_NODETYPE, null , E_USER_WARNING , "Required type: ". implode(" or ", $in_nodeTypes). ", Provided type: ". $nodeType, 'XML_XPath_Error', true );
Documentation generated on Mon, 11 Mar 2019 14:44:42 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|