vendor/doctrine/collections/src/ArrayCollection.php line 52

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\Common\Collections;
  4. use ArrayIterator;
  5. use Closure;
  6. use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor;
  7. use ReturnTypeWillChange;
  8. use Stringable;
  9. use Traversable;
  10. use function array_all;
  11. use function array_any;
  12. use function array_filter;
  13. use function array_find;
  14. use function array_key_exists;
  15. use function array_keys;
  16. use function array_map;
  17. use function array_reduce;
  18. use function array_reverse;
  19. use function array_search;
  20. use function array_slice;
  21. use function array_values;
  22. use function count;
  23. use function current;
  24. use function end;
  25. use function in_array;
  26. use function key;
  27. use function next;
  28. use function reset;
  29. use function spl_object_hash;
  30. use function uasort;
  31. use const ARRAY_FILTER_USE_BOTH;
  32. /**
  33.  * An ArrayCollection is a Collection implementation that wraps a regular PHP array.
  34.  *
  35.  * Warning: Using (un-)serialize() on a collection is not a supported use-case
  36.  * and may break when we change the internals in the future. If you need to
  37.  * serialize a collection use {@link toArray()} and reconstruct the collection
  38.  * manually.
  39.  *
  40.  * @phpstan-template TKey of array-key
  41.  * @phpstan-template T
  42.  * @template-implements Collection<TKey,T>
  43.  * @template-implements Selectable<TKey,T>
  44.  * @phpstan-consistent-constructor
  45.  */
  46. class ArrayCollection implements CollectionSelectableStringable
  47. {
  48.     /**
  49.      * An array containing the entries of this collection.
  50.      *
  51.      * @phpstan-var array<TKey,T>
  52.      * @var mixed[]
  53.      */
  54.     private array $elements = [];
  55.     /**
  56.      * Initializes a new ArrayCollection.
  57.      *
  58.      * @phpstan-param array<TKey,T> $elements
  59.      */
  60.     public function __construct(array $elements = [])
  61.     {
  62.         $this->elements $elements;
  63.     }
  64.     /**
  65.      * {@inheritDoc}
  66.      */
  67.     public function toArray()
  68.     {
  69.         return $this->elements;
  70.     }
  71.     /**
  72.      * {@inheritDoc}
  73.      */
  74.     public function first()
  75.     {
  76.         return reset($this->elements);
  77.     }
  78.     /**
  79.      * Creates a new instance from the specified elements.
  80.      *
  81.      * This method is provided for derived classes to specify how a new
  82.      * instance should be created when constructor semantics have changed.
  83.      *
  84.      * @param array $elements Elements.
  85.      * @phpstan-param array<K,V> $elements
  86.      *
  87.      * @return static
  88.      * @phpstan-return static<K,V>
  89.      *
  90.      * @phpstan-template K of array-key
  91.      * @phpstan-template V
  92.      */
  93.     protected function createFrom(array $elements)
  94.     {
  95.         return new static($elements);
  96.     }
  97.     /**
  98.      * {@inheritDoc}
  99.      */
  100.     public function last()
  101.     {
  102.         return end($this->elements);
  103.     }
  104.     /**
  105.      * {@inheritDoc}
  106.      */
  107.     public function key()
  108.     {
  109.         return key($this->elements);
  110.     }
  111.     /**
  112.      * {@inheritDoc}
  113.      */
  114.     public function next()
  115.     {
  116.         return next($this->elements);
  117.     }
  118.     /**
  119.      * {@inheritDoc}
  120.      */
  121.     public function current()
  122.     {
  123.         return current($this->elements);
  124.     }
  125.     /**
  126.      * {@inheritDoc}
  127.      */
  128.     public function remove(string|int $key)
  129.     {
  130.         if (! isset($this->elements[$key]) && ! array_key_exists($key$this->elements)) {
  131.             return null;
  132.         }
  133.         $removed $this->elements[$key];
  134.         unset($this->elements[$key]);
  135.         return $removed;
  136.     }
  137.     /**
  138.      * {@inheritDoc}
  139.      */
  140.     public function removeElement(mixed $element)
  141.     {
  142.         $key array_search($element$this->elementstrue);
  143.         if ($key === false) {
  144.             return false;
  145.         }
  146.         unset($this->elements[$key]);
  147.         return true;
  148.     }
  149.     /**
  150.      * Required by interface ArrayAccess.
  151.      *
  152.      * @param TKey $offset
  153.      *
  154.      * @return bool
  155.      */
  156.     #[ReturnTypeWillChange]
  157.     public function offsetExists(mixed $offset)
  158.     {
  159.         return $this->containsKey($offset);
  160.     }
  161.     /**
  162.      * Required by interface ArrayAccess.
  163.      *
  164.      * @param TKey $offset
  165.      *
  166.      * @return T|null
  167.      */
  168.     #[ReturnTypeWillChange]
  169.     public function offsetGet(mixed $offset)
  170.     {
  171.         return $this->get($offset);
  172.     }
  173.     /**
  174.      * Required by interface ArrayAccess.
  175.      *
  176.      * @param TKey|null $offset
  177.      * @param T         $value
  178.      *
  179.      * @return void
  180.      */
  181.     #[ReturnTypeWillChange]
  182.     public function offsetSet(mixed $offsetmixed $value)
  183.     {
  184.         if ($offset === null) {
  185.             $this->add($value);
  186.             return;
  187.         }
  188.         $this->set($offset$value);
  189.     }
  190.     /**
  191.      * Required by interface ArrayAccess.
  192.      *
  193.      * @param TKey $offset
  194.      *
  195.      * @return void
  196.      */
  197.     #[ReturnTypeWillChange]
  198.     public function offsetUnset(mixed $offset)
  199.     {
  200.         $this->remove($offset);
  201.     }
  202.     /**
  203.      * {@inheritDoc}
  204.      */
  205.     public function containsKey(string|int $key)
  206.     {
  207.         return isset($this->elements[$key]) || array_key_exists($key$this->elements);
  208.     }
  209.     /**
  210.      * {@inheritDoc}
  211.      */
  212.     public function contains(mixed $element)
  213.     {
  214.         return in_array($element$this->elementstrue);
  215.     }
  216.     /**
  217.      * {@inheritDoc}
  218.      */
  219.     public function exists(Closure $p)
  220.     {
  221.         return array_any(
  222.             $this->elements,
  223.             static fn (mixed $elementmixed $key): bool => (bool) $p($key$element),
  224.         );
  225.     }
  226.     /**
  227.      * {@inheritDoc}
  228.      *
  229.      * @phpstan-param TMaybeContained $element
  230.      *
  231.      * @return int|string|false
  232.      * @phpstan-return (TMaybeContained is T ? TKey|false : false)
  233.      *
  234.      * @template TMaybeContained
  235.      */
  236.     public function indexOf($element)
  237.     {
  238.         return array_search($element$this->elementstrue);
  239.     }
  240.     /**
  241.      * {@inheritDoc}
  242.      */
  243.     public function get(string|int $key)
  244.     {
  245.         return $this->elements[$key] ?? null;
  246.     }
  247.     /**
  248.      * {@inheritDoc}
  249.      */
  250.     public function getKeys()
  251.     {
  252.         return array_keys($this->elements);
  253.     }
  254.     /**
  255.      * {@inheritDoc}
  256.      */
  257.     public function getValues()
  258.     {
  259.         return array_values($this->elements);
  260.     }
  261.     /**
  262.      * {@inheritDoc}
  263.      *
  264.      * @return int<0, max>
  265.      */
  266.     #[ReturnTypeWillChange]
  267.     public function count()
  268.     {
  269.         return count($this->elements);
  270.     }
  271.     /**
  272.      * {@inheritDoc}
  273.      */
  274.     public function set(string|int $keymixed $value)
  275.     {
  276.         $this->elements[$key] = $value;
  277.     }
  278.     /**
  279.      * {@inheritDoc}
  280.      *
  281.      * This breaks assumptions about the template type, but it would
  282.      * be a backwards-incompatible change to remove this method
  283.      */
  284.     public function add(mixed $element)
  285.     {
  286.         $this->elements[] = $element;
  287.     }
  288.     /**
  289.      * {@inheritDoc}
  290.      */
  291.     public function isEmpty()
  292.     {
  293.         return empty($this->elements);
  294.     }
  295.     /**
  296.      * {@inheritDoc}
  297.      *
  298.      * @return Traversable<int|string, mixed>
  299.      * @phpstan-return Traversable<TKey, T>
  300.      */
  301.     #[ReturnTypeWillChange]
  302.     public function getIterator()
  303.     {
  304.         return new ArrayIterator($this->elements);
  305.     }
  306.     /**
  307.      * {@inheritDoc}
  308.      *
  309.      * @phpstan-param Closure(T):U $func
  310.      *
  311.      * @return static
  312.      * @phpstan-return static<TKey, U>
  313.      *
  314.      * @phpstan-template U
  315.      */
  316.     public function map(Closure $func)
  317.     {
  318.         return $this->createFrom(array_map($func$this->elements));
  319.     }
  320.     /**
  321.      * {@inheritDoc}
  322.      */
  323.     public function reduce(Closure $func$initial null)
  324.     {
  325.         return array_reduce($this->elements$func$initial);
  326.     }
  327.     /**
  328.      * {@inheritDoc}
  329.      *
  330.      * @phpstan-param Closure(T, TKey):bool $p
  331.      *
  332.      * @return static
  333.      * @phpstan-return static<TKey,T>
  334.      */
  335.     public function filter(Closure $p)
  336.     {
  337.         return $this->createFrom(array_filter($this->elements$pARRAY_FILTER_USE_BOTH));
  338.     }
  339.     /**
  340.      * {@inheritDoc}
  341.      */
  342.     public function findFirst(Closure $p)
  343.     {
  344.         return array_find(
  345.             $this->elements,
  346.             static fn (mixed $elementmixed $key): bool => (bool) $p($key$element),
  347.         );
  348.     }
  349.     /**
  350.      * {@inheritDoc}
  351.      */
  352.     public function forAll(Closure $p)
  353.     {
  354.         return array_all(
  355.             $this->elements,
  356.             static fn (mixed $elementmixed $key): bool => (bool) $p($key$element),
  357.         );
  358.     }
  359.     /**
  360.      * {@inheritDoc}
  361.      */
  362.     public function partition(Closure $p)
  363.     {
  364.         $matches $noMatches = [];
  365.         foreach ($this->elements as $key => $element) {
  366.             if ($p($key$element)) {
  367.                 $matches[$key] = $element;
  368.             } else {
  369.                 $noMatches[$key] = $element;
  370.             }
  371.         }
  372.         return [$this->createFrom($matches), $this->createFrom($noMatches)];
  373.     }
  374.     /**
  375.      * Returns a string representation of this object.
  376.      * {@inheritDoc}
  377.      *
  378.      * @return string
  379.      */
  380.     #[ReturnTypeWillChange]
  381.     public function __toString()
  382.     {
  383.         return self::class . '@' spl_object_hash($this);
  384.     }
  385.     /**
  386.      * {@inheritDoc}
  387.      */
  388.     public function clear()
  389.     {
  390.         $this->elements = [];
  391.     }
  392.     /**
  393.      * {@inheritDoc}
  394.      */
  395.     public function slice(int $offsetint|null $length null)
  396.     {
  397.         return array_slice($this->elements$offset$lengthtrue);
  398.     }
  399.     /** @phpstan-return Collection<TKey, T>&Selectable<TKey,T> */
  400.     public function matching(Criteria $criteria)
  401.     {
  402.         $expr     $criteria->getWhereExpression();
  403.         $filtered $this->elements;
  404.         if ($expr) {
  405.             $visitor  = new ClosureExpressionVisitor();
  406.             $filter   $visitor->dispatch($expr);
  407.             $filtered array_filter($filtered$filter);
  408.         }
  409.         $orderings $criteria->orderings();
  410.         if ($orderings) {
  411.             $next null;
  412.             foreach (array_reverse($orderings) as $field => $ordering) {
  413.                 $next ClosureExpressionVisitor::sortByField($field$ordering === Order::Descending ? -1$next);
  414.             }
  415.             uasort($filtered$next);
  416.         }
  417.         $offset $criteria->getFirstResult();
  418.         $length $criteria->getMaxResults();
  419.         if ($offset !== null && $offset || $length !== null && $length 0) {
  420.             $filtered array_slice($filtered, (int) $offset$lengthtrue);
  421.         }
  422.         return $this->createFrom($filtered);
  423.     }
  424. }