Dot.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. <?php
  2. /**
  3. * Dot - PHP dot notation access to arrays
  4. *
  5. * @author Riku Särkinen <riku@adbar.io>
  6. * @link https://github.com/adbario/php-dot-notation
  7. * @license https://github.com/adbario/php-dot-notation/blob/2.x/LICENSE.md (MIT License)
  8. */
  9. namespace Adbar;
  10. use Countable;
  11. use ArrayAccess;
  12. use ArrayIterator;
  13. use JsonSerializable;
  14. use IteratorAggregate;
  15. /**
  16. * Dot
  17. *
  18. * This class provides a dot notation access and helper functions for
  19. * working with arrays of data. Inspired by Laravel Collection.
  20. */
  21. class Dot implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
  22. {
  23. /**
  24. * The stored items
  25. *
  26. * @var array
  27. */
  28. protected $items = [];
  29. /**
  30. * Create a new Dot instance
  31. *
  32. * @param mixed $items
  33. */
  34. public function __construct($items = [])
  35. {
  36. $this->items = $this->getArrayItems($items);
  37. }
  38. /**
  39. * Set a given key / value pair or pairs
  40. * if the key doesn't exist already
  41. *
  42. * @param array|int|string $keys
  43. * @param mixed $value
  44. */
  45. public function add($keys, $value = null)
  46. {
  47. if (is_array($keys)) {
  48. foreach ($keys as $key => $value) {
  49. $this->add($key, $value);
  50. }
  51. } elseif (is_null($this->get($keys))) {
  52. $this->set($keys, $value);
  53. }
  54. }
  55. /**
  56. * Return all the stored items
  57. *
  58. * @return array
  59. */
  60. public function all()
  61. {
  62. return $this->items;
  63. }
  64. /**
  65. * Delete the contents of a given key or keys
  66. *
  67. * @param array|int|string|null $keys
  68. */
  69. public function clear($keys = null)
  70. {
  71. if (is_null($keys)) {
  72. $this->items = [];
  73. return;
  74. }
  75. $keys = (array) $keys;
  76. foreach ($keys as $key) {
  77. $this->set($key, []);
  78. }
  79. }
  80. /**
  81. * Delete the given key or keys
  82. *
  83. * @param array|int|string $keys
  84. */
  85. public function delete($keys)
  86. {
  87. $keys = (array) $keys;
  88. foreach ($keys as $key) {
  89. if ($this->exists($this->items, $key)) {
  90. unset($this->items[$key]);
  91. continue;
  92. }
  93. $items = &$this->items;
  94. $segments = explode('.', $key);
  95. $lastSegment = array_pop($segments);
  96. foreach ($segments as $segment) {
  97. if (!isset($items[$segment]) || !is_array($items[$segment])) {
  98. continue 2;
  99. }
  100. $items = &$items[$segment];
  101. }
  102. unset($items[$lastSegment]);
  103. }
  104. }
  105. /**
  106. * Checks if the given key exists in the provided array.
  107. *
  108. * @param array $array Array to validate
  109. * @param int|string $key The key to look for
  110. *
  111. * @return bool
  112. */
  113. protected function exists($array, $key)
  114. {
  115. return array_key_exists($key, $array);
  116. }
  117. /**
  118. * Flatten an array with the given character as a key delimiter
  119. *
  120. * @param string $delimiter
  121. * @param array|null $items
  122. * @param string $prepend
  123. * @return array
  124. */
  125. public function flatten($delimiter = '.', $items = null, $prepend = '')
  126. {
  127. $flatten = [];
  128. if (is_null($items)) {
  129. $items = $this->items;
  130. }
  131. foreach ($items as $key => $value) {
  132. if (is_array($value) && !empty($value)) {
  133. $flatten = array_merge(
  134. $flatten,
  135. $this->flatten($delimiter, $value, $prepend.$key.$delimiter)
  136. );
  137. } else {
  138. $flatten[$prepend.$key] = $value;
  139. }
  140. }
  141. return $flatten;
  142. }
  143. /**
  144. * Return the value of a given key
  145. *
  146. * @param int|string|null $key
  147. * @param mixed $default
  148. * @return mixed
  149. */
  150. public function get($key = null, $default = null)
  151. {
  152. if (is_null($key)) {
  153. return $this->items;
  154. }
  155. if ($this->exists($this->items, $key)) {
  156. return $this->items[$key];
  157. }
  158. if (strpos($key, '.') === false) {
  159. return $default;
  160. }
  161. $items = $this->items;
  162. foreach (explode('.', $key) as $segment) {
  163. if (!is_array($items) || !$this->exists($items, $segment)) {
  164. return $default;
  165. }
  166. $items = &$items[$segment];
  167. }
  168. return $items;
  169. }
  170. /**
  171. * Return the given items as an array
  172. *
  173. * @param mixed $items
  174. * @return array
  175. */
  176. protected function getArrayItems($items)
  177. {
  178. if (is_array($items)) {
  179. return $items;
  180. } elseif ($items instanceof self) {
  181. return $items->all();
  182. }
  183. return (array) $items;
  184. }
  185. /**
  186. * Check if a given key or keys exists
  187. *
  188. * @param array|int|string $keys
  189. * @return bool
  190. */
  191. public function has($keys)
  192. {
  193. $keys = (array) $keys;
  194. if (!$this->items || $keys === []) {
  195. return false;
  196. }
  197. foreach ($keys as $key) {
  198. $items = $this->items;
  199. if ($this->exists($items, $key)) {
  200. continue;
  201. }
  202. foreach (explode('.', $key) as $segment) {
  203. if (!is_array($items) || !$this->exists($items, $segment)) {
  204. return false;
  205. }
  206. $items = $items[$segment];
  207. }
  208. }
  209. return true;
  210. }
  211. /**
  212. * Check if a given key or keys are empty
  213. *
  214. * @param array|int|string|null $keys
  215. * @return bool
  216. */
  217. public function isEmpty($keys = null)
  218. {
  219. if (is_null($keys)) {
  220. return empty($this->items);
  221. }
  222. $keys = (array) $keys;
  223. foreach ($keys as $key) {
  224. if (!empty($this->get($key))) {
  225. return false;
  226. }
  227. }
  228. return true;
  229. }
  230. /**
  231. * Merge a given array or a Dot object with the given key
  232. * or with the whole Dot object
  233. *
  234. * @param array|string|self $key
  235. * @param array $value
  236. */
  237. public function merge($key, $value = null)
  238. {
  239. if (is_array($key)) {
  240. $this->items = array_merge($this->items, $key);
  241. } elseif (is_string($key)) {
  242. $items = (array) $this->get($key);
  243. $value = array_merge($items, $this->getArrayItems($value));
  244. $this->set($key, $value);
  245. } elseif ($key instanceof self) {
  246. $this->items = array_merge($this->items, $key->all());
  247. }
  248. }
  249. /**
  250. * Return the value of a given key and
  251. * delete the key
  252. *
  253. * @param int|string|null $key
  254. * @param mixed $default
  255. * @return mixed
  256. */
  257. public function pull($key = null, $default = null)
  258. {
  259. if (is_null($key)) {
  260. $value = $this->all();
  261. $this->clear();
  262. return $value;
  263. }
  264. $value = $this->get($key, $default);
  265. $this->delete($key);
  266. return $value;
  267. }
  268. /**
  269. * Push a given value to the end of the array
  270. * in a given key
  271. *
  272. * @param mixed $key
  273. * @param mixed $value
  274. */
  275. public function push($key, $value = null)
  276. {
  277. if (is_null($value)) {
  278. $this->items[] = $key;
  279. return;
  280. }
  281. $items = $this->get($key);
  282. if (is_array($items) || is_null($items)) {
  283. $items[] = $value;
  284. $this->set($key, $items);
  285. }
  286. }
  287. /**
  288. * Set a given key / value pair or pairs
  289. *
  290. * @param array|int|string $keys
  291. * @param mixed $value
  292. */
  293. public function set($keys, $value = null)
  294. {
  295. if (is_array($keys)) {
  296. foreach ($keys as $key => $value) {
  297. $this->set($key, $value);
  298. }
  299. return;
  300. }
  301. $items = &$this->items;
  302. foreach (explode('.', $keys) as $key) {
  303. if (!isset($items[$key]) || !is_array($items[$key])) {
  304. $items[$key] = [];
  305. }
  306. $items = &$items[$key];
  307. }
  308. $items = $value;
  309. }
  310. /**
  311. * Replace all items with a given array
  312. *
  313. * @param mixed $items
  314. */
  315. public function setArray($items)
  316. {
  317. $this->items = $this->getArrayItems($items);
  318. }
  319. /**
  320. * Replace all items with a given array as a reference
  321. *
  322. * @param array $items
  323. */
  324. public function setReference(array &$items)
  325. {
  326. $this->items = &$items;
  327. }
  328. /**
  329. * Return the value of a given key or all the values as JSON
  330. *
  331. * @param mixed $key
  332. * @param int $options
  333. * @return string
  334. */
  335. public function toJson($key = null, $options = 0)
  336. {
  337. if (is_string($key)) {
  338. return json_encode($this->get($key), $options);
  339. }
  340. $options = $key === null ? 0 : $key;
  341. return json_encode($this->items, $options);
  342. }
  343. /*
  344. * --------------------------------------------------------------
  345. * ArrayAccess interface
  346. * --------------------------------------------------------------
  347. */
  348. /**
  349. * Check if a given key exists
  350. *
  351. * @param int|string $key
  352. * @return bool
  353. */
  354. public function offsetExists($key)
  355. {
  356. return $this->has($key);
  357. }
  358. /**
  359. * Return the value of a given key
  360. *
  361. * @param int|string $key
  362. * @return mixed
  363. */
  364. public function offsetGet($key)
  365. {
  366. return $this->get($key);
  367. }
  368. /**
  369. * Set a given value to the given key
  370. *
  371. * @param int|string|null $key
  372. * @param mixed $value
  373. */
  374. public function offsetSet($key, $value)
  375. {
  376. if (is_null($key)) {
  377. $this->items[] = $value;
  378. return;
  379. }
  380. $this->set($key, $value);
  381. }
  382. /**
  383. * Delete the given key
  384. *
  385. * @param int|string $key
  386. */
  387. public function offsetUnset($key)
  388. {
  389. $this->delete($key);
  390. }
  391. /*
  392. * --------------------------------------------------------------
  393. * Countable interface
  394. * --------------------------------------------------------------
  395. */
  396. /**
  397. * Return the number of items in a given key
  398. *
  399. * @param int|string|null $key
  400. * @return int
  401. */
  402. public function count($key = null)
  403. {
  404. return count($this->get($key));
  405. }
  406. /*
  407. * --------------------------------------------------------------
  408. * IteratorAggregate interface
  409. * --------------------------------------------------------------
  410. */
  411. /**
  412. * Get an iterator for the stored items
  413. *
  414. * @return \ArrayIterator
  415. */
  416. public function getIterator()
  417. {
  418. return new ArrayIterator($this->items);
  419. }
  420. /*
  421. * --------------------------------------------------------------
  422. * JsonSerializable interface
  423. * --------------------------------------------------------------
  424. */
  425. /**
  426. * Return items for JSON serialization
  427. *
  428. * @return array
  429. */
  430. public function jsonSerialize()
  431. {
  432. return $this->items;
  433. }
  434. }