Skip to content

Instantly share code, notes, and snippets.

@hakre
Created March 12, 2012 19:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hakre/0f0a65234fa139b95ee1 to your computer and use it in GitHub Desktop.
Save hakre/0f0a65234fa139b95ee1 to your computer and use it in GitHub Desktop.
Using DOMXml and Xpath, to update XML entries
<?php
/**
* http://stackoverflow.com/questions/9671273/using-domxml-and-xpath-to-update-xml-entries
*/
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<storagehouse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="schema.xsd">
<item id="c7278e33ef0f4aff88da10dfeeaaae7a">
<name>HDMI Cable 3m</name>
<weight>0.5</weight>
<category>Cables</category>
<location>B3</location>
</item>
<item id="df799fb47bc1e13f3e1c8b04ebd16a96">
<name>Dell U2410</name>
<weight>2.5</weight>
<category>Monitors</category>
<location>C2</location>
</item>
</storagehouse>
XML;
class ItemNode
{
private $node;
private $createData;
public function __construct(DOMNode $node)
{
$this->node = $node;
$this->snapshot();
}
/**
* did the repository node changed since last snapshot?
*
* @return bool
*/
public function hasChanged() {
return !$this->arraysAreEqual($this->getData(), $this->createData);
}
/**
* compare arrays for equality but w/o key order
* @param array $a
* @param array $b
* @return bool
*/
private function arraysAreEqual(array $a, array $b)
{
if (count($a) != count($b)) {
return FALSE;
}
foreach($a as $name => $value)
{
if (!array_key_exists($name, $b)) return FALSE;
if ($b[$name] !== $a[$name]) return FALSE;
}
return TRUE;
}
public function itemHasChanges(Item $item)
{
return !$this->arraysAreEqual(get_object_vars($item), $this->createData);
}
public function snapshot()
{
$this->createData = $this->getData();
}
public function overloadObject($object)
{
foreach ($this->getData() as $name => $value) {
$object->$name = $value;
}
}
/**
* @return array
*/
public function getData()
{
$values = array();
foreach ($this->node->childNodes as $child) {
/* @var $child DOMElement */
if ($child instanceof DOMElement) {
$values[$child->localName] = $child->nodeValue;
}
}
return $values;
}
public function updateWith(Item $item)
{
$data = get_object_vars($item);
$this->setData($data);
$this->snapshot();
}
public function setData(array $data)
{
foreach ($data as $name => $value)
{
$this->setValue($name, $value);
}
}
private function setValue($name, $value)
{
$xp = new DOMXPath($this->node->ownerDocument);
$result = $xp->query(sprintf('./%s', $name), $this->node);
if (!$result || $result->length != 1) {
throw new InvalidArgumentException(sprintf('Value "%s" does not exist.', $name));
}
$result->item(0)->nodeValue = $value;
}
}
class Repository
{
private $doc;
private $provided = array();
public function __construct($xml)
{
$doc = new DOMDocument();
$doc->loadXML($xml);
$this->doc = $doc;
}
public function getItemByID($id)
{
$xp = new DOMXPath($this->doc);
if (($result = $xp->query(sprintf('//item[@id="%s"]', $id))) && $result->length == 1) {
$node = new ItemNode($result->item(0));
$item = new Item($id);
$node->overloadObject($item);
$this->register($item, $node);
return $item;
} else {
throw new InvalidArgumentException(sprintf('No Item with id "%s" exists.', $id));
}
}
private function register($object, ItemNode $node)
{
$this->provided[] = array($object, $node);
}
public function saveChanges()
{
$updates = array();
foreach($this->provided as $entity)
{
/* @var $object Item */
/* @var $node ItemNode */
list($object, $node) = $entity;
if ($node->hasChanged()) {
throw new UnexpectedValueException('Transaction not safe (DOMElement Node has changed).');
}
if ($node->itemHasChanges($object)) {
$changes[] = $entity;
}
}
if (!count($changes)) {
return;
}
foreach($changes as $entity) {
list($object, $node) = $entity;
$node->updateWith($object);
}
}
public function getXML() {
return $this->doc->saveXML();
}
}
class Item
{
private $id;
public function __construct($id)
{
$this->id = $id;
}
public function getID()
{
return $this->id;
}
}
$id = "df799fb47bc1e13f3e1c8b04ebd16a96";
$simplexml = simplexml_load_string($xml);
$result = $simplexml->xpath(sprintf('/storagehouse/item[@id="%s"]', $id));
if (!$result || count($result) !== 1) {
throw new Exception(sprintf('Item with id "%s" does not exists or is not unique.', $id));
}
list($item) = $result;
$item->category = 'LCD Monitor';
echo $simplexml->asXML();
$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
$result = $xpath->query(sprintf('/storagehouse/item[@id="%s"]', $id));
if (!$result || $result->length !== 1) {
throw new Exception(sprintf('Item with id "%s" does not exists or is not unique.', $id));
}
$item = $result->item(0);
$category = $xpath->query('./category', $item)->item(0);
$category->nodeValue = "LCD Monitor";
echo $doc->saveXML($item);
$repository = new Repository($xml);
$item = $repository->getItemByID($id);
$item->category = 'LCD Monitor';
$repository->saveChanges();
echo $repository->getXML();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment