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.         /** @phpstan-var TKey $offset */
  189.         $this->set($offset$value);
  190.     }
  191.     /**
  192.      * Required by interface ArrayAccess.
  193.      *
  194.      * @param TKey $offset
  195.      *
  196.      * @return void
  197.      */
  198.     #[ReturnTypeWillChange]
  199.     public function offsetUnset(mixed $offset)
  200.     {
  201.         $this->remove($offset);
  202.     }
  203.     /**
  204.      * {@inheritDoc}
  205.      */
  206.     public function containsKey(string|int $key)
  207.     {
  208.         return isset($this->elements[$key]) || array_key_exists($key$this->elements);
  209.     }
  210.     /**
  211.      * {@inheritDoc}
  212.      */
  213.     public function contains(mixed $element)
  214.     {
  215.         return in_array($element$this->elementstrue);
  216.     }
  217.     /**
  218.      * {@inheritDoc}
  219.      */
  220.     public function exists(Closure $p)
  221.     {
  222.         return array_any(
  223.             $this->elements,
  224.             static fn (mixed $elementmixed $key): bool => (bool) $p($key$element),
  225.         );
  226.     }
  227.     /**
  228.      * {@inheritDoc}
  229.      *
  230.      * @phpstan-param TMaybeContained $element
  231.      *
  232.      * @return int|string|false
  233.      * @phpstan-return (TMaybeContained is T ? TKey|false : false)
  234.      *
  235.      * @template TMaybeContained
  236.      */
  237.     public function indexOf($element)
  238.     {
  239.         return array_search($element$this->elementstrue);
  240.     }
  241.     /**
  242.      * {@inheritDoc}
  243.      */
  244.     public function get(string|int $key)
  245.     {
  246.         return $this->elements[$key] ?? null;
  247.     }
  248.     /**
  249.      * {@inheritDoc}
  250.      */
  251.     public function getKeys()
  252.     {
  253.         return array_keys($this->elements);
  254.     }
  255.     /**
  256.      * {@inheritDoc}
  257.      */
  258.     public function getValues()
  259.     {
  260.         return array_values($this->elements);
  261.     }
  262.     /**
  263.      * {@inheritDoc}
  264.      *
  265.      * @return int<0, max>
  266.      */
  267.     #[ReturnTypeWillChange]
  268.     public function count()
  269.     {
  270.         return count($this->elements);
  271.     }
  272.     /**
  273.      * {@inheritDoc}
  274.      */
  275.     public function set(string|int $keymixed $value)
  276.     {
  277.         $this->elements[$key] = $value;
  278.     }
  279.     /**
  280.      * {@inheritDoc}
  281.      *
  282.      * This breaks assumptions about the template type, but it would
  283.      * be a backwards-incompatible change to remove this method
  284.      */
  285.     public function add(mixed $element)
  286.     {
  287.         $this->elements[] = $element;
  288.     }
  289.     /**
  290.      * {@inheritDoc}
  291.      */
  292.     public function isEmpty()
  293.     {
  294.         return empty($this->elements);
  295.     }
  296.     /**
  297.      * {@inheritDoc}
  298.      *
  299.      * @return Traversable<int|string, mixed>
  300.      * @phpstan-return Traversable<TKey, T>
  301.      */
  302.     #[ReturnTypeWillChange]
  303.     public function getIterator()
  304.     {
  305.         return new ArrayIterator($this->elements);
  306.     }
  307.     /**
  308.      * {@inheritDoc}
  309.      *
  310.      * @phpstan-param Closure(T):U $func
  311.      *
  312.      * @return static
  313.      * @phpstan-return static<TKey, U>
  314.      *
  315.      * @phpstan-template U
  316.      */
  317.     public function map(Closure $func)
  318.     {
  319.         return $this->createFrom(array_map($func$this->elements));
  320.     }
  321.     /**
  322.      * {@inheritDoc}
  323.      */
  324.     public function reduce(Closure $func$initial null)
  325.     {
  326.         return array_reduce($this->elements$func$initial);
  327.     }
  328.     /**
  329.      * {@inheritDoc}
  330.      *
  331.      * @phpstan-param Closure(T, TKey):bool $p
  332.      *
  333.      * @return static
  334.      * @phpstan-return static<TKey,T>
  335.      */
  336.     public function filter(Closure $p)
  337.     {
  338.         return $this->createFrom(array_filter($this->elements$pARRAY_FILTER_USE_BOTH));
  339.     }
  340.     /**
  341.      * {@inheritDoc}
  342.      */
  343.     public function findFirst(Closure $p)
  344.     {
  345.         return array_find(
  346.             $this->elements,
  347.             static fn (mixed $elementmixed $key): bool => (bool) $p($key$element),
  348.         );
  349.     }
  350.     /**
  351.      * {@inheritDoc}
  352.      */
  353.     public function forAll(Closure $p)
  354.     {
  355.         return array_all(
  356.             $this->elements,
  357.             static fn (mixed $elementmixed $key): bool => (bool) $p($key$element),
  358.         );
  359.     }
  360.     /**
  361.      * {@inheritDoc}
  362.      */
  363.     public function partition(Closure $p)
  364.     {
  365.         $matches $noMatches = [];
  366.         foreach ($this->elements as $key => $element) {
  367.             if ($p($key$element)) {
  368.                 $matches[$key] = $element;
  369.             } else {
  370.                 $noMatches[$key] = $element;
  371.             }
  372.         }
  373.         return [$this->createFrom($matches), $this->createFrom($noMatches)];
  374.     }
  375.     /**
  376.      * Returns a string representation of this object.
  377.      * {@inheritDoc}
  378.      *
  379.      * @return string
  380.      */
  381.     #[ReturnTypeWillChange]
  382.     public function __toString()
  383.     {
  384.         return self::class . '@' spl_object_hash($this);
  385.     }
  386.     /**
  387.      * {@inheritDoc}
  388.      */
  389.     public function clear()
  390.     {
  391.         $this->elements = [];
  392.     }
  393.     /**
  394.      * {@inheritDoc}
  395.      */
  396.     public function slice(int $offsetint|null $length null)
  397.     {
  398.         return array_slice($this->elements$offset$lengthtrue);
  399.     }
  400.     /** @phpstan-return Collection<TKey, T>&Selectable<TKey,T> */
  401.     public function matching(Criteria $criteria)
  402.     {
  403.         $accessRawFieldValues $criteria->isRawFieldValueAccessEnabled();
  404.         $expr     $criteria->getWhereExpression();
  405.         $filtered $this->elements;
  406.         if ($expr) {
  407.             $visitor  = new ClosureExpressionVisitor($accessRawFieldValues);
  408.             $filter   $visitor->dispatch($expr);
  409.             $filtered array_filter($filtered$filter);
  410.         }
  411.         $orderings $criteria->orderings();
  412.         if ($orderings) {
  413.             $next null;
  414.             foreach (array_reverse($orderings) as $field => $ordering) {
  415.                 $next ClosureExpressionVisitor::sortByField($field$ordering === Order::Descending ? -1$next$accessRawFieldValues);
  416.             }
  417.             uasort($filtered$next);
  418.         }
  419.         $offset $criteria->getFirstResult();
  420.         $length $criteria->getMaxResults();
  421.         if ($offset !== null && $offset || $length !== null && $length 0) {
  422.             $filtered array_slice($filtered, (int) $offset$lengthtrue);
  423.         }
  424.         return $this->createFrom($filtered);
  425.     }
  426. }