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.      * @param array $elements
  56.      * @psalm-param array<TKey,T> $elements
  57.      */
  58.     public function __construct(array $elements = [])
  59.     {
  60.         $this->elements $elements;
  61.     }
  62.     /**
  63.      * {@inheritDoc}
  64.      */
  65.     public function toArray()
  66.     {
  67.         return $this->elements;
  68.     }
  69.     /**
  70.      * {@inheritDoc}
  71.      */
  72.     public function first()
  73.     {
  74.         return reset($this->elements);
  75.     }
  76.     /**
  77.      * Creates a new instance from the specified elements.
  78.      *
  79.      * This method is provided for derived classes to specify how a new
  80.      * instance should be created when constructor semantics have changed.
  81.      *
  82.      * @param array $elements Elements.
  83.      * @psalm-param array<K,V> $elements
  84.      *
  85.      * @return static
  86.      * @psalm-return static<K,V>
  87.      *
  88.      * @psalm-template K of array-key
  89.      * @psalm-template V
  90.      */
  91.     protected function createFrom(array $elements)
  92.     {
  93.         return new static($elements);
  94.     }
  95.     /**
  96.      * {@inheritDoc}
  97.      */
  98.     public function last()
  99.     {
  100.         return end($this->elements);
  101.     }
  102.     /**
  103.      * {@inheritDoc}
  104.      */
  105.     public function key()
  106.     {
  107.         return key($this->elements);
  108.     }
  109.     /**
  110.      * {@inheritDoc}
  111.      */
  112.     public function next()
  113.     {
  114.         return next($this->elements);
  115.     }
  116.     /**
  117.      * {@inheritDoc}
  118.      */
  119.     public function current()
  120.     {
  121.         return current($this->elements);
  122.     }
  123.     /**
  124.      * {@inheritDoc}
  125.      */
  126.     public function remove(string|int $key)
  127.     {
  128.         if (! isset($this->elements[$key]) && ! array_key_exists($key$this->elements)) {
  129.             return null;
  130.         }
  131.         $removed $this->elements[$key];
  132.         unset($this->elements[$key]);
  133.         return $removed;
  134.     }
  135.     /**
  136.      * {@inheritDoc}
  137.      */
  138.     public function removeElement(mixed $element)
  139.     {
  140.         $key array_search($element$this->elementstrue);
  141.         if ($key === false) {
  142.             return false;
  143.         }
  144.         unset($this->elements[$key]);
  145.         return true;
  146.     }
  147.     /**
  148.      * Required by interface ArrayAccess.
  149.      *
  150.      * @param TKey $offset
  151.      *
  152.      * @return bool
  153.      */
  154.     #[ReturnTypeWillChange]
  155.     public function offsetExists(mixed $offset)
  156.     {
  157.         return $this->containsKey($offset);
  158.     }
  159.     /**
  160.      * Required by interface ArrayAccess.
  161.      *
  162.      * @param TKey $offset
  163.      *
  164.      * @return T|null
  165.      */
  166.     #[ReturnTypeWillChange]
  167.     public function offsetGet(mixed $offset)
  168.     {
  169.         return $this->get($offset);
  170.     }
  171.     /**
  172.      * Required by interface ArrayAccess.
  173.      *
  174.      * @param TKey|null $offset
  175.      * @param T         $value
  176.      *
  177.      * @return void
  178.      */
  179.     #[ReturnTypeWillChange]
  180.     public function offsetSet(mixed $offsetmixed $value)
  181.     {
  182.         if ($offset === null) {
  183.             $this->add($value);
  184.             return;
  185.         }
  186.         $this->set($offset$value);
  187.     }
  188.     /**
  189.      * Required by interface ArrayAccess.
  190.      *
  191.      * @param TKey $offset
  192.      *
  193.      * @return void
  194.      */
  195.     #[ReturnTypeWillChange]
  196.     public function offsetUnset(mixed $offset)
  197.     {
  198.         $this->remove($offset);
  199.     }
  200.     /**
  201.      * {@inheritDoc}
  202.      */
  203.     public function containsKey(string|int $key)
  204.     {
  205.         return isset($this->elements[$key]) || array_key_exists($key$this->elements);
  206.     }
  207.     /**
  208.      * {@inheritDoc}
  209.      *
  210.      * @template TMaybeContained
  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.         foreach ($this->elements as $key => $element) {
  222.             if ($p($key$element)) {
  223.                 return true;
  224.             }
  225.         }
  226.         return false;
  227.     }
  228.     /**
  229.      * {@inheritDoc}
  230.      *
  231.      * @psalm-param TMaybeContained $element
  232.      *
  233.      * @return int|string|false
  234.      * @psalm-return (TMaybeContained is T ? TKey|false : false)
  235.      *
  236.      * @template TMaybeContained
  237.      */
  238.     public function indexOf($element)
  239.     {
  240.         return array_search($element$this->elementstrue);
  241.     }
  242.     /**
  243.      * {@inheritDoc}
  244.      */
  245.     public function get(string|int $key)
  246.     {
  247.         return $this->elements[$key] ?? null;
  248.     }
  249.     /**
  250.      * {@inheritDoc}
  251.      */
  252.     public function getKeys()
  253.     {
  254.         return array_keys($this->elements);
  255.     }
  256.     /**
  257.      * {@inheritDoc}
  258.      */
  259.     public function getValues()
  260.     {
  261.         return array_values($this->elements);
  262.     }
  263.     /**
  264.      * {@inheritDoc}
  265.      *
  266.      * @return int<0, max>
  267.      */
  268.     #[ReturnTypeWillChange]
  269.     public function count()
  270.     {
  271.         return count($this->elements);
  272.     }
  273.     /**
  274.      * {@inheritDoc}
  275.      */
  276.     public function set(string|int $keymixed $value)
  277.     {
  278.         $this->elements[$key] = $value;
  279.     }
  280.     /**
  281.      * {@inheritDoc}
  282.      *
  283.      * @psalm-suppress InvalidPropertyAssignmentValue
  284.      *
  285.      * This breaks assumptions about the template type, but it would
  286.      * be a backwards-incompatible change to remove this method
  287.      */
  288.     public function add(mixed $element)
  289.     {
  290.         $this->elements[] = $element;
  291.     }
  292.     /**
  293.      * {@inheritDoc}
  294.      */
  295.     public function isEmpty()
  296.     {
  297.         return empty($this->elements);
  298.     }
  299.     /**
  300.      * {@inheritDoc}
  301.      *
  302.      * @return Traversable<int|string, mixed>
  303.      * @psalm-return Traversable<TKey, T>
  304.      */
  305.     #[ReturnTypeWillChange]
  306.     public function getIterator()
  307.     {
  308.         return new ArrayIterator($this->elements);
  309.     }
  310.     /**
  311.      * {@inheritDoc}
  312.      *
  313.      * @psalm-param Closure(T):U $func
  314.      *
  315.      * @return static
  316.      * @psalm-return static<TKey, U>
  317.      *
  318.      * @psalm-template U
  319.      */
  320.     public function map(Closure $func)
  321.     {
  322.         return $this->createFrom(array_map($func$this->elements));
  323.     }
  324.     /**
  325.      * {@inheritDoc}
  326.      */
  327.     public function reduce(Closure $func$initial null)
  328.     {
  329.         return array_reduce($this->elements$func$initial);
  330.     }
  331.     /**
  332.      * {@inheritDoc}
  333.      *
  334.      * @return static
  335.      * @psalm-return static<TKey,T>
  336.      */
  337.     public function filter(Closure $p)
  338.     {
  339.         return $this->createFrom(array_filter($this->elements$pARRAY_FILTER_USE_BOTH));
  340.     }
  341.     /**
  342.      * {@inheritDoc}
  343.      */
  344.     public function findFirst(Closure $p)
  345.     {
  346.         foreach ($this->elements as $key => $element) {
  347.             if ($p($key$element)) {
  348.                 return $element;
  349.             }
  350.         }
  351.         return null;
  352.     }
  353.     /**
  354.      * {@inheritDoc}
  355.      */
  356.     public function forAll(Closure $p)
  357.     {
  358.         foreach ($this->elements as $key => $element) {
  359.             if (! $p($key$element)) {
  360.                 return false;
  361.             }
  362.         }
  363.         return true;
  364.     }
  365.     /**
  366.      * {@inheritDoc}
  367.      */
  368.     public function partition(Closure $p)
  369.     {
  370.         $matches $noMatches = [];
  371.         foreach ($this->elements as $key => $element) {
  372.             if ($p($key$element)) {
  373.                 $matches[$key] = $element;
  374.             } else {
  375.                 $noMatches[$key] = $element;
  376.             }
  377.         }
  378.         return [$this->createFrom($matches), $this->createFrom($noMatches)];
  379.     }
  380.     /**
  381.      * Returns a string representation of this object.
  382.      * {@inheritDoc}
  383.      */
  384.     public function __toString()
  385.     {
  386.         return self::class . '@' spl_object_hash($this);
  387.     }
  388.     /**
  389.      * {@inheritDoc}
  390.      */
  391.     public function clear()
  392.     {
  393.         $this->elements = [];
  394.     }
  395.     /**
  396.      * {@inheritDoc}
  397.      */
  398.     public function slice(int $offsetint|null $length null)
  399.     {
  400.         return array_slice($this->elements$offset$lengthtrue);
  401.     }
  402.     /** @psalm-return Collection<TKey, T>&Selectable<TKey,T> */
  403.     public function matching(Criteria $criteria)
  404.     {
  405.         $expr     $criteria->getWhereExpression();
  406.         $filtered $this->elements;
  407.         if ($expr) {
  408.             $visitor  = new ClosureExpressionVisitor();
  409.             $filter   $visitor->dispatch($expr);
  410.             $filtered array_filter($filtered$filter);
  411.         }
  412.         $orderings $criteria->getOrderings();
  413.         if ($orderings) {
  414.             $next null;
  415.             foreach (array_reverse($orderings) as $field => $ordering) {
  416.                 $next ClosureExpressionVisitor::sortByField($field$ordering === Criteria::DESC ? -1$next);
  417.             }
  418.             uasort($filtered$next);
  419.         }
  420.         $offset $criteria->getFirstResult();
  421.         $length $criteria->getMaxResults();
  422.         if ($offset || $length) {
  423.             $filtered array_slice($filtered, (int) $offset$lengthtrue);
  424.         }
  425.         return $this->createFrom($filtered);
  426.     }
  427. }