Skip to content

Instantly share code, notes, and snippets.

@Sija
Created January 19, 2011 09:07
Show Gist options
  • Save Sija/785894 to your computer and use it in GitHub Desktop.
Save Sija/785894 to your computer and use it in GitHub Desktop.
XML manipulation class found in dark corners of my hdd
<?php
/**
*
* @mainpage
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/
/**
* @class EasyXMLNode
* @brief Basic EasyXML construct - it wraps DOMElement, and gives access to
* it's children nodes and attributes. Serialization is also supported.
*
* @author Dariusz "njoy" Paciorek
* @author Zbigniew "ShaXbee" Mandziejewicz
* @author Sijawusz Pur Rahnama
* @version 1.0
* @date 01-2007
*/
class EasyXMLNode implements ArrayAccess {
//! Attributes
protected $_root;
protected $_node;
//! Used for serialization
protected static $_serializeRoot;
/**
* @brief Construct
*
* @param $root
* @param $node
*/
public function __construct(DOMDocument $root, DOMNode $node) {
$this->_root = $root;
$this->_node = $node;
}
/**
* @brief Add node child
*
* @param $name
* @param $value
* @return EasyXMLNode
*/
public function addChild($name, $value = null) {
if ($value) {
$node = $this->_root->createElement($name, $value);
} else {
$node = $this->_root->createElement($name);
}
$node = $this->_node->appendChild($node);
return new self($this->_root, $node);
}
/**
* @brief Remove child from node
*
* @param $name
*/
public function removeChild($name = null) {
if ($name) {
$node = $this->getElementByTagName($name);
$this->_node->removeChild($node);
} else {
$node = $this->_node->cloneNode(false);
$parent = $this->_node->parentNode;
$parent->removeChild($this->_node);
$parent->appendChild($node);
}
}
/**
* @brief Load xml into node
*
* @param $xmlData
*/
public function loadXMLString($xmlData) {
$fragment = $this->_root->createDocumentFragment();
$fragment->appendXML($xmlData);
$fragment->formatOutput = true;
$node = $this->_node->cloneNode(false);
$node->appendChild($fragment);
$parent = $this->_node->parentNode;
$parent->removeChild($this->_node);
$parent->appendChild($node);
}
/**
* @brief Return node or document as XML
*
* @return string
*/
public function asXML() {
if ($this->_node->isSameNode($this->_node)) {
return $this->_root->saveXML();
} else {
return $this->_root->saveXML($this->_node);
}
return null;
}
/**
* ArrayAccess method. Set node attribute
*/
public function offsetSet($key, $value) {
$this->_node->setAttribute($key, $value);
}
/**
* ArrayAccess .Get node attribute
*/
public function offsetGet($key) {
return $this->_node->getAttribute($key);
}
/**
* ArrayAccess. Remove node attribute
*/
public function offsetUnset($key) {
$this->_node->removeAttribute($key);
}
/**
* ArrayAccess . Check for node attribute exists
*/
public function offsetExists($key) {
return $this->_node->hasAttribute($key);
}
/**
* @brief Return node
*
* @return object
*/
public function getNode() {
return $this->_node;
}
/**
* @brief Return node with given name
*
* @param $name
* @return mixed
*/
protected function getElementByTagName($name) {
$result = null;
$node = $this->_node->firstChild;
while ($node && !$result) {
if ($node->localName == $name || $node->nodeName == $name) {
$result = $node;
}
$node = $node->nextSibling;
}
return $result;
}
/**
* @brief Execute Xpath query on current node
*
* @param $query xpath query to execute
* @return EasyXMLResult
*/
public function xpath($query) {
$xpath = new DOMXpath($this->_root);
$entries = @$xpath->query($query, $this->_node);
if ($entries && !$entries->length) {
$entries = null;
}
return ($entries ? new EasyXMLResult($this->_root, $entries) : false);
}
/**
* @brief Returns true if any node exists under given name or name and key
*
* @param $name
* @return bool
*/
public function __isset($name) {
$node = $this->_node->firstChild;
while ($node) {
if ($name == $node->localName || $name == $node->nodeName) {
return true;
}
$node = $node->nextSibling;
}
return false;
}
/**
* @brief Get node by name or by name and key
*
* @param $name
* @return object
*/
public function __get($name) {
$result = Array();
$node = $this->_node->firstChild;
while ($node) {
if ($name == $node->localName || $name == $node->nodeName) {
$result[] = $node;
}
$node = $node->nextSibling;
}
return new EasyXMLResult($this->_root, $result);
}
/**
* @brief Set node value
*
* @param $name
* @param $value
*/
public function __set($name, $value) {
$this->__unset($name);
if ($value instanceof self) {
$this->_node->appendChild($value->getNode());
} else {
$node = $this->addChild($name);
$node->loadXMLString($value);
}
}
/**
* @brief Routes DOM method calls to underlaying DOMElement $node
*/
public function __call($func, $args) {
if (method_exists($this->_node, $func)) {
return call_user_func_array(Array($this->_node, $func), $args);
}
}
/**
* @brief Removes node(s) with given name - called on unset($obj->$name)
*
* @param $name
*/
public function __unset($name) {
$result = Array();
$node = $this->_node->firstChild;
while ($node) {
if ($node->localName == $name || $node->nodeName == $name) {
$result[] = $node;
}
$node = $node->nextSibling;
}
foreach ($result as $node) {
$this->_node->removeChild($node);
}
}
/**
* @brief Return node as XML or node value
*
* @return string
*/
public function __toString() {
if ($this->_node->hasChildNodes() && $this->_node->childNodes->length == 1 && $this->_node->firstChild->nodeType == XML_TEXT_NODE) {
return $this->_node->firstChild->nodeValue;
} else {
return $this->_root->saveXML($this->_node);
}
}
/**
* @brief Convert node to Array
*
* @param $node Node to convert
* @return Array with node attributes, childs and value
*/
static protected function toArray($node) {
$attributes = Array();
if ($node->nodeType == XML_ELEMENT_NODE) {
$name = $node->nodeName;
}
if (!$node instanceof DOMDocument) {
$nodes = $node->attributes;
if ($node->hasAttribute('xmlns')) {
$attributes['xmlns'] = $node->getAttribute('xmlns');
}
for ($i = 0; $i < $nodes->length; ++$i) {
$attr = $nodes->item($i);
$attributes[$attr->name] = $attr->value;
}
}
$childs = Array();
$nodes = $node->childNodes;
for ($i = 0; $i < $nodes->length; ++$i) {
$node = $nodes->item($i);
if ($node->nodeType == XML_ELEMENT_NODE) {
$childs[] = self::toArray($node);
} else if ($node->nodeType == XML_TEXT_NODE) {
$value = $node->nodeValue;
}
}
return compact('name', 'childs', 'attributes', 'value');
}
/**
* @brief Converts current node to Array
*
* @return Array with node attributes, childs and value
*/
public function getArray() {
return self::toArray($this->_node);
}
/**
* @brief Unserialize serialized xml document
*
* @param $node
* @param $data
*/
protected static function fromArray($node, & $data) {
extract($data);
if (isset($value)) {
$textNode = self::$_serializeRoot->createTextNode($value);
$node->appendChild($textNode);
}
foreach ($childs as & $child) {
$newNode = self::$_serializeRoot->createElement($child['name']);
$node->appendChild($newNode);
self::fromArray($newNode, $child);
}
if (!$node instanceof DOMDocument) {
foreach ($attributes as $key => & $value) {
$node->setAttribute($key, $value);
}
}
}
}
/**
* @class EasyXMLResult
* @brief Queries result container, returned by EasyXMLNode::__get and
* EasyXMLNode::xpath
*
* @author Dariusz "njoy" Paciorek
* @author Zbigniew "ShaXbee" Mandziejewicz
* @author Sijawusz Pur Rahnama
* @version 1.0
* @date 01-2007
*/
class EasyXMLResult extends EasyXMLNode implements Countable {
//! Array $data. Store nodes
protected $_data;
/**
* @brief Construct. Creates result object
*/
public function __construct(DOMDocument $root, $data) {
$this->_root = $root;
if ($data instanceof DOMNodeList) {
$result = Array();
foreach ($data as $row) {
$result[] = $row;
}
$data = $result;
}
$this->_node = count($data) ? $data[0] : null;
$this->_data = $data;
}
/**
* Check for node(s) with given index or node attribute
*/
public function offsetExists($key) {
if (is_numeric($key)) {
return isset($this->_data[$key]);
} else {
return parent::offsetExists($key);
}
}
/**
* @brief Return node(s) with given index or node attribute
*
* @param $key
* @return mixed
*/
public function offsetGet($key) {
if (is_numeric($key)) {
if (isset($this->_data[$key])) {
return new EasyXMLNode($this->_root, $this->_data[$key]);
}
} else {
return parent::offsetGet($key);
}
return null;
}
/**
* @brief Remove node
*
* @param $key
*/
public function offsetUnset($key) {
if (is_numeric($key)) {
if (isset($this->_data[$key])) {
$this->data[$key]->parentNode->removeChild($this->_data[$key]);
}
} else {
return parent::offsetUnset($key);
}
}
/**
* @brief Return number of nodes
*
* @return integer
*/
public function count() {
return count($this->_data);
}
}
/**
* @class EasyXML
* @brief DOMDocument wrapper, root for EasyXMLNode's
*
* @author Dariusz "njoy" Paciorek
* @author Zbigniew "ShaXbee" Mandziejewicz
* @author Sijawusz Pur Rahnama
* @version 1.0
* @date 01-2007
*/
class EasyXML extends EasyXMLNode {
//! Serialization data
protected $_serialData;
/**
* @brief Construct
*
* @param $xmlData
*/
public function __construct($xmlData = null) {
if ($xmlData) {
$this->_root = @DOMDocument::loadXML($xmlData);
} else {
$this->_root = new DOMDocument('1.0', 'utf-8');
}
$this->_node = $this->_root;
$this->_root->formatOutput = true;
$this->_root->preserveWhiteSpace = false;
}
/**
* @brief Create new EasyXML object and load XML data from file
*
* @param $fileName XML File
* @return EasyXML
*/
static public function loadXMLFile($fileName) {
if (file_exists($fileName)) {
$data = file_get_contents($fileName);
return new EasyXML($data);
}
return null;
}
/**
* @brief Serialization handler - called on serialize($obj);
* @return List of fields to serialize
*/
public function __sleep() {
$this->_serialData = self::toArray($this->_node);
return Array('_serialData');
}
/**
* @brief Unserialization handler - called on $obj = unserialize($data);
* Restores child nodes and document
*/
public function __wakeup() {
$this->_node = new DOMDocument('1.0', 'utf-8');
$this->_node->formatOutput = true;
$this->_root = $this->_node;
self::$_serializeRoot = $this->_node;
self::fromArray($this->_node, $this->_serialData);
// cleanup after unserialization
self::$_serializeRoot = null;
$this->_serialData = null;
}
/**
* @brief Return document as XML
*
* @return string
*/
public function __toString() {
return $this->_root->saveXML();
}
}
?>
<?php
// create object from string
$xml = new EasyXML('<root><check>someValue</check></root>');
// or from file
$xml = EasyXML::loadXMLFile('file.xml');
// set innerText of <check/> to given string, creates tag if not exists
$xml->root->check = '<someOtherValue/>';
// set attribute value
$xml->root['attribute'] = 'attributeValue';
// remove attribute
unset($xml->root['attribute']);
// add some <domain/> tags as children
$domain = $xml->root->check->addChild('domain');
$otherDomain = $xml->root->check->addChild('domain');
$domain['name'] = 'aqq.pl';
// remove all <domain/> tags from given node
unset($xml->root->check->domain);
// use DOM-alike methods
$xml->root->firstChild;
$xml->root->hasAttribute('test');
// you can use string to define tags
$xml->root->check = '<domain>aqq</domain><domain>bqq</domain>';
// serialize it for later
$data = serialize($xml);
// get it in array format
$xml->getArray();
// that works too! :)
$xml->xpath('//check');
// print xml string
echo $xml;
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment