From bf6655a534a6775d30cafa67bd801276bda1d98d Mon Sep 17 00:00:00 2001 From: polo Date: Tue, 13 Aug 2024 23:45:21 +0200 Subject: =?UTF-8?q?VERSION=200.2=20doctrine=20ORM=20et=20entit=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../collections/src/AbstractLazyCollection.php | 414 +++++++++++++++++ .../doctrine/collections/src/ArrayCollection.php | 490 +++++++++++++++++++++ vendor/doctrine/collections/src/Collection.php | 117 +++++ vendor/doctrine/collections/src/Criteria.php | 285 ++++++++++++ .../src/Expr/ClosureExpressionVisitor.php | 222 ++++++++++ .../doctrine/collections/src/Expr/Comparison.php | 62 +++ .../collections/src/Expr/CompositeExpression.php | 70 +++ .../doctrine/collections/src/Expr/Expression.php | 14 + .../collections/src/Expr/ExpressionVisitor.php | 52 +++ vendor/doctrine/collections/src/Expr/Value.php | 26 ++ .../doctrine/collections/src/ExpressionBuilder.php | 123 ++++++ vendor/doctrine/collections/src/Order.php | 11 + .../collections/src/ReadableCollection.php | 242 ++++++++++ vendor/doctrine/collections/src/Selectable.php | 32 ++ 14 files changed, 2160 insertions(+) create mode 100644 vendor/doctrine/collections/src/AbstractLazyCollection.php create mode 100644 vendor/doctrine/collections/src/ArrayCollection.php create mode 100644 vendor/doctrine/collections/src/Collection.php create mode 100644 vendor/doctrine/collections/src/Criteria.php create mode 100644 vendor/doctrine/collections/src/Expr/ClosureExpressionVisitor.php create mode 100644 vendor/doctrine/collections/src/Expr/Comparison.php create mode 100644 vendor/doctrine/collections/src/Expr/CompositeExpression.php create mode 100644 vendor/doctrine/collections/src/Expr/Expression.php create mode 100644 vendor/doctrine/collections/src/Expr/ExpressionVisitor.php create mode 100644 vendor/doctrine/collections/src/Expr/Value.php create mode 100644 vendor/doctrine/collections/src/ExpressionBuilder.php create mode 100644 vendor/doctrine/collections/src/Order.php create mode 100644 vendor/doctrine/collections/src/ReadableCollection.php create mode 100644 vendor/doctrine/collections/src/Selectable.php (limited to 'vendor/doctrine/collections/src') diff --git a/vendor/doctrine/collections/src/AbstractLazyCollection.php b/vendor/doctrine/collections/src/AbstractLazyCollection.php new file mode 100644 index 0000000..56a340b --- /dev/null +++ b/vendor/doctrine/collections/src/AbstractLazyCollection.php @@ -0,0 +1,414 @@ + + */ +abstract class AbstractLazyCollection implements Collection +{ + /** + * The backed collection to use + * + * @psalm-var Collection|null + * @var Collection|null + */ + protected Collection|null $collection; + + protected bool $initialized = false; + + /** + * {@inheritDoc} + * + * @return int + */ + #[ReturnTypeWillChange] + public function count() + { + $this->initialize(); + + return $this->collection->count(); + } + + /** + * {@inheritDoc} + */ + public function add(mixed $element) + { + $this->initialize(); + + $this->collection->add($element); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->initialize(); + $this->collection->clear(); + } + + /** + * {@inheritDoc} + */ + public function contains(mixed $element) + { + $this->initialize(); + + return $this->collection->contains($element); + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + $this->initialize(); + + return $this->collection->isEmpty(); + } + + /** + * {@inheritDoc} + */ + public function remove(string|int $key) + { + $this->initialize(); + + return $this->collection->remove($key); + } + + /** + * {@inheritDoc} + */ + public function removeElement(mixed $element) + { + $this->initialize(); + + return $this->collection->removeElement($element); + } + + /** + * {@inheritDoc} + */ + public function containsKey(string|int $key) + { + $this->initialize(); + + return $this->collection->containsKey($key); + } + + /** + * {@inheritDoc} + */ + public function get(string|int $key) + { + $this->initialize(); + + return $this->collection->get($key); + } + + /** + * {@inheritDoc} + */ + public function getKeys() + { + $this->initialize(); + + return $this->collection->getKeys(); + } + + /** + * {@inheritDoc} + */ + public function getValues() + { + $this->initialize(); + + return $this->collection->getValues(); + } + + /** + * {@inheritDoc} + */ + public function set(string|int $key, mixed $value) + { + $this->initialize(); + $this->collection->set($key, $value); + } + + /** + * {@inheritDoc} + */ + public function toArray() + { + $this->initialize(); + + return $this->collection->toArray(); + } + + /** + * {@inheritDoc} + */ + public function first() + { + $this->initialize(); + + return $this->collection->first(); + } + + /** + * {@inheritDoc} + */ + public function last() + { + $this->initialize(); + + return $this->collection->last(); + } + + /** + * {@inheritDoc} + */ + public function key() + { + $this->initialize(); + + return $this->collection->key(); + } + + /** + * {@inheritDoc} + */ + public function current() + { + $this->initialize(); + + return $this->collection->current(); + } + + /** + * {@inheritDoc} + */ + public function next() + { + $this->initialize(); + + return $this->collection->next(); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $p) + { + $this->initialize(); + + return $this->collection->exists($p); + } + + /** + * {@inheritDoc} + */ + public function findFirst(Closure $p) + { + $this->initialize(); + + return $this->collection->findFirst($p); + } + + /** + * {@inheritDoc} + */ + public function filter(Closure $p) + { + $this->initialize(); + + return $this->collection->filter($p); + } + + /** + * {@inheritDoc} + */ + public function forAll(Closure $p) + { + $this->initialize(); + + return $this->collection->forAll($p); + } + + /** + * {@inheritDoc} + */ + public function map(Closure $func) + { + $this->initialize(); + + return $this->collection->map($func); + } + + /** + * {@inheritDoc} + */ + public function reduce(Closure $func, mixed $initial = null) + { + $this->initialize(); + + return $this->collection->reduce($func, $initial); + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $p) + { + $this->initialize(); + + return $this->collection->partition($p); + } + + /** + * {@inheritDoc} + * + * @template TMaybeContained + */ + public function indexOf(mixed $element) + { + $this->initialize(); + + return $this->collection->indexOf($element); + } + + /** + * {@inheritDoc} + */ + public function slice(int $offset, int|null $length = null) + { + $this->initialize(); + + return $this->collection->slice($offset, $length); + } + + /** + * {@inheritDoc} + * + * @return Traversable + * @psalm-return Traversable + */ + #[ReturnTypeWillChange] + public function getIterator() + { + $this->initialize(); + + return $this->collection->getIterator(); + } + + /** + * {@inheritDoc} + * + * @param TKey $offset + * + * @return bool + */ + #[ReturnTypeWillChange] + public function offsetExists(mixed $offset) + { + $this->initialize(); + + return $this->collection->offsetExists($offset); + } + + /** + * {@inheritDoc} + * + * @param TKey $offset + * + * @return T|null + */ + #[ReturnTypeWillChange] + public function offsetGet(mixed $offset) + { + $this->initialize(); + + return $this->collection->offsetGet($offset); + } + + /** + * {@inheritDoc} + * + * @param TKey|null $offset + * @param T $value + * + * @return void + */ + #[ReturnTypeWillChange] + public function offsetSet(mixed $offset, mixed $value) + { + $this->initialize(); + $this->collection->offsetSet($offset, $value); + } + + /** + * @param TKey $offset + * + * @return void + */ + #[ReturnTypeWillChange] + public function offsetUnset(mixed $offset) + { + $this->initialize(); + $this->collection->offsetUnset($offset); + } + + /** + * Is the lazy collection already initialized? + * + * @return bool + * + * @psalm-assert-if-true Collection $this->collection + */ + public function isInitialized() + { + return $this->initialized; + } + + /** + * Initialize the collection + * + * @return void + * + * @psalm-assert Collection $this->collection + */ + protected function initialize() + { + if ($this->initialized) { + return; + } + + $this->doInitialize(); + $this->initialized = true; + + if ($this->collection === null) { + throw new LogicException('You must initialize the collection property in the doInitialize() method.'); + } + } + + /** + * Do the initialization logic + * + * @return void + */ + abstract protected function doInitialize(); +} diff --git a/vendor/doctrine/collections/src/ArrayCollection.php b/vendor/doctrine/collections/src/ArrayCollection.php new file mode 100644 index 0000000..2e7d0e3 --- /dev/null +++ b/vendor/doctrine/collections/src/ArrayCollection.php @@ -0,0 +1,490 @@ + + * @template-implements Selectable + * @psalm-consistent-constructor + */ +class ArrayCollection implements Collection, Selectable, Stringable +{ + /** + * An array containing the entries of this collection. + * + * @psalm-var array + * @var mixed[] + */ + private array $elements = []; + + /** + * Initializes a new ArrayCollection. + * + * @psalm-param array $elements + */ + public function __construct(array $elements = []) + { + $this->elements = $elements; + } + + /** + * {@inheritDoc} + */ + public function toArray() + { + return $this->elements; + } + + /** + * {@inheritDoc} + */ + public function first() + { + return reset($this->elements); + } + + /** + * Creates a new instance from the specified elements. + * + * This method is provided for derived classes to specify how a new + * instance should be created when constructor semantics have changed. + * + * @param array $elements Elements. + * @psalm-param array $elements + * + * @return static + * @psalm-return static + * + * @psalm-template K of array-key + * @psalm-template V + */ + protected function createFrom(array $elements) + { + return new static($elements); + } + + /** + * {@inheritDoc} + */ + public function last() + { + return end($this->elements); + } + + /** + * {@inheritDoc} + */ + public function key() + { + return key($this->elements); + } + + /** + * {@inheritDoc} + */ + public function next() + { + return next($this->elements); + } + + /** + * {@inheritDoc} + */ + public function current() + { + return current($this->elements); + } + + /** + * {@inheritDoc} + */ + public function remove(string|int $key) + { + if (! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) { + return null; + } + + $removed = $this->elements[$key]; + unset($this->elements[$key]); + + return $removed; + } + + /** + * {@inheritDoc} + */ + public function removeElement(mixed $element) + { + $key = array_search($element, $this->elements, true); + + if ($key === false) { + return false; + } + + unset($this->elements[$key]); + + return true; + } + + /** + * Required by interface ArrayAccess. + * + * @param TKey $offset + * + * @return bool + */ + #[ReturnTypeWillChange] + public function offsetExists(mixed $offset) + { + return $this->containsKey($offset); + } + + /** + * Required by interface ArrayAccess. + * + * @param TKey $offset + * + * @return T|null + */ + #[ReturnTypeWillChange] + public function offsetGet(mixed $offset) + { + return $this->get($offset); + } + + /** + * Required by interface ArrayAccess. + * + * @param TKey|null $offset + * @param T $value + * + * @return void + */ + #[ReturnTypeWillChange] + public function offsetSet(mixed $offset, mixed $value) + { + if ($offset === null) { + $this->add($value); + + return; + } + + $this->set($offset, $value); + } + + /** + * Required by interface ArrayAccess. + * + * @param TKey $offset + * + * @return void + */ + #[ReturnTypeWillChange] + public function offsetUnset(mixed $offset) + { + $this->remove($offset); + } + + /** + * {@inheritDoc} + */ + public function containsKey(string|int $key) + { + return isset($this->elements[$key]) || array_key_exists($key, $this->elements); + } + + /** + * {@inheritDoc} + */ + public function contains(mixed $element) + { + return in_array($element, $this->elements, true); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $p) + { + foreach ($this->elements as $key => $element) { + if ($p($key, $element)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + * + * @psalm-param TMaybeContained $element + * + * @return int|string|false + * @psalm-return (TMaybeContained is T ? TKey|false : false) + * + * @template TMaybeContained + */ + public function indexOf($element) + { + return array_search($element, $this->elements, true); + } + + /** + * {@inheritDoc} + */ + public function get(string|int $key) + { + return $this->elements[$key] ?? null; + } + + /** + * {@inheritDoc} + */ + public function getKeys() + { + return array_keys($this->elements); + } + + /** + * {@inheritDoc} + */ + public function getValues() + { + return array_values($this->elements); + } + + /** + * {@inheritDoc} + * + * @return int<0, max> + */ + #[ReturnTypeWillChange] + public function count() + { + return count($this->elements); + } + + /** + * {@inheritDoc} + */ + public function set(string|int $key, mixed $value) + { + $this->elements[$key] = $value; + } + + /** + * {@inheritDoc} + * + * @psalm-suppress InvalidPropertyAssignmentValue + * + * This breaks assumptions about the template type, but it would + * be a backwards-incompatible change to remove this method + */ + public function add(mixed $element) + { + $this->elements[] = $element; + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + return empty($this->elements); + } + + /** + * {@inheritDoc} + * + * @return Traversable + * @psalm-return Traversable + */ + #[ReturnTypeWillChange] + public function getIterator() + { + return new ArrayIterator($this->elements); + } + + /** + * {@inheritDoc} + * + * @psalm-param Closure(T):U $func + * + * @return static + * @psalm-return static + * + * @psalm-template U + */ + public function map(Closure $func) + { + return $this->createFrom(array_map($func, $this->elements)); + } + + /** + * {@inheritDoc} + */ + public function reduce(Closure $func, $initial = null) + { + return array_reduce($this->elements, $func, $initial); + } + + /** + * {@inheritDoc} + * + * @psalm-param Closure(T, TKey):bool $p + * + * @return static + * @psalm-return static + */ + public function filter(Closure $p) + { + return $this->createFrom(array_filter($this->elements, $p, ARRAY_FILTER_USE_BOTH)); + } + + /** + * {@inheritDoc} + */ + public function findFirst(Closure $p) + { + foreach ($this->elements as $key => $element) { + if ($p($key, $element)) { + return $element; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function forAll(Closure $p) + { + foreach ($this->elements as $key => $element) { + if (! $p($key, $element)) { + return false; + } + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $p) + { + $matches = $noMatches = []; + + foreach ($this->elements as $key => $element) { + if ($p($key, $element)) { + $matches[$key] = $element; + } else { + $noMatches[$key] = $element; + } + } + + return [$this->createFrom($matches), $this->createFrom($noMatches)]; + } + + /** + * Returns a string representation of this object. + * {@inheritDoc} + * + * @return string + */ + #[ReturnTypeWillChange] + public function __toString() + { + return self::class . '@' . spl_object_hash($this); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->elements = []; + } + + /** + * {@inheritDoc} + */ + public function slice(int $offset, int|null $length = null) + { + return array_slice($this->elements, $offset, $length, true); + } + + /** @psalm-return Collection&Selectable */ + public function matching(Criteria $criteria) + { + $expr = $criteria->getWhereExpression(); + $filtered = $this->elements; + + if ($expr) { + $visitor = new ClosureExpressionVisitor(); + $filter = $visitor->dispatch($expr); + $filtered = array_filter($filtered, $filter); + } + + $orderings = $criteria->orderings(); + + if ($orderings) { + $next = null; + foreach (array_reverse($orderings) as $field => $ordering) { + $next = ClosureExpressionVisitor::sortByField($field, $ordering === Order::Descending ? -1 : 1, $next); + } + + uasort($filtered, $next); + } + + $offset = $criteria->getFirstResult(); + $length = $criteria->getMaxResults(); + + if ($offset !== null && $offset > 0 || $length !== null && $length > 0) { + $filtered = array_slice($filtered, (int) $offset, $length, true); + } + + return $this->createFrom($filtered); + } +} diff --git a/vendor/doctrine/collections/src/Collection.php b/vendor/doctrine/collections/src/Collection.php new file mode 100644 index 0000000..c3c62aa --- /dev/null +++ b/vendor/doctrine/collections/src/Collection.php @@ -0,0 +1,117 @@ +ordered map that can also be used + * like a list. + * + * A Collection has an internal iterator just like a PHP array. In addition, + * a Collection can be iterated with external iterators, which is preferable. + * To use an external iterator simply use the foreach language construct to + * iterate over the collection (which calls {@link getIterator()} internally) or + * explicitly retrieve an iterator though {@link getIterator()} which can then be + * used to iterate over the collection. + * You can not rely on the internal iterator of the collection being at a certain + * position unless you explicitly positioned it before. Prefer iteration with + * external iterators. + * + * @psalm-template TKey of array-key + * @psalm-template T + * @template-extends ReadableCollection + * @template-extends ArrayAccess + */ +interface Collection extends ReadableCollection, ArrayAccess +{ + /** + * Adds an element at the end of the collection. + * + * @param mixed $element The element to add. + * @psalm-param T $element + * + * @return void we will require a native return type declaration in 3.0 + */ + public function add(mixed $element); + + /** + * Clears the collection, removing all elements. + * + * @return void + */ + public function clear(); + + /** + * Removes the element at the specified index from the collection. + * + * @param string|int $key The key/index of the element to remove. + * @psalm-param TKey $key + * + * @return mixed The removed element or NULL, if the collection did not contain the element. + * @psalm-return T|null + */ + public function remove(string|int $key); + + /** + * Removes the specified element from the collection, if it is found. + * + * @param mixed $element The element to remove. + * @psalm-param T $element + * + * @return bool TRUE if this collection contained the specified element, FALSE otherwise. + */ + public function removeElement(mixed $element); + + /** + * Sets an element in the collection at the specified key/index. + * + * @param string|int $key The key/index of the element to set. + * @param mixed $value The element to set. + * @psalm-param TKey $key + * @psalm-param T $value + * + * @return void + */ + public function set(string|int $key, mixed $value); + + /** + * {@inheritDoc} + * + * @psalm-param Closure(T):U $func + * + * @return Collection + * @psalm-return Collection + * + * @psalm-template U + */ + public function map(Closure $func); + + /** + * {@inheritDoc} + * + * @psalm-param Closure(T, TKey):bool $p + * + * @return Collection A collection with the results of the filter operation. + * @psalm-return Collection + */ + public function filter(Closure $p); + + /** + * {@inheritDoc} + * + * @psalm-param Closure(TKey, T):bool $p + * + * @return Collection[] An array with two elements. The first element contains the collection + * of elements where the predicate returned TRUE, the second element + * contains the collection of elements where the predicate returned FALSE. + * @psalm-return array{0: Collection, 1: Collection} + */ + public function partition(Closure $p); +} diff --git a/vendor/doctrine/collections/src/Criteria.php b/vendor/doctrine/collections/src/Criteria.php new file mode 100644 index 0000000..4c8a0a7 --- /dev/null +++ b/vendor/doctrine/collections/src/Criteria.php @@ -0,0 +1,285 @@ + */ + private array $orderings = []; + + private int|null $firstResult = null; + private int|null $maxResults = null; + + /** + * Creates an instance of the class. + * + * @return static + */ + public static function create() + { + return new static(); + } + + /** + * Returns the expression builder. + * + * @return ExpressionBuilder + */ + public static function expr() + { + if (self::$expressionBuilder === null) { + self::$expressionBuilder = new ExpressionBuilder(); + } + + return self::$expressionBuilder; + } + + /** + * Construct a new Criteria. + * + * @param array|null $orderings + */ + public function __construct( + private Expression|null $expression = null, + array|null $orderings = null, + int|null $firstResult = null, + int|null $maxResults = null, + ) { + $this->expression = $expression; + + if ($firstResult === null && func_num_args() > 2) { + Deprecation::trigger( + 'doctrine/collections', + 'https://github.com/doctrine/collections/pull/311', + 'Passing null as $firstResult to the constructor of %s is deprecated. Pass 0 instead or omit the argument.', + self::class, + ); + } + + $this->setFirstResult($firstResult); + $this->setMaxResults($maxResults); + + if ($orderings === null) { + return; + } + + $this->orderBy($orderings); + } + + /** + * Sets the where expression to evaluate when this Criteria is searched for. + * + * @return $this + */ + public function where(Expression $expression) + { + $this->expression = $expression; + + return $this; + } + + /** + * Appends the where expression to evaluate when this Criteria is searched for + * using an AND with previous expression. + * + * @return $this + */ + public function andWhere(Expression $expression) + { + if ($this->expression === null) { + return $this->where($expression); + } + + $this->expression = new CompositeExpression( + CompositeExpression::TYPE_AND, + [$this->expression, $expression], + ); + + return $this; + } + + /** + * Appends the where expression to evaluate when this Criteria is searched for + * using an OR with previous expression. + * + * @return $this + */ + public function orWhere(Expression $expression) + { + if ($this->expression === null) { + return $this->where($expression); + } + + $this->expression = new CompositeExpression( + CompositeExpression::TYPE_OR, + [$this->expression, $expression], + ); + + return $this; + } + + /** + * Gets the expression attached to this Criteria. + * + * @return Expression|null + */ + public function getWhereExpression() + { + return $this->expression; + } + + /** + * Gets the current orderings of this Criteria. + * + * @deprecated use orderings() instead + * + * @return array + */ + public function getOrderings() + { + Deprecation::trigger( + 'doctrine/collections', + 'https://github.com/doctrine/collections/pull/389', + 'Calling %s() is deprecated. Use %s::orderings() instead.', + __METHOD__, + self::class, + ); + + return array_map( + static fn (Order $ordering): string => $ordering->value, + $this->orderings, + ); + } + + /** + * Gets the current orderings of this Criteria. + * + * @return array + */ + public function orderings(): array + { + return $this->orderings; + } + + /** + * Sets the ordering of the result of this Criteria. + * + * Keys are field and values are the order, being a valid Order enum case. + * + * @see Order::Ascending + * @see Order::Descending + * + * @param array $orderings + * + * @return $this + */ + public function orderBy(array $orderings) + { + $method = __METHOD__; + $this->orderings = array_map( + static function (string|Order $ordering) use ($method): Order { + if ($ordering instanceof Order) { + return $ordering; + } + + static $triggered = false; + + if (! $triggered) { + Deprecation::trigger( + 'doctrine/collections', + 'https://github.com/doctrine/collections/pull/389', + 'Passing non-Order enum values to %s() is deprecated. Pass Order enum values instead.', + $method, + ); + } + + $triggered = true; + + return strtoupper($ordering) === Order::Ascending->value ? Order::Ascending : Order::Descending; + }, + $orderings, + ); + + return $this; + } + + /** + * Gets the current first result option of this Criteria. + * + * @return int|null + */ + public function getFirstResult() + { + return $this->firstResult; + } + + /** + * Set the number of first result that this Criteria should return. + * + * @param int|null $firstResult The value to set. + * + * @return $this + */ + public function setFirstResult(int|null $firstResult) + { + if ($firstResult === null) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/collections', + 'https://github.com/doctrine/collections/pull/311', + 'Passing null to %s() is deprecated, pass 0 instead.', + __METHOD__, + ); + } + + $this->firstResult = $firstResult; + + return $this; + } + + /** + * Gets maxResults. + * + * @return int|null + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Sets maxResults. + * + * @param int|null $maxResults The value to set. + * + * @return $this + */ + public function setMaxResults(int|null $maxResults) + { + $this->maxResults = $maxResults; + + return $this; + } +} diff --git a/vendor/doctrine/collections/src/Expr/ClosureExpressionVisitor.php b/vendor/doctrine/collections/src/Expr/ClosureExpressionVisitor.php new file mode 100644 index 0000000..4319b9b --- /dev/null +++ b/vendor/doctrine/collections/src/Expr/ClosureExpressionVisitor.php @@ -0,0 +1,222 @@ +$accessor(); + } + } + + if (preg_match('/^is[A-Z]+/', $field) === 1 && method_exists($object, $field)) { + return $object->$field(); + } + + // __call should be triggered for get. + $accessor = $accessors[0] . $field; + + if (method_exists($object, '__call')) { + return $object->$accessor(); + } + + if ($object instanceof ArrayAccess) { + return $object[$field]; + } + + if (isset($object->$field)) { + return $object->$field; + } + + // camelcase field name to support different variable naming conventions + $ccField = preg_replace_callback('/_(.?)/', static fn ($matches) => strtoupper((string) $matches[1]), $field); + + foreach ($accessors as $accessor) { + $accessor .= $ccField; + + if (method_exists($object, $accessor)) { + return $object->$accessor(); + } + } + + return $object->$field; + } + + /** + * Helper for sorting arrays of objects based on multiple fields + orientations. + * + * @return Closure + */ + public static function sortByField(string $name, int $orientation = 1, Closure|null $next = null) + { + if (! $next) { + $next = static fn (): int => 0; + } + + return static function ($a, $b) use ($name, $next, $orientation): int { + $aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name); + + $bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name); + + if ($aValue === $bValue) { + return $next($a, $b); + } + + return ($aValue > $bValue ? 1 : -1) * $orientation; + }; + } + + /** + * {@inheritDoc} + */ + public function walkComparison(Comparison $comparison) + { + $field = $comparison->getField(); + $value = $comparison->getValue()->getValue(); + + return match ($comparison->getOperator()) { + Comparison::EQ => static fn ($object): bool => self::getObjectFieldValue($object, $field) === $value, + Comparison::NEQ => static fn ($object): bool => self::getObjectFieldValue($object, $field) !== $value, + Comparison::LT => static fn ($object): bool => self::getObjectFieldValue($object, $field) < $value, + Comparison::LTE => static fn ($object): bool => self::getObjectFieldValue($object, $field) <= $value, + Comparison::GT => static fn ($object): bool => self::getObjectFieldValue($object, $field) > $value, + Comparison::GTE => static fn ($object): bool => self::getObjectFieldValue($object, $field) >= $value, + Comparison::IN => static function ($object) use ($field, $value): bool { + $fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field); + + return in_array($fieldValue, $value, is_scalar($fieldValue)); + }, + Comparison::NIN => static function ($object) use ($field, $value): bool { + $fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field); + + return ! in_array($fieldValue, $value, is_scalar($fieldValue)); + }, + Comparison::CONTAINS => static fn ($object): bool => str_contains((string) self::getObjectFieldValue($object, $field), (string) $value), + Comparison::MEMBER_OF => static function ($object) use ($field, $value): bool { + $fieldValues = ClosureExpressionVisitor::getObjectFieldValue($object, $field); + + if (! is_array($fieldValues)) { + $fieldValues = iterator_to_array($fieldValues); + } + + return in_array($value, $fieldValues, true); + }, + Comparison::STARTS_WITH => static fn ($object): bool => str_starts_with((string) self::getObjectFieldValue($object, $field), (string) $value), + Comparison::ENDS_WITH => static fn ($object): bool => str_ends_with((string) self::getObjectFieldValue($object, $field), (string) $value), + default => throw new RuntimeException('Unknown comparison operator: ' . $comparison->getOperator()), + }; + } + + /** + * {@inheritDoc} + */ + public function walkValue(Value $value) + { + return $value->getValue(); + } + + /** + * {@inheritDoc} + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = []; + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + return match ($expr->getType()) { + CompositeExpression::TYPE_AND => $this->andExpressions($expressionList), + CompositeExpression::TYPE_OR => $this->orExpressions($expressionList), + CompositeExpression::TYPE_NOT => $this->notExpression($expressionList), + default => throw new RuntimeException('Unknown composite ' . $expr->getType()), + }; + } + + /** @param callable[] $expressions */ + private function andExpressions(array $expressions): Closure + { + return static function ($object) use ($expressions): bool { + foreach ($expressions as $expression) { + if (! $expression($object)) { + return false; + } + } + + return true; + }; + } + + /** @param callable[] $expressions */ + private function orExpressions(array $expressions): Closure + { + return static function ($object) use ($expressions): bool { + foreach ($expressions as $expression) { + if ($expression($object)) { + return true; + } + } + + return false; + }; + } + + /** @param callable[] $expressions */ + private function notExpression(array $expressions): Closure + { + return static fn ($object) => ! $expressions[0]($object); + } +} diff --git a/vendor/doctrine/collections/src/Expr/Comparison.php b/vendor/doctrine/collections/src/Expr/Comparison.php new file mode 100644 index 0000000..f1ea07f --- /dev/null +++ b/vendor/doctrine/collections/src/Expr/Comparison.php @@ -0,0 +1,62 @@ +'; + final public const LT = '<'; + final public const LTE = '<='; + final public const GT = '>'; + final public const GTE = '>='; + final public const IS = '='; // no difference with EQ + final public const IN = 'IN'; + final public const NIN = 'NIN'; + final public const CONTAINS = 'CONTAINS'; + final public const MEMBER_OF = 'MEMBER_OF'; + final public const STARTS_WITH = 'STARTS_WITH'; + final public const ENDS_WITH = 'ENDS_WITH'; + + private readonly Value $value; + + public function __construct(private readonly string $field, private readonly string $op, mixed $value) + { + if (! ($value instanceof Value)) { + $value = new Value($value); + } + + $this->value = $value; + } + + /** @return string */ + public function getField() + { + return $this->field; + } + + /** @return Value */ + public function getValue() + { + return $this->value; + } + + /** @return string */ + public function getOperator() + { + return $this->op; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkComparison($this); + } +} diff --git a/vendor/doctrine/collections/src/Expr/CompositeExpression.php b/vendor/doctrine/collections/src/Expr/CompositeExpression.php new file mode 100644 index 0000000..b7b4544 --- /dev/null +++ b/vendor/doctrine/collections/src/Expr/CompositeExpression.php @@ -0,0 +1,70 @@ + */ + private array $expressions = []; + + /** + * @param Expression[] $expressions + * + * @throws RuntimeException + */ + public function __construct(private readonly string $type, array $expressions) + { + foreach ($expressions as $expr) { + if ($expr instanceof Value) { + throw new RuntimeException('Values are not supported expressions as children of and/or expressions.'); + } + + if (! ($expr instanceof Expression)) { + throw new RuntimeException('No expression given to CompositeExpression.'); + } + + $this->expressions[] = $expr; + } + + if ($type === self::TYPE_NOT && count($this->expressions) !== 1) { + throw new RuntimeException('Not expression only allows one expression as child.'); + } + } + + /** + * Returns the list of expressions nested in this composite. + * + * @return list + */ + public function getExpressionList() + { + return $this->expressions; + } + + /** @return string */ + public function getType() + { + return $this->type; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkCompositeExpression($this); + } +} diff --git a/vendor/doctrine/collections/src/Expr/Expression.php b/vendor/doctrine/collections/src/Expr/Expression.php new file mode 100644 index 0000000..70ad45f --- /dev/null +++ b/vendor/doctrine/collections/src/Expr/Expression.php @@ -0,0 +1,14 @@ + $this->walkComparison($expr), + $expr instanceof Value => $this->walkValue($expr), + $expr instanceof CompositeExpression => $this->walkCompositeExpression($expr), + default => throw new RuntimeException('Unknown Expression ' . $expr::class), + }; + } +} diff --git a/vendor/doctrine/collections/src/Expr/Value.php b/vendor/doctrine/collections/src/Expr/Value.php new file mode 100644 index 0000000..776770d --- /dev/null +++ b/vendor/doctrine/collections/src/Expr/Value.php @@ -0,0 +1,26 @@ +value; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkValue($this); + } +} diff --git a/vendor/doctrine/collections/src/ExpressionBuilder.php b/vendor/doctrine/collections/src/ExpressionBuilder.php new file mode 100644 index 0000000..fc25e3a --- /dev/null +++ b/vendor/doctrine/collections/src/ExpressionBuilder.php @@ -0,0 +1,123 @@ + + */ +interface ReadableCollection extends Countable, IteratorAggregate +{ + /** + * Checks whether an element is contained in the collection. + * This is an O(n) operation, where n is the size of the collection. + * + * @param mixed $element The element to search for. + * @psalm-param TMaybeContained $element + * + * @return bool TRUE if the collection contains the element, FALSE otherwise. + * @psalm-return (TMaybeContained is T ? bool : false) + * + * @template TMaybeContained + */ + public function contains(mixed $element); + + /** + * Checks whether the collection is empty (contains no elements). + * + * @return bool TRUE if the collection is empty, FALSE otherwise. + */ + public function isEmpty(); + + /** + * Checks whether the collection contains an element with the specified key/index. + * + * @param string|int $key The key/index to check for. + * @psalm-param TKey $key + * + * @return bool TRUE if the collection contains an element with the specified key/index, + * FALSE otherwise. + */ + public function containsKey(string|int $key); + + /** + * Gets the element at the specified key/index. + * + * @param string|int $key The key/index of the element to retrieve. + * @psalm-param TKey $key + * + * @return mixed + * @psalm-return T|null + */ + public function get(string|int $key); + + /** + * Gets all keys/indices of the collection. + * + * @return int[]|string[] The keys/indices of the collection, in the order of the corresponding + * elements in the collection. + * @psalm-return list + */ + public function getKeys(); + + /** + * Gets all values of the collection. + * + * @return mixed[] The values of all elements in the collection, in the + * order they appear in the collection. + * @psalm-return list + */ + public function getValues(); + + /** + * Gets a native PHP array representation of the collection. + * + * @return mixed[] + * @psalm-return array + */ + public function toArray(); + + /** + * Sets the internal iterator to the first element in the collection and returns this element. + * + * @return mixed + * @psalm-return T|false + */ + public function first(); + + /** + * Sets the internal iterator to the last element in the collection and returns this element. + * + * @return mixed + * @psalm-return T|false + */ + public function last(); + + /** + * Gets the key/index of the element at the current iterator position. + * + * @return int|string|null + * @psalm-return TKey|null + */ + public function key(); + + /** + * Gets the element of the collection at the current iterator position. + * + * @return mixed + * @psalm-return T|false + */ + public function current(); + + /** + * Moves the internal iterator position to the next element and returns this element. + * + * @return mixed + * @psalm-return T|false + */ + public function next(); + + /** + * Extracts a slice of $length elements starting at position $offset from the Collection. + * + * If $length is null it returns all elements from $offset to the end of the Collection. + * Keys have to be preserved by this method. Calling this method will only return the + * selected slice and NOT change the elements contained in the collection slice is called on. + * + * @param int $offset The offset to start from. + * @param int|null $length The maximum number of elements to return, or null for no limit. + * + * @return mixed[] + * @psalm-return array + */ + public function slice(int $offset, int|null $length = null); + + /** + * Tests for the existence of an element that satisfies the given predicate. + * + * @param Closure $p The predicate. + * @psalm-param Closure(TKey, T):bool $p + * + * @return bool TRUE if the predicate is TRUE for at least one element, FALSE otherwise. + */ + public function exists(Closure $p); + + /** + * Returns all the elements of this collection that satisfy the predicate p. + * The order of the elements is preserved. + * + * @param Closure $p The predicate used for filtering. + * @psalm-param Closure(T, TKey):bool $p + * + * @return ReadableCollection A collection with the results of the filter operation. + * @psalm-return ReadableCollection + */ + public function filter(Closure $p); + + /** + * Applies the given function to each element in the collection and returns + * a new collection with the elements returned by the function. + * + * @psalm-param Closure(T):U $func + * + * @return ReadableCollection + * @psalm-return ReadableCollection + * + * @psalm-template U + */ + public function map(Closure $func); + + /** + * Partitions this collection in two collections according to a predicate. + * Keys are preserved in the resulting collections. + * + * @param Closure $p The predicate on which to partition. + * @psalm-param Closure(TKey, T):bool $p + * + * @return ReadableCollection[] An array with two elements. The first element contains the collection + * of elements where the predicate returned TRUE, the second element + * contains the collection of elements where the predicate returned FALSE. + * @psalm-return array{0: ReadableCollection, 1: ReadableCollection} + */ + public function partition(Closure $p); + + /** + * Tests whether the given predicate p holds for all elements of this collection. + * + * @param Closure $p The predicate. + * @psalm-param Closure(TKey, T):bool $p + * + * @return bool TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + public function forAll(Closure $p); + + /** + * Gets the index/key of a given element. The comparison of two elements is strict, + * that means not only the value but also the type must match. + * For objects this means reference equality. + * + * @param mixed $element The element to search for. + * @psalm-param TMaybeContained $element + * + * @return int|string|bool The key/index of the element or FALSE if the element was not found. + * @psalm-return (TMaybeContained is T ? TKey|false : false) + * + * @template TMaybeContained + */ + public function indexOf(mixed $element); + + /** + * Returns the first element of this collection that satisfies the predicate p. + * + * @param Closure $p The predicate. + * @psalm-param Closure(TKey, T):bool $p + * + * @return mixed The first element respecting the predicate, + * null if no element respects the predicate. + * @psalm-return T|null + */ + public function findFirst(Closure $p); + + /** + * Applies iteratively the given function to each element in the collection, + * so as to reduce the collection to a single value. + * + * @psalm-param Closure(TReturn|TInitial, T):TReturn $func + * @psalm-param TInitial $initial + * + * @return mixed + * @psalm-return TReturn|TInitial + * + * @psalm-template TReturn + * @psalm-template TInitial + */ + public function reduce(Closure $func, mixed $initial = null); +} diff --git a/vendor/doctrine/collections/src/Selectable.php b/vendor/doctrine/collections/src/Selectable.php new file mode 100644 index 0000000..5fa87cb --- /dev/null +++ b/vendor/doctrine/collections/src/Selectable.php @@ -0,0 +1,32 @@ +&Selectable + * @psalm-return ReadableCollection&Selectable + */ + public function matching(Criteria $criteria); +} -- cgit v1.2.3