Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TXmlElement, TXmlDocument, TXmlElementList class file
4
 *
5
 * @author Qiang Xue <qiang.xue@gmail.com>
6
 * @link http://www.pradosoft.com/
7
 * @copyright Copyright &copy; 2005-2008 PradoSoft
8
 * @license http://www.pradosoft.com/license/
9
 * @version $Id: TXmlDocument.php 2541 2008-10-21 15:05:13Z qiang.xue $
10
 * @package System.Xml
11
 */
12
 
13
/**
14
 * TXmlElement class.
15
 *
16
 * TXmlElement represents an XML element node.
17
 * You can obtain its tag-name, attributes, text between the opening and closing
18
 * tags via the TagName, Attributes, and Value properties, respectively.
19
 * You can also retrieve its parent and child elements by Parent and Elements
20
 * properties, respectively.
21
 *
22
 * TBD: xpath
23
 *
24
 * @author Qiang Xue <qiang.xue@gmail.com>
25
 * @version $Id: TXmlDocument.php 2541 2008-10-21 15:05:13Z qiang.xue $
26
 * @package System.Xml
27
 * @since 3.0
28
 */
29
class TXmlElement extends TComponent
30
{
31
	/**
32
	 * @var TXmlElement parent of this element
33
	 */
34
	private $_parent=null;
35
	/**
36
	 * @var string tag-name of this element
37
	 */
38
	private $_tagName='unknown';
39
	/**
40
	 * @var string text enclosed between opening and closing tags of this element
41
	 */
42
	private $_value='';
43
	/**
44
	 * @var TXmlElementList list of child elements of this element
45
	 */
46
	private $_elements=null;
47
	/**
48
	 * @var TMap attributes of this element
49
	 */
50
	private $_attributes=null;
51
 
52
	/**
53
	 * Constructor.
54
	 * @param string tag-name for this element
55
	 */
56
	public function __construct($tagName)
57
	{
58
		$this->setTagName($tagName);
59
	}
60
 
61
	/**
62
	 * @return TXmlElement parent element of this element
63
	 */
64
	public function getParent()
65
	{
66
		return $this->_parent;
67
	}
68
 
69
	/**
70
	 * @param TXmlElement parent element of this element
71
	 */
72
	public function setParent($parent)
73
	{
74
		$this->_parent=$parent;
75
	}
76
 
77
	/**
78
	 * @return string tag-name of this element
79
	 */
80
	public function getTagName()
81
	{
82
		return $this->_tagName;
83
	}
84
 
85
	/**
86
	 * @param string tag-name of this element
87
	 */
88
	public function setTagName($tagName)
89
	{
90
		$this->_tagName=$tagName;
91
	}
92
 
93
	/**
94
	 * @return string text enclosed between opening and closing tag of this element
95
	 */
96
	public function getValue()
97
	{
98
		return $this->_value;
99
	}
100
 
101
	/**
102
	 * @param string text enclosed between opening and closing tag of this element
103
	 */
104
	public function setValue($value)
105
	{
106
		$this->_value=$value;
107
	}
108
 
109
	/**
110
	 * @return boolean true if this element has child elements
111
	 */
112
	public function getHasElement()
113
	{
114
		return $this->_elements!==null && $this->_elements->getCount()>0;
115
	}
116
 
117
	/**
118
	 * @return boolean true if this element has attributes
119
	 */
120
	public function getHasAttribute()
121
	{
122
		return $this->_attributes!==null && $this->_attributes->getCount()>0;
123
	}
124
 
125
	/**
126
	 * @return string the attribute specified by the name, null if no such attribute
127
	 */
128
	public function getAttribute($name)
129
	{
130
		if($this->_attributes!==null)
131
			return $this->_attributes->itemAt($name);
132
		else
133
			return null;
134
	}
135
 
136
	/**
137
	 * @param string attribute name
138
	 * @param string attribute value
139
	 */
140
	public function setAttribute($name,$value)
141
	{
142
		$this->getAttributes()->add($name,$value);
143
	}
144
 
145
	/**
146
	 * @return TXmlElementList list of child elements
147
	 */
148
	public function getElements()
149
	{
150
		if(!$this->_elements)
151
			$this->_elements=new TXmlElementList($this);
152
		return $this->_elements;
153
	}
154
 
155
	/**
156
	 * @return TMap list of attributes
157
	 */
158
	public function getAttributes()
159
	{
160
		if(!$this->_attributes)
161
			$this->_attributes=new TMap;
162
		return $this->_attributes;
163
	}
164
 
165
	/**
166
	 * @return TXmlElement the first child element that has the specified tag-name, null if not found
167
	 */
168
	public function getElementByTagName($tagName)
169
	{
170
		if($this->_elements)
171
		{
172
			foreach($this->_elements as $element)
173
				if($element->_tagName===$tagName)
174
					return $element;
175
		}
176
		return null;
177
	}
178
 
179
	/**
180
	 * @return TList list of all child elements that have the specified tag-name
181
	 */
182
	public function getElementsByTagName($tagName)
183
	{
184
		$list=new TList;
185
		if($this->_elements)
186
		{
187
			foreach($this->_elements as $element)
188
				if($element->_tagName===$tagName)
189
					$list->add($element);
190
		}
191
		return $list;
192
	}
193
 
194
	/**
195
	 * @return string string representation of this element
196
	 */
197
	public function toString($indent=0)
198
	{
199
		$attr='';
200
		if($this->_attributes!==null)
201
		{
202
			foreach($this->_attributes as $name=>$value)
203
			{
204
				$value=$this->xmlEncode($value);
205
				$attr.=" $name=\"$value\"";
206
			}
207
		}
208
		$prefix=str_repeat(' ',$indent*4);
209
		if($this->getHasElement())
210
		{
211
			$str=$prefix."<{$this->_tagName}$attr>\n";
212
			foreach($this->getElements() as $element)
213
				$str.=$element->toString($indent+1)."\n";
214
			$str.=$prefix."</{$this->_tagName}>";
215
			return $str;
216
		}
217
		else if(($value=$this->getValue())!=='')
218
		{
219
			$value=$this->xmlEncode($value);
220
			return $prefix."<{$this->_tagName}$attr>$value</{$this->_tagName}>";
221
		}
222
		else
223
			return $prefix."<{$this->_tagName}$attr />";
224
	}
225
 
226
	/**
227
	 * Magic-method override. Called whenever this element is used as a string.
228
	 * <code>
229
	 * $element = new TXmlElement('tag');
230
	 * echo $element;
231
	 * </code>
232
	 * or
233
	 * <code>
234
	 * $element = new TXmlElement('tag');
235
	 * $xml = (string)$element;
236
	 * </code>
237
	 * @return string string representation of this element
238
	 */
239
	public function __toString()
240
	{
241
		return $this->toString();
242
	}
243
 
244
	private function xmlEncode($str)
245
	{
246
		return strtr($str,array(
247
			'>'=>'&gt;',
248
			'<'=>'&lt;',
249
			'&'=>'&amp;',
250
			'"'=>'&quot;',
251
			"\r"=>'&#xD;',
252
			"\t"=>'&#x9;',
253
			"\n"=>'&#xA;'));
254
	}
255
}
256
 
