PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/framework/Xml/TXmlDocument.php

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