1: <?php
2: /**
3: * This file contains the base XML class.
4: *
5: * @package Core
6: * @subpackage XML
7: * @version SVN Revision $Rev:$
8: *
9: * @author Dominik Ziegler
10: * @copyright four for business AG <www.4fb.de>
11: * @license http://www.contenido.org/license/LIZENZ.txt
12: * @link http://www.4fb.de
13: * @link http://www.contenido.org
14: */
15:
16: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
17:
18: /**
19: * Base XML class
20: *
21: * @package Core
22: * @subpackage XML
23: */
24: abstract class cXmlBase {
25:
26: protected $_dom = NULL;
27:
28: protected $_xpath = NULL;
29:
30: /**
31: * Creates a new XML document using DOMDocument.
32: *
33: * @access protected
34: *
35: * @param string $version version of DOMDocument (optional, default: 1.0)
36: * @param string $encoding encoding of DOMDocumen (optional, default: UTF-8)
37: */
38: protected function _createDocument($version = '', $encoding = '') {
39: if ($version == '') {
40: $version = '1.0';
41: }
42:
43: if ($encoding == '') {
44: $encoding = 'UTF-8';
45: }
46:
47: $this->_dom = new DOMDocument($version, $encoding);
48: }
49:
50: /**
51: * Returns the DOMDocument object.
52: * @return DOMDocument
53: */
54: public function getDomDocument() {
55: return $this->_dom;
56: }
57:
58: /**
59: * Sets a current DOMDocument object to class.
60: * @param DOMDocument $domDocument DOMDocument object
61: */
62: public function setDomDocument(DOMDocument $domDocument) {
63: $this->_dom = $domDocument;
64: $this->_initXpathInstance();
65: }
66:
67: /**
68: * Returns the encoding of the XML document.
69: *
70: * @throws cException if there is no DOM document
71: * @return string encoding
72: */
73: public function getEncoding() {
74: if ($this->_dom === NULL) {
75: throw new cException('Can not determine encoding: DOMDocument not found.');
76: }
77:
78: return $this->_dom->xmlEncoding;
79: }
80:
81: /**
82: *
83: * @param string $name
84: * @param string $value
85: */
86: public function registerXpathNamespace($name, $value) {
87: $this->_xpath->registerNamespace($name, $value);
88: }
89:
90: /**
91: * Initializes a new DOMXPath instance for DOMDocument.
92: *
93: * @throws cException if there is no valid DOM document
94: */
95: protected function _initXpathInstance() {
96: if (!($this->_dom instanceof DOMDocument)) {
97: throw new cException('Can not initialize XPath instance: DOMDocument not found.');
98: }
99:
100: $this->_xpath = new DOMXpath($this->_dom);
101: }
102:
103: /**
104: * Resolves a given path which contains ".." statement for moving up one
105: * level in path.
106: * @param string $path path to resolve
107: *
108: * @return string resolved path
109: */
110: public static function resolvePath($path) {
111: if (substr($path, 0, 1) != '/') {
112: $path = '/' . $path;
113: }
114:
115: $splits = explode('/', $path);
116:
117: foreach ($splits as $i => $sSplitter) {
118: if ($sSplitter == '..') {
119: unset($splits[$i]);
120: unset($splits[$i - 1]);
121: }
122: }
123:
124: $pathString = implode('/', $splits);
125:
126: if (substr($pathString, -1) == '/') {
127: $pathString = substr($pathString, 0, -1);
128: }
129:
130: return $pathString;
131: }
132:
133: /**
134: * Returns given XPath with integrad level definition.
135: *
136: * @param string $path XPath to extend
137: * @param integer $level level
138: *
139: * @return string extended XPath
140: */
141: public static function getLevelXpath($path, $level) {
142: $splits = explode('/', $path);
143: $splitCount = count($splits);
144:
145: if ($splitCount <= 1) {
146: return $path;
147: }
148:
149: $lastElementName = $splits[$splitCount - 1];
150: unset($splits[$splitCount - 1]);
151:
152: $returnPath = implode('/', $splits);
153: $returnPath .= '[' . ($level + 1) . ']/' . $lastElementName;
154:
155: return $returnPath;
156: }
157:
158: /**
159: * Converts an array to a SimpleXMLElement. Example:
160: * array(
161: * 'key1' => 'value1',
162: * 'key2' => array('value21', 'value22'),
163: * 'key3' => array('key31' => 'value31', 'key32' => 'value32')
164: * );
165: *
166: * becomes
167: *
168: * <?/**
169: * Converts an array to a SimpleXMLElement.
170: * Example:
171: * array(
172: * 'key1' => 'value1',
173: * 'key2' => array('value21', 'value22'),
174: * 'key3' => array('key31' => 'value31', 'key32' => 'value32')
175: * );
176: *
177: * becomes
178: *
179: * <?xml version="1.0" encoding="utf-8"?>
180: * <root>
181: * <key1>value1</key1>
182: * <key2>
183: * <array_value>value21</array_value>
184: * <array_value>value22</array_value>
185: * </key2>
186: * <key3>
187: * <key31>value31</key31>
188: * <key32>value32</key32>
189: * </key3>
190: * </root>
191: *
192: * @param array $array the array which should be converted to XML
193: * @param SimpleXMLElement $xml [optional] the element to which the array
194: * should be added
195: * @param string $rootTagName [optional] the root tag name which should be
196: * used - is only used when $xml is NULL!
197: * @return SimpleXMLElement the array as a SimpleXMLElement
198: */
199:
200: public static function arrayToXml($array, $xml = NULL, $rootTagName = 'root') {
201: if ($xml == NULL) {
202: $xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><' . $rootTagName . '/>', LIBXML_NOCDATA);
203: }
204:
205: // check whether array is associative
206: if ($array !== array_values($array)) {
207: // if array is associative, use the keys as well as the values
208: foreach ($array as $key => $value) {
209: // recursion if value is an array
210: if (is_array($value)) {
211: self::arrayToXml($value, $xml->addChild($key));
212: } else {
213: $child = $xml->addChild($key);
214: $node = dom_import_simplexml($child);
215: $no = $node->ownerDocument;
216: $node->appendChild($no->createCDATASection($value));
217: }
218: }
219: } else {
220: // if array is not associative, use the array values as separate xml
221: // nodes
222: foreach ($array as $value) {
223: $child = $xml->addChild('array_value');
224: $node = dom_import_simplexml($child);
225: $no = $node->ownerDocument;
226: $node->appendChild($no->createCDATASection($value));
227: }
228: }
229:
230: return $xml;
231: }
232:
233: /**
234: * Converts the given XML string to an array.
235: * Example:
236: * <?xml version="1.0" encoding="utf-8"?>
237: * <root>
238: * <key1>value1</key1>
239: * <key2>
240: * <array_value>value21</array_value>
241: * <array_value>value22</array_value>
242: * </key2>
243: * <key3>
244: * <key31>value31</key31>
245: * <key32>value32</key32>
246: * </key3>
247: * </root>
248: *
249: * becomes
250: *
251: * array(
252: * 'key1' => 'value1',
253: * 'key2' => array('value21', 'value22'),
254: * 'key3' => array('key31' => 'value31', 'key32' => 'value32')
255: * );
256: *
257: * @param string $xmlString contains a valid XML structure
258: */
259:
260: public static function xmlStringToArray($xmlString) {
261: return self::xmlToArray(new SimpleXMLElement($xmlString, LIBXML_NOCDATA));
262: }
263:
264: /**
265: * Converts the given SimpleXMLElement object to an array.
266: * Example:
267: * <?xml version="1.0" encoding="utf-8"?>
268: * <root>
269: * <key1>value1</key1>
270: * <key2>
271: * <array_value>value21</array_value>
272: * <array_value>value22</array_value>
273: * </key2>
274: * <key3>
275: * <key31>value31</key31>
276: * <key32>value32</key32>
277: * </key3>
278: * </root>
279: *
280: * becomes
281: *
282: * array(
283: * 'key1' => 'value1',
284: * 'key2' => array('value21', 'value22'),
285: * 'key3' => array('key31' => 'value31', 'key32' => 'value32')
286: * );
287: *
288: * @param SimpleXMLElement $xml
289: */
290:
291: public static function xmlToArray($xml) {
292: $json = json_encode($xml);
293: $array = json_decode($json, true);
294: $array = self::_cleanArray($array);
295:
296: return $array;
297: }
298:
299: /**
300: * Cleans an array by replacing all empty arrays with empty strings.
301: * Additionally, the function replaces all associative arrays which have
302: * only empty values with the array keys of the array.
303: *
304: * @param array $array the array to clean
305: * @return array the cleaned array
306: */
307: private static function _cleanArray($array) {
308: // replace empty arrays with empty strings recursively
309: foreach ($array as $key => $value) {
310: if (is_array($value)) {
311: if (empty($value)) {
312: $array[$key] = '';
313: } else {
314: // if array contains array values, take them directly
315: if ($key == 'array_value') {
316: return $array['array_value'];
317: }
318: $array[$key] = self::_cleanArray($value);
319: }
320: }
321: }
322: // if array only contains empty values, return the array keys
323: if (count(array_keys($array, '')) == count($array)) {
324: return array_keys($array);
325: } else if (count(array_keys($array, 'array_value')) == count($array)) {
326: }
327:
328: return $array;
329: }
330:
331: }
332: