AbstractIndexCollection.php 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. <?php
  2. /**
  3. * @package Grav\Framework\Collection
  4. *
  5. * @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Framework\Collection;
  9. use ArrayIterator;
  10. use Closure;
  11. /**
  12. * Abstract Index Collection.
  13. */
  14. abstract class AbstractIndexCollection implements CollectionInterface
  15. {
  16. /** @var array */
  17. private $entries;
  18. /**
  19. * Initializes a new IndexCollection.
  20. *
  21. * @param array $entries
  22. */
  23. public function __construct(array $entries = [])
  24. {
  25. $this->entries = $entries;
  26. }
  27. /**
  28. * {@inheritDoc}
  29. */
  30. public function toArray()
  31. {
  32. return $this->loadElements($this->entries);
  33. }
  34. /**
  35. * {@inheritDoc}
  36. */
  37. public function first()
  38. {
  39. $value = reset($this->entries);
  40. $key = key($this->entries);
  41. return $this->loadElement($key, $value);
  42. }
  43. /**
  44. * {@inheritDoc}
  45. */
  46. public function last()
  47. {
  48. $value = end($this->entries);
  49. $key = key($this->entries);
  50. return $this->loadElement($key, $value);
  51. }
  52. /**
  53. * {@inheritDoc}
  54. */
  55. public function key()
  56. {
  57. return key($this->entries);
  58. }
  59. /**
  60. * {@inheritDoc}
  61. */
  62. public function next()
  63. {
  64. $value = next($this->entries);
  65. $key = key($this->entries);
  66. return $this->loadElement($key, $value);
  67. }
  68. /**
  69. * {@inheritDoc}
  70. */
  71. public function current()
  72. {
  73. $value = current($this->entries);
  74. $key = key($this->entries);
  75. return $this->loadElement($key, $value);
  76. }
  77. /**
  78. * {@inheritDoc}
  79. */
  80. public function remove($key)
  81. {
  82. if (!array_key_exists($key, $this->entries)) {
  83. return null;
  84. }
  85. $value = $this->entries[$key];
  86. unset($this->entries[$key]);
  87. return $this->loadElement($key, $value);
  88. }
  89. /**
  90. * {@inheritDoc}
  91. */
  92. public function removeElement($element)
  93. {
  94. $key = $this->isAllowedElement($element) ? $element->getKey() : null;
  95. if (!$key || !isset($this->entries[$key])) {
  96. return false;
  97. }
  98. unset($this->entries[$key]);
  99. return true;
  100. }
  101. /**
  102. * Required by interface ArrayAccess.
  103. *
  104. * {@inheritDoc}
  105. */
  106. public function offsetExists($offset)
  107. {
  108. return $this->containsKey($offset);
  109. }
  110. /**
  111. * Required by interface ArrayAccess.
  112. *
  113. * {@inheritDoc}
  114. */
  115. public function offsetGet($offset)
  116. {
  117. return $this->get($offset);
  118. }
  119. /**
  120. * Required by interface ArrayAccess.
  121. *
  122. * {@inheritDoc}
  123. */
  124. public function offsetSet($offset, $value)
  125. {
  126. if (null === $offset) {
  127. $this->add($value);
  128. }
  129. $this->set($offset, $value);
  130. }
  131. /**
  132. * Required by interface ArrayAccess.
  133. *
  134. * {@inheritDoc}
  135. */
  136. public function offsetUnset($offset)
  137. {
  138. return $this->remove($offset);
  139. }
  140. /**
  141. * {@inheritDoc}
  142. */
  143. public function containsKey($key)
  144. {
  145. return isset($this->entries[$key]) || array_key_exists($key, $this->entries);
  146. }
  147. /**
  148. * {@inheritDoc}
  149. */
  150. public function contains($element)
  151. {
  152. $key = $this->isAllowedElement($element) ? $element->getKey() : null;
  153. return $key && isset($this->entries[$key]);
  154. }
  155. /**
  156. * {@inheritDoc}
  157. */
  158. public function exists(Closure $p)
  159. {
  160. return $this->loadCollection($this->entries)->exists($p);
  161. }
  162. /**
  163. * {@inheritDoc}
  164. */
  165. public function indexOf($element)
  166. {
  167. $key = $this->isAllowedElement($element) ? $element->getKey() : null;
  168. return $key && isset($this->entries[$key]) ? $key : null;
  169. }
  170. /**
  171. * {@inheritDoc}
  172. */
  173. public function get($key)
  174. {
  175. if (!isset($this->entries[$key])) {
  176. return null;
  177. }
  178. return $this->loadElement($key, $this->entries[$key]);
  179. }
  180. /**
  181. * {@inheritDoc}
  182. */
  183. public function getKeys()
  184. {
  185. return array_keys($this->entries);
  186. }
  187. /**
  188. * {@inheritDoc}
  189. */
  190. public function getValues()
  191. {
  192. return array_values($this->loadElements($this->entries));
  193. }
  194. /**
  195. * {@inheritDoc}
  196. */
  197. public function count()
  198. {
  199. return \count($this->entries);
  200. }
  201. /**
  202. * {@inheritDoc}
  203. */
  204. public function set($key, $value)
  205. {
  206. if (!$this->isAllowedElement($value)) {
  207. throw new \InvalidArgumentException('Invalid argument $value');
  208. }
  209. if ($key !== $value->getKey()) {
  210. $value->setKey($key);
  211. }
  212. $this->entries[$key] = $this->getElementMeta($value);
  213. }
  214. /**
  215. * {@inheritDoc}
  216. */
  217. public function add($element)
  218. {
  219. if (!$this->isAllowedElement($element)) {
  220. throw new \InvalidArgumentException('Invalid argument $element');
  221. }
  222. $this->entries[$element->getKey()] = $this->getElementMeta($element);
  223. return true;
  224. }
  225. /**
  226. * {@inheritDoc}
  227. */
  228. public function isEmpty()
  229. {
  230. return empty($this->entries);
  231. }
  232. /**
  233. * Required by interface IteratorAggregate.
  234. *
  235. * {@inheritDoc}
  236. */
  237. public function getIterator()
  238. {
  239. return new ArrayIterator($this->loadElements());
  240. }
  241. /**
  242. * {@inheritDoc}
  243. */
  244. public function map(Closure $func)
  245. {
  246. return $this->loadCollection($this->entries)->map($func);
  247. }
  248. /**
  249. * {@inheritDoc}
  250. */
  251. public function filter(Closure $p)
  252. {
  253. return $this->loadCollection($this->entries)->filter($p);
  254. }
  255. /**
  256. * {@inheritDoc}
  257. */
  258. public function forAll(Closure $p)
  259. {
  260. return $this->loadCollection($this->entries)->forAll($p);
  261. }
  262. /**
  263. * {@inheritDoc}
  264. */
  265. public function partition(Closure $p)
  266. {
  267. return $this->loadCollection($this->entries)->partition($p);
  268. }
  269. /**
  270. * Returns a string representation of this object.
  271. *
  272. * @return string
  273. */
  274. public function __toString()
  275. {
  276. return __CLASS__ . '@' . spl_object_hash($this);
  277. }
  278. /**
  279. * {@inheritDoc}
  280. */
  281. public function clear()
  282. {
  283. $this->entries = [];
  284. }
  285. /**
  286. * {@inheritDoc}
  287. */
  288. public function slice($offset, $length = null)
  289. {
  290. return $this->loadElements(\array_slice($this->entries, $offset, $length, true));
  291. }
  292. /**
  293. * @param int $start
  294. * @param int|null $limit
  295. * @return static
  296. */
  297. public function limit($start, $limit = null)
  298. {
  299. return $this->createFrom(\array_slice($this->entries, $start, $limit, true));
  300. }
  301. /**
  302. * Reverse the order of the items.
  303. *
  304. * @return static
  305. */
  306. public function reverse()
  307. {
  308. return $this->createFrom(array_reverse($this->entries));
  309. }
  310. /**
  311. * Shuffle items.
  312. *
  313. * @return static
  314. */
  315. public function shuffle()
  316. {
  317. $keys = $this->getKeys();
  318. shuffle($keys);
  319. return $this->createFrom(array_replace(array_flip($keys), $this->entries));
  320. }
  321. /**
  322. * Select items from collection.
  323. *
  324. * Collection is returned in the order of $keys given to the function.
  325. *
  326. * @param array $keys
  327. * @return static
  328. */
  329. public function select(array $keys)
  330. {
  331. $list = [];
  332. foreach ($keys as $key) {
  333. if (isset($this->entries[$key])) {
  334. $list[$key] = $this->entries[$key];
  335. }
  336. }
  337. return $this->createFrom($list);
  338. }
  339. /**
  340. * Un-select items from collection.
  341. *
  342. * @param array $keys
  343. * @return static
  344. */
  345. public function unselect(array $keys)
  346. {
  347. return $this->select(array_diff($this->getKeys(), $keys));
  348. }
  349. /**
  350. * Split collection into chunks.
  351. *
  352. * @param int $size Size of each chunk.
  353. * @return array
  354. */
  355. public function chunk($size)
  356. {
  357. return $this->loadCollection($this->entries)->chunk($size);
  358. }
  359. /**
  360. * @return string
  361. */
  362. public function serialize()
  363. {
  364. return serialize(['entries' => $this->entries]);
  365. }
  366. /**
  367. * @param string $serialized
  368. */
  369. public function unserialize($serialized)
  370. {
  371. $data = unserialize($serialized, ['allowed_classes' => false]);
  372. $this->entries = $data['entries'];
  373. }
  374. /**
  375. * Implements JsonSerializable interface.
  376. *
  377. * @return array
  378. */
  379. public function jsonSerialize()
  380. {
  381. return $this->loadCollection()->jsonSerialize();
  382. }
  383. /**
  384. * Creates a new instance from the specified elements.
  385. *
  386. * This method is provided for derived classes to specify how a new
  387. * instance should be created when constructor semantics have changed.
  388. *
  389. * @param array $entries Elements.
  390. *
  391. * @return static
  392. */
  393. protected function createFrom(array $entries)
  394. {
  395. return new static($entries);
  396. }
  397. /**
  398. * @return array
  399. */
  400. protected function getEntries() : array
  401. {
  402. return $this->entries;
  403. }
  404. /**
  405. * @param array $entries
  406. */
  407. protected function setEntries(array $entries) : void
  408. {
  409. $this->entries = $entries;
  410. }
  411. /**
  412. * @param string $key
  413. * @param mixed $value
  414. * @return mixed|null
  415. */
  416. abstract protected function loadElement($key, $value);
  417. /**
  418. * @param array|null $entries
  419. * @return array
  420. */
  421. abstract protected function loadElements(array $entries = null) : array;
  422. /**
  423. * @param array|null $entries
  424. * @return CollectionInterface
  425. */
  426. abstract protected function loadCollection(array $entries = null) : CollectionInterface;
  427. /**
  428. * @param mixed $value
  429. * @return bool
  430. */
  431. abstract protected function isAllowedElement($value) : bool;
  432. /**
  433. * @param mixed $element
  434. * @return mixed
  435. */
  436. abstract protected function getElementMeta($element);
  437. }