257
/**
258
 * TXmlDocument class.
259
 *
260
 * TXmlDocument represents a DOM representation of an XML file.
261
 * Besides all properties and methods inherited from {@link TXmlElement},
262
 * you can load an XML file or string by {@link loadFromFile} or {@link loadFromString}.
263
 * You can also get the version and encoding of the XML document by
264
 * the Version and Encoding properties.
265
 *
266
 * To construct an XML string, you may do the following:
267
 * <code>
268
 * $doc=new TXmlDocument('1.0','utf-8');
269
 * $doc->TagName='Root';
270
 *
271
 * $proc=new TXmlElement('Proc');
272
 * $proc->setAttribute('Name','xxxx');
273
 * $doc->Elements[]=$proc;
274
 *
275
 * $query=new TXmlElement('Query');
276
 * $query->setAttribute('ID','xxxx');
277
 * $proc->Elements[]=$query;
278
 *
279
 * $attr=new TXmlElement('Attr');
280
 * $attr->setAttribute('Name','aaa');
281
 * $attr->Value='1';
282
 * $query->Elements[]=$attr;
283
 *
284
 * $attr=new TXmlElement('Attr');
285
 * $attr->setAttribute('Name','bbb');
286
 * $attr->Value='1';
287
 * $query->Elements[]=$attr;
288
 * </code>
289
 * The above code represents the following XML string:
290
 * <code>
291
 * <?xml version="1.0" encoding="utf-8"?>
292
 * <Root>
293
 *   <Proc Name="xxxx">
294
 *     <Query ID="xxxx">
295
 *       <Attr Name="aaa">1</Attr>
296
 *       <Attr Name="bbb">1</Attr>
297
 *     </Query>
298
 *   </Proc>
299
 * </Root>
300
 * </code>
301
 *
302
 * @author Qiang Xue <qiang.xue@gmail.com>
303
 * @version $Id: TXmlDocument.php 2541 2008-10-21 15:05:13Z qiang.xue $
304
 * @package System.Xml
305
 * @since 3.0
306
 */
