PageRenderTime 26ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/magento/framework/Simplexml/Element.php

https://gitlab.com/daigiangaitu91/magento
PHP | 486 lines | 353 code | 23 blank | 110 comment | 34 complexity | 87619b28394e4945c6de0879d32a25af MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2015 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Simplexml;
  7. /**
  8. * Extends SimpleXML to add valuable functionality to \SimpleXMLElement class
  9. */
  10. class Element extends \SimpleXMLElement
  11. {
  12. /**
  13. * Would keep reference to parent node
  14. *
  15. * If \SimpleXMLElement would support complicated attributes
  16. *
  17. * @todo make use of spl_object_hash to keep global array of simplexml elements
  18. * to emulate complicated attributes
  19. * @var \Magento\Framework\Simplexml\Element
  20. */
  21. protected $_parent = null;
  22. /**
  23. * For future use
  24. *
  25. * @param \Magento\Framework\Simplexml\Element $element
  26. * @return void
  27. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  28. */
  29. public function setParent($element)
  30. {
  31. //$this->_parent = $element;
  32. }
  33. /**
  34. * Returns parent node for the element
  35. *
  36. * Currently using xpath
  37. *
  38. * @throws \InvalidArgumentException
  39. * @return \Magento\Framework\Simplexml\Element
  40. */
  41. public function getParent()
  42. {
  43. if (!empty($this->_parent)) {
  44. $parent = $this->_parent;
  45. } else {
  46. $arr = $this->xpath('..');
  47. if (!isset($arr[0])) {
  48. throw new \InvalidArgumentException('Root node could not be unset.');
  49. }
  50. $parent = $arr[0];
  51. }
  52. return $parent;
  53. }
  54. /**
  55. * Enter description here...
  56. *
  57. * @return boolean
  58. * @SuppressWarnings(PHPMD.UnusedLocalVariable)
  59. */
  60. public function hasChildren()
  61. {
  62. if (!$this->children()) {
  63. return false;
  64. }
  65. // simplexml bug: @attributes is in children() but invisible in foreach
  66. foreach ($this->children() as $k => $child) {
  67. return true;
  68. }
  69. return false;
  70. }
  71. /**
  72. * Returns attribute value by attribute name
  73. *
  74. * @param string $name
  75. * @return string|null
  76. */
  77. public function getAttribute($name)
  78. {
  79. $attrs = $this->attributes();
  80. return isset($attrs[$name]) ? (string)$attrs[$name] : null;
  81. }
  82. /**
  83. * Find a descendant of a node by path
  84. *
  85. * @todo Do we need to make it xpath look-a-like?
  86. * @todo Check if we still need all this and revert to plain XPath if this makes any sense
  87. * @todo param string $path Subset of xpath. Example: "child/grand[@attrName='attrValue']/subGrand"
  88. * @param string $path Example: "child/grand@attrName=attrValue/subGrand" (to make it faster without regex)
  89. * @return \Magento\Framework\Simplexml\Element
  90. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  91. */
  92. public function descend($path)
  93. {
  94. # $node = $this->xpath($path);
  95. # return $node[0];
  96. if (is_array($path)) {
  97. $pathArr = $path;
  98. } else {
  99. // Simple exploding by / does not suffice,
  100. // as an attribute value may contain a / inside
  101. // Note that there are three matches for different kinds of attribute values specification
  102. if (strpos($path, "@") === false) {
  103. $pathArr = explode('/', $path);
  104. } else {
  105. $regex = "#([^@/\\\"]+(?:@[^=/]+=(?:\\\"[^\\\"]*\\\"|[^/]*))?)/?#";
  106. $pathArr = $pathMatches = [];
  107. if (preg_match_all($regex, $path, $pathMatches)) {
  108. $pathArr = $pathMatches[1];
  109. }
  110. }
  111. }
  112. $desc = $this;
  113. foreach ($pathArr as $nodeName) {
  114. if (strpos($nodeName, '@') !== false) {
  115. $a = explode('@', $nodeName);
  116. $b = explode('=', $a[1]);
  117. $nodeName = $a[0];
  118. $attributeName = $b[0];
  119. $attributeValue = $b[1];
  120. //
  121. // Does a very simplistic trimming of attribute value.
  122. //
  123. $attributeValue = trim($attributeValue, '"');
  124. $found = false;
  125. foreach ($desc->{$nodeName} as $subdesc) {
  126. if ((string)$subdesc[$attributeName] === $attributeValue) {
  127. $found = true;
  128. $desc = $subdesc;
  129. break;
  130. }
  131. }
  132. if (!$found) {
  133. $desc = false;
  134. }
  135. } else {
  136. $desc = $desc->{$nodeName};
  137. }
  138. if (!$desc) {
  139. return false;
  140. }
  141. }
  142. return $desc;
  143. }
  144. /**
  145. * Create attribute if it does not exists and set value to it
  146. *
  147. * @param string $name
  148. * @param string $value
  149. * @return void
  150. */
  151. public function setAttribute($name, $value)
  152. {
  153. if (!isset($this->attributes()[$name])) {
  154. $this->addAttribute($name, $value);
  155. }
  156. $this->attributes()[$name] = $value;
  157. }
  158. /**
  159. * Returns the node and children as an array
  160. *
  161. * @return array|string
  162. */
  163. public function asArray()
  164. {
  165. return $this->_asArray();
  166. }
  167. /**
  168. * asArray() analog, but without attributes
  169. * @return array|string
  170. */
  171. public function asCanonicalArray()
  172. {
  173. return $this->_asArray(true);
  174. }
  175. /**
  176. * Returns the node and children as an array
  177. *
  178. * @param bool $isCanonical - whether to ignore attributes
  179. * @return array|string
  180. */
  181. protected function _asArray($isCanonical = false)
  182. {
  183. $result = [];
  184. if (!$isCanonical) {
  185. // add attributes
  186. foreach ($this->attributes() as $attributeName => $attribute) {
  187. if ($attribute) {
  188. $result['@'][$attributeName] = (string)$attribute;
  189. }
  190. }
  191. }
  192. // add children values
  193. if ($this->hasChildren()) {
  194. foreach ($this->children() as $childName => $child) {
  195. $result[$childName] = $child->_asArray($isCanonical);
  196. }
  197. } else {
  198. if (empty($result)) {
  199. // return as string, if nothing was found
  200. $result = (string)$this;
  201. } else {
  202. // value has zero key element
  203. $result[0] = (string)$this;
  204. }
  205. }
  206. return $result;
  207. }
  208. /**
  209. * Makes nicely formatted XML from the node
  210. *
  211. * @param string $filename
  212. * @param int|boolean $level if false
  213. * @return string
  214. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  215. * @SuppressWarnings(PHPMD.NPathComplexity)
  216. */
  217. public function asNiceXml($filename = '', $level = 0)
  218. {
  219. if (is_numeric($level)) {
  220. $pad = str_pad('', $level * 3, ' ', STR_PAD_LEFT);
  221. $nl = "\n";
  222. } else {
  223. $pad = '';
  224. $nl = '';
  225. }
  226. $out = $pad . '<' . $this->getName();
  227. $attributes = $this->attributes();
  228. if ($attributes) {
  229. foreach ($attributes as $key => $value) {
  230. $out .= ' ' . $key . '="' . str_replace('"', '\"', (string)$value) . '"';
  231. }
  232. }
  233. $attributes = $this->attributes('xsi', true);
  234. if ($attributes) {
  235. foreach ($attributes as $key => $value) {
  236. $out .= ' xsi:' . $key . '="' . str_replace('"', '\"', (string)$value) . '"';
  237. }
  238. }
  239. if ($this->hasChildren()) {
  240. $out .= '>';
  241. $value = trim((string)$this);
  242. if (strlen($value)) {
  243. $out .= $this->xmlentities($value);
  244. }
  245. $out .= $nl;
  246. foreach ($this->children() as $child) {
  247. $out .= $child->asNiceXml('', is_numeric($level) ? $level + 1 : true);
  248. }
  249. $out .= $pad . '</' . $this->getName() . '>' . $nl;
  250. } else {
  251. $value = (string)$this;
  252. if (strlen($value)) {
  253. $out .= '>' . $this->xmlentities($value) . '</' . $this->getName() . '>' . $nl;
  254. } else {
  255. $out .= '/>' . $nl;
  256. }
  257. }
  258. if ((0 === $level || false === $level) && !empty($filename)) {
  259. file_put_contents($filename, $out);
  260. }
  261. return $out;
  262. }
  263. /**
  264. * Enter description here...
  265. *
  266. * @param int $level
  267. * @return string
  268. */
  269. public function innerXml($level = 0)
  270. {
  271. $out = '';
  272. foreach ($this->children() as $child) {
  273. $out .= $child->asNiceXml($level);
  274. }
  275. return $out;
  276. }
  277. /**
  278. * Converts meaningful xml characters to xml entities
  279. *
  280. * @param string $value
  281. * @return string
  282. */
  283. public function xmlentities($value = null)
  284. {
  285. if (is_null($value)) {
  286. $value = $this;
  287. }
  288. $value = (string)$value;
  289. $value = str_replace(
  290. ['&', '"', "'", '<', '>'],
  291. ['&amp;', '&quot;', '&apos;', '&lt;', '&gt;'],
  292. $value
  293. );
  294. return $value;
  295. }
  296. /**
  297. * Appends $source to current node
  298. *
  299. * @param \Magento\Framework\Simplexml\Element $source
  300. * @return $this
  301. */
  302. public function appendChild($source)
  303. {
  304. if ($source->count()) {
  305. $child = $this->addChild($source->getName());
  306. } else {
  307. $child = $this->addChild($source->getName(), $this->xmlentities($source));
  308. }
  309. $child->setParent($this);
  310. $attributes = $source->attributes();
  311. foreach ($attributes as $key => $value) {
  312. $child->addAttribute($key, $this->xmlentities($value));
  313. }
  314. foreach ($source->children() as $sourceChild) {
  315. $child->appendChild($sourceChild);
  316. }
  317. return $this;
  318. }
  319. /**
  320. * Extends current node with xml from $source
  321. *
  322. * If $overwrite is false will merge only missing nodes
  323. * Otherwise will overwrite existing nodes
  324. *
  325. * @param \Magento\Framework\Simplexml\Element $source
  326. * @param boolean $overwrite
  327. * @return $this
  328. */
  329. public function extend($source, $overwrite = false)
  330. {
  331. if (!$source instanceof \Magento\Framework\Simplexml\Element) {
  332. return $this;
  333. }
  334. foreach ($source->children() as $child) {
  335. $this->extendChild($child, $overwrite);
  336. }
  337. return $this;
  338. }
  339. /**
  340. * Extends one node
  341. *
  342. * @param \Magento\Framework\Simplexml\Element $source
  343. * @param boolean $overwrite
  344. * @return $this
  345. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  346. * @SuppressWarnings(PHPMD.UnusedLocalVariable)
  347. */
  348. public function extendChild($source, $overwrite = false)
  349. {
  350. // this will be our new target node
  351. $targetChild = null;
  352. // name of the source node
  353. $sourceName = $source->getName();
  354. // here we have children of our source node
  355. $sourceChildren = $source->children();
  356. if (!$source->hasChildren()) {
  357. // handle string node
  358. if (isset($this->{$sourceName})) {
  359. // if target already has children return without regard
  360. if ($this->{$sourceName}->hasChildren()) {
  361. return $this;
  362. }
  363. if ($overwrite) {
  364. unset($this->{$sourceName});
  365. } else {
  366. return $this;
  367. }
  368. }
  369. $targetChild = $this->addChild($sourceName, $source->xmlentities());
  370. $targetChild->setParent($this);
  371. foreach ($source->attributes() as $key => $value) {
  372. $targetChild->addAttribute($key, $this->xmlentities($value));
  373. }
  374. return $this;
  375. }
  376. if (isset($this->{$sourceName})) {
  377. $targetChild = $this->{$sourceName};
  378. }
  379. if (is_null($targetChild)) {
  380. // if child target is not found create new and descend
  381. $targetChild = $this->addChild($sourceName);
  382. $targetChild->setParent($this);
  383. foreach ($source->attributes() as $key => $value) {
  384. $targetChild->addAttribute($key, $this->xmlentities($value));
  385. }
  386. }
  387. // finally add our source node children to resulting new target node
  388. foreach ($sourceChildren as $childKey => $childNode) {
  389. $targetChild->extendChild($childNode, $overwrite);
  390. }
  391. return $this;
  392. }
  393. /**
  394. * Set node
  395. *
  396. * @param string $path
  397. * @param string $value
  398. * @param bool $overwrite
  399. * @return $this
  400. */
  401. public function setNode($path, $value, $overwrite = true)
  402. {
  403. $arr1 = explode('/', $path);
  404. $arr = [];
  405. foreach ($arr1 as $v) {
  406. if (!empty($v)) {
  407. $arr[] = $v;
  408. }
  409. }
  410. $last = sizeof($arr) - 1;
  411. $node = $this;
  412. foreach ($arr as $i => $nodeName) {
  413. if ($last === $i) {
  414. if (!isset($node->{$nodeName}) || $overwrite) {
  415. $node->{$nodeName} = $value;
  416. }
  417. } else {
  418. if (!isset($node->{$nodeName})) {
  419. $node = $node->addChild($nodeName);
  420. } else {
  421. $node = $node->{$nodeName};
  422. }
  423. }
  424. }
  425. return $this;
  426. }
  427. /**
  428. * Unset self from the XML-node tree
  429. *
  430. * Note: trying to refer this object as a variable after "unsetting" like this will result in E_WARNING
  431. * @return void
  432. */
  433. public function unsetSelf()
  434. {
  435. $uniqueId = uniqid();
  436. $this['_unique_id'] = $uniqueId;
  437. $children = $this->getParent()->xpath('*');
  438. for ($i = count($children); $i > 0; $i--) {
  439. if ($children[$i - 1][0]['_unique_id'] == $uniqueId) {
  440. unset($children[$i - 1][0]);
  441. return;
  442. }
  443. }
  444. }
  445. }