Grima 2018-08
Whispering into Alma's ear
grima-xmlbag.php
Go to the documentation of this file.
1<?php
2
3/******************************************************************************
4 * XML Bags!
5 *
6 * Wish you could treat XML as an array? Now you can!
7 *
8 * What does xml['item'] = ?
9 *
10 * Three plausible answers:
11 * (1) A single DOMNode, so you have to use innerXML and such to get its string value
12 * (2) A DOMNodeList, so you can have multiple nodes that match
13 * (3) A string, just the innerXML
14 * (4) An array of strings, just the innerXMLs
15 */
16
17
18function xml_debug( $obj ) {
19 $retval = get_class($obj)."\n";
20 switch(true) {
21 case ($obj instanceof DOMDocument):
22 $retval .= "XPath: {$obj->getNodePath()}\n".$obj->saveXML($obj);
23 break;
24 case ($obj instanceof DOMElement):
25 $retval .= "XPath: {$obj->getNodePath()}\n".$obj->ownerDocument->saveXML($obj);
26 break;
27 case ($obj instanceof DOMAttr):
28 $retval .= "XPath: {$obj->getNodePath()}\n".$obj->ownerDocument->saveXML($obj);
29 break;
30 case ($obj instanceof DOMNodeList):
31 $retval .= "NodeList of length {$obj->length}\n";
32 print $retval;
33 for ($i = 0; $i < $obj->length; $i++) {
34 $retval .= "Item #$i, " . xml_debug($obj->item($i));
35 }
36 break;
37 default:
38 return "Instance of unknown class";
39 }
40 return $retval;
41}
42
43abstract class XMLAsArrayOfWhateversSpecifiedByXPath implements ArrayAccess {
44 public $xml;
46 private $xpath = null;
48 if( is_string($xml) ) {
49 $this->xml = new DOMDocument();
50 $this->xml->loadXML($xml);
51 } elseif( $xml instanceof DOMDocument ) {
52 $this->xml = $xml;
53 } else {
54 throw new Exception("<xml> must be of type DOMDocument in new XMLAsArrayOf...(xml,context)");
55 }
56 $this->xpath = new DOMXPath($this->xml);
57 if( is_string($context_node) ) {
58 $nodeList = $this->xpath->query($context_node);
59 //print( "Context ($context_node) is: " . xml_debug($nodeList) . "\n" );
60 if( $nodeList->length === 1 ) {
61 $this->context_node = $nodeList->item(0);
62 } else {
63 $this->context_node = $this->xml->documentElement;
64 }
65 } elseif( $context_node instanceof DOMElement ) {
66 $this->context_node = $context_node;
67 } else {
68 $this->context_node = $this->xml->documentElement;
69 }
70 }
71 protected function query( string $offset ) {
72 if( isset($this->context_node) ) {
73 return $this->xpath->query($offset, $this->context_node);
74 } else {
75 return $this->xpath->query($offset);
76 }
77 }
78 public function __toString() {
79 return $this->xml->saveXML($this->context_node);
80 }
81 public function __get( string $name ) {
82 $class = get_class($this);
83 return new $class($this->xml, $this->query($name));
84 }
85 public function __set( string $name, $value ) {
86 $this[$name] = $value;
87 }
88 public function __isset( string $name ) {
89 return isset($this[$name]);
90 }
91 public function __unset( string $name ) {
92 unset($this[$name]);
93 }
94}
95
97 public function offsetExists ( $offset ) {
98 return $this->query($offset)->length === 1;
99 }
100 public function offsetGet ( $offset ) {
101 $nodeList = $this->query($offset);
102 if( $nodeList->length === 1 )
103 return $nodeList->item(0);
104 }
105 public function offsetSet ( $offset, $value ) {
106 $nodeList = $this->query($offset);
107 if( $nodeList->length === 1 ) {
108 $node = $nodeList->item(0);
109 if($node->parentNode !== NULL) {
110 $node->parentNode->replaceChild($value, $node);
111 }
112 } elseif( $nodeList->length === 0 ) {
113 $nodeList->context_node->appendChild($value);
114 }
115 }
116 public function offsetUnset ( $offset ) {
117 $nodeList = $this->query($offset);
118 if( $nodeList->length === 1 ) {
119 $node = $nodeList->item(0);
120 if($node->parentNode !== NULL) {
121 $node->parentNode->removeChild($node);
122 }
123 }
124 }
125}
126
128 public function offsetExists ( $offset ) {
129 return $this->query($offset)->length > 0; // technically it always exists, but...
130 }
131 public function offsetGet ( $offset ) {
132 return $this->query($offset);
133 }
134 public function offsetSet ( $offset, $value ) {
135 $nodeList = $this->query($offset);
136 $cur_length = $nodeList->length;
137 $new_length = $value->length;
138 for( $i = 0 ; $i < min($cur_length, $new_length); $i++ ) {
139 $cur_node = $nodeList->item($i);
140 $new_node = $value->item($i);
141 $cur_node->parentNode->replaceChild($new_node, $cur_node);
142 }
143 for( $i = min($cur_length, $new_length); $i < $cur_length ; $i++ ) {
144 $cur_node = $nodeList->item($i);
145 $cur_node->parentNode->removeChild($cur_node);
146 }
147 for( $i = min($cur_length, $new_length); $i < $new_length ; $i++ ) {
148 $new_node = $value->item($i);
149 $this->context_node->appendChild($new_node);
150 }
151 }
152 public function offsetUnset ( $offset ) {
153 $nodeList = $this->query($offset);
154 $cur_length = $nodeList->length;
155 for( $i = 0 ; $i < $cur_length ; $i++ ) {
156 $cur_node = $nodeList->item($i);
157 $cur_node->parentNode->removeChild($cur_node);
158 }
159 }
160}
161
163 public function offsetExists ( $offset ) {
164 return $this->query($offset)->length === 1;
165 }
166 public function offsetGet ( $offset ) {
167 $nodeList = $this->query($offset);
168 if( $nodeList->length === 1 ) {
169 return $nodeList->item(0)->nodeValue;
170 }
171 }
172 public function offsetSet ( $offset, $value ) {
173 $nodeList = $this->query($offset);
174 if( $nodeList->length === 1 ) {
175 print "Setting!\n";
176 $nodeList->item(0)->nodeValue = $value;
177 } else if( $nodeList->length === 0 ) {
178 $node = $this->xml->createElement(basename($offset));
179 $node->nodeValue = $value;
180 $this->contextNode->appendChild($node);
181 }
182 }
183 public function offsetUnset ( $offset ) {
184 if( $nodeList->length === 1 ) {
185 $node = $nodeList->item(0);
186 $node->parentNode->removeChild($node);
187 }
188 }
189}
190
191function test() {
192 $xml = file_get_contents("item-sample.xml");
193 $item_item_data = new XMLAsArrayOfStringsSpecifiedByXPath($xml, "/item/item_data");
194 print "EnumerationA: " . $item_item_data['enumeration_a'] . "\n\n";
195 $item_item_data['enumeration_a'] = "NOOOOOOOOO";
196 print $item_item_data->xml->saveXML();
197}
198
199//test();
__construct( $xml, $context_node)
Definition: grima-xmlbag.php:47
__set(string $name, $value)
Definition: grima-xmlbag.php:85
xml_debug( $obj)
Definition: grima-xmlbag.php:18
test()
Definition: grima-xmlbag.php:191