PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/Varien/Simplexml/Element.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 631 lines | 435 code | 35 blank | 161 comment | 41 complexity | f8321180f2b343080ffd89591943b239 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Varien
  22. * @package Varien_Simplexml
  23. * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * Extends SimpleXML to add valuable functionality to SimpleXMLElement class
  28. *
  29. * @category Varien
  30. * @package Varien_Simplexml
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Varien_Simplexml_Element extends SimpleXMLElement
  34. {
  35. /**
  36. * Would keep reference to parent node
  37. *
  38. * If SimpleXMLElement would support complicated attributes
  39. *
  40. * @todo make use of spl_object_hash to keep global array of simplexml elements
  41. * to emulate complicated attributes
  42. * @var Varien_Simplexml_Element
  43. */
  44. protected $_parent = null;
  45. /**
  46. * For future use
  47. *
  48. * @param Varien_Simplexml_Element $element
  49. */
  50. public function setParent($element)
  51. {
  52. #$this->_parent = $element;
  53. }
  54. /**
  55. * Returns parent node for the element
  56. *
  57. * Currently using xpath
  58. *
  59. * @return Varien_Simplexml_Element
  60. */
  61. public function getParent()
  62. {
  63. if (!empty($this->_parent)) {
  64. $parent = $this->_parent;
  65. } else {
  66. $arr = $this->xpath('..');
  67. $parent = $arr[0];
  68. }
  69. return $parent;
  70. }
  71. /**
  72. * Enter description here...
  73. *
  74. * @return boolean
  75. */
  76. public function hasChildren()
  77. {
  78. if (!$this->children()) {
  79. return false;
  80. }
  81. // simplexml bug: @attributes is in children() but invisible in foreach
  82. foreach ($this->children() as $k=>$child) {
  83. return true;
  84. }
  85. return false;
  86. }
  87. /**
  88. * Returns attribute value by attribute name
  89. *
  90. * @return string
  91. */
  92. public function getAttribute($name){
  93. $attrs = $this->attributes();
  94. return isset($attrs[$name]) ? (string)$attrs[$name] : null;
  95. }
  96. /*
  97. public function addAttribute($name, $value=null, $namespace=null)
  98. {
  99. if (is_null($value)) {
  100. return parent::addAttribute($name);
  101. } else {
  102. if (!is_null($value)) {
  103. $value = $this->xmlentities($value);
  104. }
  105. if (!is_null($namespace)) {
  106. return parent::addAttribute($name, $value, $namespace);
  107. } else {
  108. return parent::addAttribute($name, $value);
  109. }
  110. }
  111. }
  112. public function addChild($name, $value=null, $namespace=null)
  113. {
  114. if (is_null($value)) {
  115. return parent::addChild($name);
  116. } else {
  117. if (!is_null($value)) {
  118. $value = $this->xmlentities($value);
  119. }
  120. if (!is_null($namespace)) {
  121. return parent::addChild($name, $value, $namespace);
  122. } else {
  123. return parent::addChild($name, $value);
  124. }
  125. }
  126. }
  127. */
  128. /**
  129. * Find a descendant of a node by path
  130. *
  131. * @todo Do we need to make it xpath look-a-like?
  132. * @todo Check if we still need all this and revert to plain XPath if this makes any sense
  133. * @todo param string $path Subset of xpath. Example: "child/grand[@attrName='attrValue']/subGrand"
  134. * @param string $path Example: "child/grand@attrName=attrValue/subGrand" (to make it faster without regex)
  135. * @return Varien_Simplexml_Element
  136. */
  137. public function descend($path)
  138. {
  139. # $node = $this->xpath($path);
  140. # return $node[0];
  141. if (is_array($path)) {
  142. $pathArr = $path;
  143. } else {
  144. // Simple exploding by / does not suffice,
  145. // as an attribute value may contain a / inside
  146. // Note that there are three matches for different kinds of attribute values specification
  147. if(strpos($path, "@") === false) {
  148. $pathArr = explode('/', $path);
  149. }
  150. else {
  151. $regex = "#([^@/\\\"]+(?:@[^=/]+=(?:\\\"[^\\\"]*\\\"|[^/]*))?)/?#";
  152. $pathArr = $pathMatches = array();
  153. if(preg_match_all($regex, $path, $pathMatches)) {
  154. $pathArr = $pathMatches[1];
  155. }
  156. }
  157. }
  158. $desc = $this;
  159. foreach ($pathArr as $nodeName) {
  160. if (strpos($nodeName, '@')!==false) {
  161. $a = explode('@', $nodeName);
  162. $b = explode('=', $a[1]);
  163. $nodeName = $a[0];
  164. $attributeName = $b[0];
  165. $attributeValue = $b[1];
  166. //
  167. // Does a very simplistic trimming of attribute value.
  168. //
  169. $attributeValue = trim($attributeValue, '"');
  170. $found = false;
  171. foreach ($desc->$nodeName as $subdesc) {
  172. if ((string)$subdesc[$attributeName]===$attributeValue) {
  173. $found = true;
  174. $desc = $subdesc;
  175. break;
  176. }
  177. }
  178. if (!$found) {
  179. $desc = false;
  180. }
  181. } else {
  182. $desc = $desc->$nodeName;
  183. }
  184. if (!$desc) {
  185. return false;
  186. }
  187. }
  188. return $desc;
  189. }
  190. /**
  191. * Returns the node and children as an array
  192. *
  193. * @return array|string
  194. */
  195. public function asArray()
  196. {
  197. return $this->_asArray();
  198. }
  199. /**
  200. * asArray() analog, but without attributes
  201. * @return array|string
  202. */
  203. public function asCanonicalArray()
  204. {
  205. return $this->_asArray(true);
  206. }
  207. /**
  208. * Returns the node and children as an array
  209. *
  210. * @param bool $isCanonical - whether to ignore attributes
  211. * @return array|string
  212. */
  213. protected function _asArray($isCanonical = false)
  214. {
  215. $result = array();
  216. if (!$isCanonical) {
  217. // add attributes
  218. foreach ($this->attributes() as $attributeName => $attribute) {
  219. if ($attribute) {
  220. $result['@'][$attributeName] = (string)$attribute;
  221. }
  222. }
  223. }
  224. // add children values
  225. if ($this->hasChildren()) {
  226. foreach ($this->children() as $childName => $child) {
  227. $result[$childName] = $child->_asArray($isCanonical);
  228. }
  229. } else {
  230. if (empty($result)) {
  231. // return as string, if nothing was found
  232. $result = (string) $this;
  233. } else {
  234. // value has zero key element
  235. $result[0] = (string) $this;
  236. }
  237. }
  238. return $result;
  239. }
  240. /**
  241. * Makes nicely formatted XML from the node
  242. *
  243. * @param string $filename
  244. * @param int|boolean $level if false
  245. * @return string
  246. */
  247. public function asNiceXml($filename='', $level=0)
  248. {
  249. if (is_numeric($level)) {
  250. $pad = str_pad('', $level*3, ' ', STR_PAD_LEFT);
  251. $nl = "\n";
  252. } else {
  253. $pad = '';
  254. $nl = '';
  255. }
  256. $out = $pad.'<'.$this->getName();
  257. if ($attributes = $this->attributes()) {
  258. foreach ($attributes as $key=>$value) {
  259. $out .= ' '.$key.'="'.str_replace('"', '\"', (string)$value).'"';
  260. }
  261. }
  262. if ($this->hasChildren()) {
  263. $out .= '>'.$nl;
  264. foreach ($this->children() as $child) {
  265. $out .= $child->asNiceXml('', is_numeric($level) ? $level+1 : true);
  266. }
  267. $out .= $pad.'</'.$this->getName().'>'.$nl;
  268. } else {
  269. $value = (string)$this;
  270. if (strlen($value)) {
  271. $out .= '>'.$this->xmlentities($value).'</'.$this->getName().'>'.$nl;
  272. } else {
  273. $out .= '/>'.$nl;
  274. }
  275. }
  276. if ((0===$level || false===$level) && !empty($filename)) {
  277. file_put_contents($filename, $out);
  278. }
  279. return $out;
  280. }
  281. /**
  282. * Enter description here...
  283. *
  284. * @param int $level
  285. * @return string
  286. */
  287. public function innerXml($level=0)
  288. {
  289. $out = '';
  290. foreach ($this->children() as $child) {
  291. $out .= $child->asNiceXml($level);
  292. }
  293. return $out;
  294. }
  295. /**
  296. * Converts meaningful xml characters to xml entities
  297. *
  298. * @param string
  299. * @return string
  300. */
  301. public function xmlentities($value = null)
  302. {
  303. if (is_null($value)) {
  304. $value = $this;
  305. }
  306. $value = (string)$value;
  307. $value = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $value);
  308. return $value;
  309. }
  310. /**
  311. * Appends $source to current node
  312. *
  313. * @param Varien_Simplexml_Element $source
  314. * @return Varien_Simplexml_Element
  315. */
  316. public function appendChild($source)
  317. {
  318. if ($source->children()) {
  319. /**
  320. * @see http://bugs.php.net/bug.php?id=41867 , fixed in 5.2.4
  321. */
  322. if (version_compare(phpversion(), '5.2.4', '<')===true) {
  323. $name = $source->children()->getName();
  324. }
  325. else {
  326. $name = $source->getName();
  327. }
  328. $child = $this->addChild($name);
  329. } else {
  330. $child = $this->addChild($source->getName(), $this->xmlentities($source));
  331. }
  332. $child->setParent($this);
  333. $attributes = $source->attributes();
  334. foreach ($attributes as $key=>$value) {
  335. $child->addAttribute($key, $this->xmlentities($value));
  336. }
  337. foreach ($source->children() as $sourceChild) {
  338. $child->appendChild($sourceChild);
  339. }
  340. return $this;
  341. }
  342. /**
  343. * Extends current node with xml from $source
  344. *
  345. * If $overwrite is false will merge only missing nodes
  346. * Otherwise will overwrite existing nodes
  347. *
  348. * @param Varien_Simplexml_Element $source
  349. * @param boolean $overwrite
  350. * @return Varien_Simplexml_Element
  351. */
  352. public function extend($source, $overwrite=false)
  353. {
  354. if (!$source instanceof Varien_Simplexml_Element) {
  355. return $this;
  356. }
  357. foreach ($source->children() as $child) {
  358. $this->extendChild($child, $overwrite);
  359. }
  360. return $this;
  361. }
  362. /**
  363. * Extends one node
  364. *
  365. * @param Varien_Simplexml_Element $source
  366. * @param boolean $overwrite
  367. * @return Varien_Simplexml_Element
  368. */
  369. public function extendChild($source, $overwrite=false)
  370. {
  371. // this will be our new target node
  372. $targetChild = null;
  373. // name of the source node
  374. $sourceName = $source->getName();
  375. // here we have children of our source node
  376. $sourceChildren = $source->children();
  377. if (!$source->hasChildren()) {
  378. // handle string node
  379. if (isset($this->$sourceName)) {
  380. // if target already has children return without regard
  381. if ($this->$sourceName->children()) {
  382. return $this;
  383. }
  384. if ($overwrite) {
  385. unset($this->$sourceName);
  386. } else {
  387. return $this;
  388. }
  389. }
  390. $targetChild = $this->addChild($sourceName, $source->xmlentities());
  391. $targetChild->setParent($this);
  392. foreach ($source->attributes() as $key=>$value) {
  393. $targetChild->addAttribute($key, $this->xmlentities($value));
  394. }
  395. return $this;
  396. }
  397. if (isset($this->$sourceName)) {
  398. $targetChild = $this->$sourceName;
  399. }
  400. if (is_null($targetChild)) {
  401. // if child target is not found create new and descend
  402. $targetChild = $this->addChild($sourceName);
  403. $targetChild->setParent($this);
  404. foreach ($source->attributes() as $key=>$value) {
  405. $targetChild->addAttribute($key, $this->xmlentities($value));
  406. }
  407. }
  408. // finally add our source node children to resulting new target node
  409. foreach ($sourceChildren as $childKey=>$childNode) {
  410. $targetChild->extendChild($childNode, $overwrite);
  411. }
  412. return $this;
  413. }
  414. public function setNode($path, $value, $overwrite=true)
  415. {
  416. $arr1 = explode('/', $path);
  417. $arr = array();
  418. foreach ($arr1 as $v) {
  419. if (!empty($v)) $arr[] = $v;
  420. }
  421. $last = sizeof($arr)-1;
  422. $node = $this;
  423. foreach ($arr as $i=>$nodeName) {
  424. if ($last===$i) {
  425. /*
  426. if (isset($xml->$nodeName)) {
  427. if ($overwrite) {
  428. unset($xml->$nodeName);
  429. } else {
  430. continue;
  431. }
  432. }
  433. $xml->addChild($nodeName, $xml->xmlentities($value));
  434. */
  435. if (!isset($node->$nodeName) || $overwrite) {
  436. // http://bugs.php.net/bug.php?id=36795
  437. // comment on [8 Feb 8:09pm UTC]
  438. if (isset($node->$nodeName) && (version_compare(phpversion(), '5.2.6', '<')===true)) {
  439. $node->$nodeName = $node->xmlentities($value);
  440. } else {
  441. $node->$nodeName = $value;
  442. }
  443. }
  444. } else {
  445. if (!isset($node->$nodeName)) {
  446. $node = $node->addChild($nodeName);
  447. } else {
  448. $node = $node->$nodeName;
  449. }
  450. }
  451. }
  452. return $this;
  453. }
  454. /*
  455. public function extendChildByNode($source, $overwrite=false, $mergeBy='name')
  456. {
  457. // this will be our new target node
  458. $targetChild = null;
  459. // name of the source node
  460. $sourceName = $source->getName();
  461. // here we have children of our source node
  462. $sourceChildren = $source->children();
  463. if (!$sourceChildren) {
  464. // handle string node
  465. if (isset($this->$sourceName)) {
  466. if ($overwrite) {
  467. unset($this->$sourceName);
  468. } else {
  469. return $this;
  470. }
  471. }
  472. $targetChild = $this->addChild($sourceName, (string)$source);
  473. foreach ($source->attributes() as $key=>$value) {
  474. $targetChild->addAttribute($key, $value);
  475. }
  476. return $this;
  477. }
  478. if (isset($this->$sourceName)) {
  479. // search for target child with same name subnode as node's name
  480. if (isset($source->$mergeBy)) {
  481. foreach ($this->$sourceName as $targetNode) {
  482. if (!isset($targetNode->$mergeBy)) {
  483. Zend::exception("Can't merge identified node with non identified");
  484. }
  485. if ((string)$source->$mergeBy==(string)$targetNode->$mergeBy) {
  486. $targetChild = $targetNode;
  487. break;
  488. }
  489. }
  490. } else {
  491. $existsWithId = false;
  492. foreach ($this->$sourceName as $targetNode) {
  493. if (isset($targetNode->$mergeBy)) {
  494. Zend::exception("Can't merge identified node with non identified");
  495. }
  496. }
  497. $targetChild = $this->$sourceName;
  498. }
  499. }
  500. if (is_null($targetChild)) {
  501. // if child target is not found create new and descend
  502. $targetChild = $this->addChild($sourceName);
  503. foreach ($source->attributes() as $key=>$value) {
  504. $targetChild->addAttribute($key, $value);
  505. }
  506. }
  507. // finally add our source node children to resulting new target node
  508. foreach ($sourceChildren as $childKey=>$childNode) {
  509. $targetChild->extendChildByNode($childNode, $overwrite, $mergeBy);
  510. }
  511. return $this;
  512. }
  513. public function extendChildByAttribute($source, $overwrite=false, $mergeBy='name')
  514. {
  515. // this will be our new target node
  516. $targetChild = null;
  517. // name of the source node
  518. $sourceName = $source->getName();
  519. // here we have children of our source node
  520. $sourceChildren = $source->children();
  521. if (!$sourceChildren) {
  522. // handle string node
  523. if (isset($this->$sourceName)) {
  524. if ($overwrite) {
  525. unset($this->$sourceName);
  526. } else {
  527. return $this;
  528. }
  529. }
  530. $targetChild = $this->addChild($sourceName, (string)$source);
  531. foreach ($source->attributes() as $key=>$value) {
  532. $targetChild->addAttribute($key, $value);
  533. }
  534. return $this;
  535. }
  536. if (isset($this->$sourceName)) {
  537. // search for target child with same name subnode as node's name
  538. if (isset($source[$mergeBy])) {
  539. foreach ($this->$sourceName as $targetNode) {
  540. if (!isset($targetNode[$mergeBy])) {
  541. Zend::exception("Can't merge identified node with non identified");
  542. }
  543. if ((string)$source[$mergeBy]==(string)$targetNode[$mergeBy]) {
  544. $targetChild = $targetNode;
  545. break;
  546. }
  547. }
  548. } else {
  549. $existsWithId = false;
  550. foreach ($this->$sourceName as $targetNode) {
  551. if (isset($targetNode[$mergeBy])) {
  552. Zend::exception("Can't merge identified node with non identified");
  553. }
  554. }
  555. $targetChild = $this->$sourceName;
  556. }
  557. }
  558. if (is_null($targetChild)) {
  559. // if child target is not found create new and descend
  560. $targetChild = $this->addChild($sourceName);
  561. foreach ($source->attributes() as $key=>$value) {
  562. $targetChild->addAttribute($key, $value);
  563. }
  564. }
  565. // finally add our source node children to resulting new target node
  566. foreach ($sourceChildren as $childKey=>$childNode) {
  567. $targetChild->extendChildByAttribute($childNode, $overwrite, $mergeBy);
  568. }
  569. return $this;
  570. }
  571. */
  572. }