PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/internal/Magento/Framework/DataObject.php

https://gitlab.com/crazybutterfly815/magento2
PHP | 541 lines | 388 code | 21 blank | 132 comment | 22 complexity | a333f0fde8d50d2854abc6ba173fd357 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework;
  7. /**
  8. * Universal data container with array access implementation
  9. *
  10. * @author Magento Core Team <core@magentocommerce.com>
  11. * @SuppressWarnings(PHPMD.NumberOfChildren)
  12. */
  13. class DataObject implements \ArrayAccess
  14. {
  15. /**
  16. * Object attributes
  17. *
  18. * @var array
  19. */
  20. protected $_data = [];
  21. /**
  22. * Setter/Getter underscore transformation cache
  23. *
  24. * @var array
  25. */
  26. protected static $_underscoreCache = [];
  27. /**
  28. * Constructor
  29. *
  30. * By default is looking for first argument as array and assigns it as object attributes
  31. * This behavior may change in child classes
  32. *
  33. * @param array $data
  34. */
  35. public function __construct(array $data = [])
  36. {
  37. $this->_data = $data;
  38. }
  39. /**
  40. * Add data to the object.
  41. *
  42. * Retains previous data in the object.
  43. *
  44. * @param array $arr
  45. * @return $this
  46. */
  47. public function addData(array $arr)
  48. {
  49. foreach ($arr as $index => $value) {
  50. $this->setData($index, $value);
  51. }
  52. return $this;
  53. }
  54. /**
  55. * Overwrite data in the object.
  56. *
  57. * The $key parameter can be string or array.
  58. * If $key is string, the attribute value will be overwritten by $value
  59. *
  60. * If $key is an array, it will overwrite all the data in the object.
  61. *
  62. * @param string|array $key
  63. * @param mixed $value
  64. * @return $this
  65. */
  66. public function setData($key, $value = null)
  67. {
  68. if ($key === (array)$key) {
  69. $this->_data = $key;
  70. } else {
  71. $this->_data[$key] = $value;
  72. }
  73. return $this;
  74. }
  75. /**
  76. * Unset data from the object.
  77. *
  78. * @param null|string|array $key
  79. * @return $this
  80. */
  81. public function unsetData($key = null)
  82. {
  83. if ($key === null) {
  84. $this->setData([]);
  85. } elseif (is_string($key)) {
  86. if (isset($this->_data[$key]) || array_key_exists($key, $this->_data)) {
  87. unset($this->_data[$key]);
  88. }
  89. } elseif ($key === (array)$key) {
  90. foreach ($key as $element) {
  91. $this->unsetData($element);
  92. }
  93. }
  94. return $this;
  95. }
  96. /**
  97. * Object data getter
  98. *
  99. * If $key is not defined will return all the data as an array.
  100. * Otherwise it will return value of the element specified by $key.
  101. * It is possible to use keys like a/b/c for access nested array data
  102. *
  103. * If $index is specified it will assume that attribute data is an array
  104. * and retrieve corresponding member. If data is the string - it will be explode
  105. * by new line character and converted to array.
  106. *
  107. * @param string $key
  108. * @param string|int $index
  109. * @return mixed
  110. */
  111. public function getData($key = '', $index = null)
  112. {
  113. if ('' === $key) {
  114. return $this->_data;
  115. }
  116. /* process a/b/c key as ['a']['b']['c'] */
  117. if (strpos($key, '/')) {
  118. $data = $this->getDataByPath($key);
  119. } else {
  120. $data = $this->_getData($key);
  121. }
  122. if ($index !== null) {
  123. if ($data === (array)$data) {
  124. $data = isset($data[$index]) ? $data[$index] : null;
  125. } elseif (is_string($data)) {
  126. $data = explode(PHP_EOL, $data);
  127. $data = isset($data[$index]) ? $data[$index] : null;
  128. } elseif ($data instanceof \Magento\Framework\DataObject) {
  129. $data = $data->getData($index);
  130. } else {
  131. $data = null;
  132. }
  133. }
  134. return $data;
  135. }
  136. /**
  137. * Get object data by path
  138. *
  139. * Method consider the path as chain of keys: a/b/c => ['a']['b']['c']
  140. *
  141. * @param string $path
  142. * @return mixed
  143. */
  144. public function getDataByPath($path)
  145. {
  146. $keys = explode('/', $path);
  147. $data = $this->_data;
  148. foreach ($keys as $key) {
  149. if ((array)$data === $data && isset($data[$key])) {
  150. $data = $data[$key];
  151. } elseif ($data instanceof \Magento\Framework\DataObject) {
  152. $data = $data->getDataByKey($key);
  153. } else {
  154. return null;
  155. }
  156. }
  157. return $data;
  158. }
  159. /**
  160. * Get object data by particular key
  161. *
  162. * @param string $key
  163. * @return mixed
  164. */
  165. public function getDataByKey($key)
  166. {
  167. return $this->_getData($key);
  168. }
  169. /**
  170. * Get value from _data array without parse key
  171. *
  172. * @param string $key
  173. * @return mixed
  174. */
  175. protected function _getData($key)
  176. {
  177. if (isset($this->_data[$key])) {
  178. return $this->_data[$key];
  179. }
  180. return null;
  181. }
  182. /**
  183. * Set object data with calling setter method
  184. *
  185. * @param string $key
  186. * @param mixed $args
  187. * @return $this
  188. */
  189. public function setDataUsingMethod($key, $args = [])
  190. {
  191. $method = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key)));
  192. $this->{$method}($args);
  193. return $this;
  194. }
  195. /**
  196. * Get object data by key with calling getter method
  197. *
  198. * @param string $key
  199. * @param mixed $args
  200. * @return mixed
  201. */
  202. public function getDataUsingMethod($key, $args = null)
  203. {
  204. $method = 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key)));
  205. return $this->{$method}($args);
  206. }
  207. /**
  208. * If $key is empty, checks whether there's any data in the object
  209. * Otherwise checks if the specified attribute is set.
  210. *
  211. * @param string $key
  212. * @return bool
  213. */
  214. public function hasData($key = '')
  215. {
  216. if (empty($key) || !is_string($key)) {
  217. return !empty($this->_data);
  218. }
  219. return array_key_exists($key, $this->_data);
  220. }
  221. /**
  222. * Convert array of object data with to array with keys requested in $keys array
  223. *
  224. * @param array $keys array of required keys
  225. * @return array
  226. */
  227. public function toArray(array $keys = [])
  228. {
  229. if (empty($keys)) {
  230. return $this->_data;
  231. }
  232. $result = [];
  233. foreach ($keys as $key) {
  234. if (isset($this->_data[$key])) {
  235. $result[$key] = $this->_data[$key];
  236. } else {
  237. $result[$key] = null;
  238. }
  239. }
  240. return $result;
  241. }
  242. /**
  243. * The "__" style wrapper for toArray method
  244. *
  245. * @param array $keys
  246. * @return array
  247. */
  248. public function convertToArray(array $keys = [])
  249. {
  250. return $this->toArray($keys);
  251. }
  252. /**
  253. * Convert object data into XML string
  254. *
  255. * @param array $keys array of keys that must be represented
  256. * @param string $rootName root node name
  257. * @param bool $addOpenTag flag that allow to add initial xml node
  258. * @param bool $addCdata flag that require wrap all values in CDATA
  259. * @return string
  260. */
  261. public function toXml(array $keys = [], $rootName = 'item', $addOpenTag = false, $addCdata = true)
  262. {
  263. $xml = '';
  264. $data = $this->toArray($keys);
  265. foreach ($data as $fieldName => $fieldValue) {
  266. if ($addCdata === true) {
  267. $fieldValue = "<![CDATA[{$fieldValue}]]>";
  268. } else {
  269. $fieldValue = str_replace(
  270. ['&', '"', "'", '<', '>'],
  271. ['&amp;', '&quot;', '&apos;', '&lt;', '&gt;'],
  272. $fieldValue
  273. );
  274. }
  275. $xml .= "<{$fieldName}>{$fieldValue}</{$fieldName}>\n";
  276. }
  277. if ($rootName) {
  278. $xml = "<{$rootName}>\n{$xml}</{$rootName}>\n";
  279. }
  280. if ($addOpenTag) {
  281. $xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $xml;
  282. }
  283. return $xml;
  284. }
  285. /**
  286. * The "__" style wrapper for toXml method
  287. *
  288. * @param array $arrAttributes array of keys that must be represented
  289. * @param string $rootName root node name
  290. * @param bool $addOpenTag flag that allow to add initial xml node
  291. * @param bool $addCdata flag that require wrap all values in CDATA
  292. * @return string
  293. */
  294. public function convertToXml(
  295. array $arrAttributes = [],
  296. $rootName = 'item',
  297. $addOpenTag = false,
  298. $addCdata = true
  299. ) {
  300. return $this->toXml($arrAttributes, $rootName, $addOpenTag, $addCdata);
  301. }
  302. /**
  303. * Convert object data to JSON
  304. *
  305. * @param array $keys array of required keys
  306. * @return string
  307. */
  308. public function toJson(array $keys = [])
  309. {
  310. $data = $this->toArray($keys);
  311. return \Zend_Json::encode($data);
  312. }
  313. /**
  314. * The "__" style wrapper for toJson
  315. *
  316. * @param array $keys
  317. * @return string
  318. */
  319. public function convertToJson(array $keys = [])
  320. {
  321. return $this->toJson($keys);
  322. }
  323. /**
  324. * Convert object data into string with predefined format
  325. *
  326. * Will use $format as an template and substitute {{key}} for attributes
  327. *
  328. * @param string $format
  329. * @return string
  330. */
  331. public function toString($format = '')
  332. {
  333. if (empty($format)) {
  334. $result = implode(', ', $this->getData());
  335. } else {
  336. preg_match_all('/\{\{([a-z0-9_]+)\}\}/is', $format, $matches);
  337. foreach ($matches[1] as $var) {
  338. $format = str_replace('{{' . $var . '}}', $this->getData($var), $format);
  339. }
  340. $result = $format;
  341. }
  342. return $result;
  343. }
  344. /**
  345. * Set/Get attribute wrapper
  346. *
  347. * @param string $method
  348. * @param array $args
  349. * @return mixed
  350. * @throws \Magento\Framework\Exception\LocalizedException
  351. */
  352. public function __call($method, $args)
  353. {
  354. switch (substr($method, 0, 3)) {
  355. case 'get':
  356. $key = $this->_underscore(substr($method, 3));
  357. $index = isset($args[0]) ? $args[0] : null;
  358. return $this->getData($key, $index);
  359. case 'set':
  360. $key = $this->_underscore(substr($method, 3));
  361. $value = isset($args[0]) ? $args[0] : null;
  362. return $this->setData($key, $value);
  363. case 'uns':
  364. $key = $this->_underscore(substr($method, 3));
  365. return $this->unsetData($key);
  366. case 'has':
  367. $key = $this->_underscore(substr($method, 3));
  368. return isset($this->_data[$key]);
  369. }
  370. throw new \Magento\Framework\Exception\LocalizedException(
  371. new \Magento\Framework\Phrase('Invalid method %1::%2', [get_class($this), $method])
  372. );
  373. }
  374. /**
  375. * Checks whether the object is empty
  376. *
  377. * @return bool
  378. */
  379. public function isEmpty()
  380. {
  381. if (empty($this->_data)) {
  382. return true;
  383. }
  384. return false;
  385. }
  386. /**
  387. * Converts field names for setters and getters
  388. *
  389. * $this->setMyField($value) === $this->setData('my_field', $value)
  390. * Uses cache to eliminate unnecessary preg_replace
  391. *
  392. * @param string $name
  393. * @return string
  394. */
  395. protected function _underscore($name)
  396. {
  397. if (isset(self::$_underscoreCache[$name])) {
  398. return self::$_underscoreCache[$name];
  399. }
  400. $result = strtolower(trim(preg_replace('/([A-Z]|[0-9]+)/', "_$1", $name), '_'));
  401. self::$_underscoreCache[$name] = $result;
  402. return $result;
  403. }
  404. /**
  405. * Convert object data into string with defined keys and values.
  406. *
  407. * Example: key1="value1" key2="value2" ...
  408. *
  409. * @param array $keys array of accepted keys
  410. * @param string $valueSeparator separator between key and value
  411. * @param string $fieldSeparator separator between key/value pairs
  412. * @param string $quote quoting sign
  413. * @return string
  414. */
  415. public function serialize($keys = [], $valueSeparator = '=', $fieldSeparator = ' ', $quote = '"')
  416. {
  417. $data = [];
  418. if (empty($keys)) {
  419. $keys = array_keys($this->_data);
  420. }
  421. foreach ($this->_data as $key => $value) {
  422. if (in_array($key, $keys)) {
  423. $data[] = $key . $valueSeparator . $quote . $value . $quote;
  424. }
  425. }
  426. $res = implode($fieldSeparator, $data);
  427. return $res;
  428. }
  429. /**
  430. * Present object data as string in debug mode
  431. *
  432. * @param mixed $data
  433. * @param array &$objects
  434. * @return array
  435. */
  436. public function debug($data = null, &$objects = [])
  437. {
  438. if ($data === null) {
  439. $hash = spl_object_hash($this);
  440. if (!empty($objects[$hash])) {
  441. return '*** RECURSION ***';
  442. }
  443. $objects[$hash] = true;
  444. $data = $this->getData();
  445. }
  446. $debug = [];
  447. foreach ($data as $key => $value) {
  448. if (is_scalar($value)) {
  449. $debug[$key] = $value;
  450. } elseif (is_array($value)) {
  451. $debug[$key] = $this->debug($value, $objects);
  452. } elseif ($value instanceof \Magento\Framework\DataObject) {
  453. $debug[$key . ' (' . get_class($value) . ')'] = $value->debug(null, $objects);
  454. }
  455. }
  456. return $debug;
  457. }
  458. /**
  459. * Implementation of \ArrayAccess::offsetSet()
  460. *
  461. * @param string $offset
  462. * @param mixed $value
  463. * @return void
  464. * @link http://www.php.net/manual/en/arrayaccess.offsetset.php
  465. */
  466. public function offsetSet($offset, $value)
  467. {
  468. $this->_data[$offset] = $value;
  469. }
  470. /**
  471. * Implementation of \ArrayAccess::offsetExists()
  472. *
  473. * @param string $offset
  474. * @return bool
  475. * @link http://www.php.net/manual/en/arrayaccess.offsetexists.php
  476. */
  477. public function offsetExists($offset)
  478. {
  479. return isset($this->_data[$offset]) || array_key_exists($offset, $this->_data);
  480. }
  481. /**
  482. * Implementation of \ArrayAccess::offsetUnset()
  483. *
  484. * @param string $offset
  485. * @return void
  486. * @link http://www.php.net/manual/en/arrayaccess.offsetunset.php
  487. */
  488. public function offsetUnset($offset)
  489. {
  490. unset($this->_data[$offset]);
  491. }
  492. /**
  493. * Implementation of \ArrayAccess::offsetGet()
  494. *
  495. * @param string $offset
  496. * @return mixed
  497. * @link http://www.php.net/manual/en/arrayaccess.offsetget.php
  498. */
  499. public function offsetGet($offset)
  500. {
  501. if (isset($this->_data[$offset])) {
  502. return $this->_data[$offset];
  503. }
  504. return null;
  505. }
  506. }