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

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