307
class TXmlDocument extends TXmlElement
308
{
309
	/**
310
	 * @var string version of this XML document
311
	 */
312
	private $_version;
313
	/**
314
	 * @var string encoding of this XML document
315
	 */
316
	private $_encoding;
317
 
318
	/**
319
	 * Constructor.
320
	 * @param string version of this XML document
321
	 * @param string encoding of this XML document
322
	 */
323
	public function __construct($version='1.0',$encoding='')
324
	{
325
		parent::__construct('');
326
		$this->setVersion($version);
327
		$this->setEncoding($encoding);
328
	}
329
 
330
	/**
331
	 * @return string version of this XML document
332
	 */
333
	public function getVersion()
334
	{
335
		return $this->_version;
336
	}
337
 
338
	/**
339
	 * @param string version of this XML document
340
	 */
341
	public function setVersion($version)
342
	{
343
		$this->_version=$version;
344
	}
345
 
346
	/**
347
	 * @return string encoding of this XML document
348
	 */
349
	public function getEncoding()
350
	{
351
		return $this->_encoding;
352
	}
353
 
354
	/**
355
	 * @param string encoding of this XML document
356
	 */
357
	public function setEncoding($encoding)
358
	{
359
		$this->_encoding=$encoding;
360
	}
361
 
362
	/**
363
	 * Loads and parses an XML document.
364
	 * @param string the XML file path
365
	 * @return boolean whether the XML file is parsed successfully
366
	 * @throws TIOException if the file fails to be opened.
367
	 */
368
	public function loadFromFile($file)
369
	{
370
		if(($str=@file_get_contents($file))!==false)
371
			return $this->loadFromString($str);
372
		else
373
			throw new TIOException('xmldocument_file_read_failed',$file);
374
	}
375
 
376
	/**
377
	 * Loads and parses an XML string.
378
	 * The version and encoding will be determined based on the parsing result.
379
	 * @param string the XML string
380
	 * @return boolean whether the XML string is parsed successfully
381
	 */
382
	public function loadFromString($string)
383
	{
384
		// TODO: since PHP 5.1, we can get parsing errors and throw them as exception
385
		$doc=new DOMDocument();
386
		if($doc->loadXML($string)===false)
387
			return false;
388
 
389
		$this->setEncoding($doc->encoding);
390
		$this->setVersion($doc->version);
391
 
392
		$element=$doc->documentElement;
393
		$this->setTagName($element->tagName);
394
		$this->setValue($element->nodeValue);
395
		$elements=$this->getElements();
396
		$attributes=$this->getAttributes();
397
		$elements->clear();
398
		$attributes->clear();
399
		foreach($element->attributes as $name=>$attr)
400
			$attributes->add($name,$attr->value);
401
		foreach($element->childNodes as $child)
402
		{
403
			if($child instanceof DOMElement)
404
				$elements->add($this->buildElement($child));
405
		}
406
 
407
		return true;
408
	}
409
 
410
	/**
411
	 * Saves this XML document as an XML file.
412
	 * @param string the name of the file to be stored with XML output
413
	 * @throws TIOException if the file cannot be written
414
	 */
415
	public function saveToFile($file)
416
	{
417
		if(($fw=fopen($file,'w'))!==false)
418
		{
419
			fwrite($fw,$this->saveToString());
420
			fclose($fw);
421
		}
422
		else
423
			throw new TIOException('xmldocument_file_write_failed',$file);
424
	}
425
 
426
	/**
427
	 * Saves this XML document as an XML string
428
	 * @return string the XML string of this XML document
429
	 */
430
	public function saveToString()
431
	{
432
		$version=empty($this->_version)?' version="1.0"':' version="'.$this->_version.'"';
433
		$encoding=empty($this->_encoding)?'':' encoding="'.$this->_encoding.'"';
434
		return "<?xml{$version}{$encoding}?>\n".$this->toString(0);
435
	}
436
 
437
	/**
438
	 * Magic-method override. Called whenever this document is used as a string.
439
	 * <code>
440
	 * $document = new TXmlDocument();
441
	 * $document->TagName = 'root';
442
	 * echo $document;
443
	 * </code>
444
	 * or
445
	 * <code>
446
	 * $document = new TXmlDocument();
447
	 * $document->TagName = 'root';
448
	 * $xml = (string)$document;
449
	 * </code>
450
	 * @return string string representation of this document
451
	 */
452
	public function __toString()
453
	{
454
		return $this->saveToString();
455
	}
456
 
457
	/**
458
	 * Recursively converts DOM XML nodes into TXmlElement
459
	 * @param DOMXmlNode the node to be converted
460
	 * @return TXmlElement the converted TXmlElement
461
	 */
462
	private function buildElement($node)
463
	{
464
		$element=new TXmlElement($node->tagName);
465
		$element->setValue($node->nodeValue);
466
		foreach($node->attributes as $name=>$attr)
467
			$element->getAttributes()->add($name,$attr->value);
468
		foreach($node->childNodes as $child)
469
		{
470
			if($child instanceof DOMElement)
471
				$element->getElements()->add($this->buildElement($child));
472
		}
473
		return $element;
474
	}
475
}
476
 
477
 
478
/**
479
 * TXmlElementList class.
480
 *
481
 * TXmlElementList represents a collection of {@link TXmlElement}.
482
 * You may manipulate the collection with the operations defined in {@link TList}.
483
 *
484
 * @author Qiang Xue <qiang.xue@gmail.com>
485
 * @version $Id: TXmlDocument.php 2541 2008-10-21 15:05:13Z qiang.xue $
486
 * @package System.Xml
487
 * @since 3.0
488
 */
489
class TXmlElementList extends TList
490
{
491
	/**
492
	 * @var TXmlElement owner of this list
493
	 */
494
	private $_o;
495
 
496
	/**
497
	 * Constructor.
498
	 * @param TXmlElement owner of this list
499
	 */
500
	public function __construct(TXmlElement $owner)
501
	{
502
		$this->_o=$owner;
503
	}
504
 
505
	/**
506
	 * @return TXmlElement owner of this list
507
	 */
508
	protected function getOwner()
509
	{
510
		return $this->_o;
511
	}
512
 
513
	/**
514
	 * Inserts an item at the specified position.
515
	 * This overrides the parent implementation by performing additional
516
	 * operations for each newly added TXmlElement object.
517
	 * @param integer the specified position.
518
	 * @param mixed new item
519
	 * @throws TInvalidDataTypeException if the item to be inserted is not a TXmlElement object.
520
	 */
521
	public function insertAt($index,$item)
522
	{
523
		if($item instanceof TXmlElement)
524
		{
525
			parent::insertAt($index,$item);
526
			if($item->getParent()!==null)
527
				$item->getParent()->getElements()->remove($item);
528
			$item->setParent($this->_o);
529
		}
530
		else
531
			throw new TInvalidDataTypeException('xmlelementlist_xmlelement_required');
532
	}
533
 
534
	/**
535
	 * Removes an item at the specified position.
536
	 * This overrides the parent implementation by performing additional
537
	 * cleanup work when removing a TXmlElement object.
538
	 * @param integer the index of the item to be removed.
539
	 * @return mixed the removed item.
540
	 */
541
	public function removeAt($index)
542
	{
543
		$item=parent::removeAt($index);
544
		if($item instanceof TXmlElement)
545
			$item->setParent(null);
546
		return $item;
547
	}
548
}
549