summaryrefslogtreecommitdiffstats
path: root/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr')
-rw-r--r--vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php269
-rw-r--r--vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php74
-rw-r--r--vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php67
-rw-r--r--vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php12
-rw-r--r--vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php59
-rw-r--r--vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php29
6 files changed, 510 insertions, 0 deletions
diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php
new file mode 100644
index 0000000..c10b8d5
--- /dev/null
+++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php
@@ -0,0 +1,269 @@
+<?php
+
+namespace Doctrine\Common\Collections\Expr;
+
+use ArrayAccess;
+use Closure;
+use RuntimeException;
+
+use function explode;
+use function in_array;
+use function is_array;
+use function is_scalar;
+use function iterator_to_array;
+use function method_exists;
+use function preg_match;
+use function preg_replace_callback;
+use function strlen;
+use function strpos;
+use function strtoupper;
+use function substr;
+
+/**
+ * Walks an expression graph and turns it into a PHP closure.
+ *
+ * This closure can be used with {@Collection#filter()} and is used internally
+ * by {@ArrayCollection#select()}.
+ */
+class ClosureExpressionVisitor extends ExpressionVisitor
+{
+ /**
+ * Accesses the field of a given object. This field has to be public
+ * directly or indirectly (through an accessor get*, is*, or a magic
+ * method, __get, __call).
+ *
+ * @param object|mixed[] $object
+ * @param string $field
+ *
+ * @return mixed
+ */
+ public static function getObjectFieldValue($object, $field)
+ {
+ if (strpos($field, '.') !== false) {
+ [$field, $subField] = explode('.', $field, 2);
+ $object = self::getObjectFieldValue($object, $field);
+
+ return self::getObjectFieldValue($object, $subField);
+ }
+
+ if (is_array($object)) {
+ return $object[$field];
+ }
+
+ $accessors = ['get', 'is', ''];
+
+ foreach ($accessors as $accessor) {
+ $accessor .= $field;
+
+ if (method_exists($object, $accessor)) {
+ return $object->$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 function ($matches) {
+ return strtoupper($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.
+ *
+ * @param string $name
+ * @param int $orientation
+ *
+ * @return Closure
+ */
+ public static function sortByField($name, $orientation = 1, ?Closure $next = null)
+ {
+ if (! $next) {
+ $next = static function (): int {
+ return 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(); // shortcut for walkValue()
+
+ switch ($comparison->getOperator()) {
+ case Comparison::EQ:
+ return static function ($object) use ($field, $value): bool {
+ return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value;
+ };
+
+ case Comparison::NEQ:
+ return static function ($object) use ($field, $value): bool {
+ return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value;
+ };
+
+ case Comparison::LT:
+ return static function ($object) use ($field, $value): bool {
+ return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value;
+ };
+
+ case Comparison::LTE:
+ return static function ($object) use ($field, $value): bool {
+ return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value;
+ };
+
+ case Comparison::GT:
+ return static function ($object) use ($field, $value): bool {
+ return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value;
+ };
+
+ case Comparison::GTE:
+ return static function ($object) use ($field, $value): bool {
+ return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value;
+ };
+
+ case Comparison::IN:
+ return static function ($object) use ($field, $value): bool {
+ $fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
+
+ return in_array($fieldValue, $value, is_scalar($fieldValue));
+ };
+
+ case Comparison::NIN:
+ return static function ($object) use ($field, $value): bool {
+ $fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
+
+ return ! in_array($fieldValue, $value, is_scalar($fieldValue));
+ };
+
+ case Comparison::CONTAINS:
+ return static function ($object) use ($field, $value) {
+ return strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value) !== false;
+ };
+
+ case Comparison::MEMBER_OF:
+ return 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);
+ };
+
+ case Comparison::STARTS_WITH:
+ return static function ($object) use ($field, $value): bool {
+ return strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value) === 0;
+ };
+
+ case Comparison::ENDS_WITH:
+ return static function ($object) use ($field, $value): bool {
+ return $value === substr(ClosureExpressionVisitor::getObjectFieldValue($object, $field), -strlen($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);
+ }
+
+ switch ($expr->getType()) {
+ case CompositeExpression::TYPE_AND:
+ return $this->andExpressions($expressionList);
+
+ case CompositeExpression::TYPE_OR:
+ return $this->orExpressions($expressionList);
+
+ default:
+ throw new RuntimeException('Unknown composite ' . $expr->getType());
+ }
+ }
+
+ /** @param callable[] $expressions */
+ private function andExpressions(array $expressions): callable
+ {
+ 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): callable
+ {
+ return static function ($object) use ($expressions): bool {
+ foreach ($expressions as $expression) {
+ if ($expression($object)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+ }
+}
diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php
new file mode 100644
index 0000000..6762b8b
--- /dev/null
+++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Doctrine\Common\Collections\Expr;
+
+/**
+ * Comparison of a field with a value by the given operator.
+ */
+class Comparison implements Expression
+{
+ public const EQ = '=';
+ public const NEQ = '<>';
+ public const LT = '<';
+ public const LTE = '<=';
+ public const GT = '>';
+ public const GTE = '>=';
+ public const IS = '='; // no difference with EQ
+ public const IN = 'IN';
+ public const NIN = 'NIN';
+ public const CONTAINS = 'CONTAINS';
+ public const MEMBER_OF = 'MEMBER_OF';
+ public const STARTS_WITH = 'STARTS_WITH';
+ public const ENDS_WITH = 'ENDS_WITH';
+
+ /** @var string */
+ private $field;
+
+ /** @var string */
+ private $op;
+
+ /** @var Value */
+ private $value;
+
+ /**
+ * @param string $field
+ * @param string $operator
+ * @param mixed $value
+ */
+ public function __construct($field, $operator, $value)
+ {
+ if (! ($value instanceof Value)) {
+ $value = new Value($value);
+ }
+
+ $this->field = $field;
+ $this->op = $operator;
+ $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/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php
new file mode 100644
index 0000000..174b21b
--- /dev/null
+++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Doctrine\Common\Collections\Expr;
+
+use RuntimeException;
+
+/**
+ * Expression of Expressions combined by AND or OR operation.
+ */
+class CompositeExpression implements Expression
+{
+ public const TYPE_AND = 'AND';
+ public const TYPE_OR = 'OR';
+
+ /** @var string */
+ private $type;
+
+ /** @var Expression[] */
+ private $expressions = [];
+
+ /**
+ * @param string $type
+ * @param mixed[] $expressions
+ *
+ * @throws RuntimeException
+ */
+ public function __construct($type, array $expressions)
+ {
+ $this->type = $type;
+
+ 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;
+ }
+ }
+
+ /**
+ * Returns the list of expressions nested in this composite.
+ *
+ * @return Expression[]
+ */
+ 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/lib/Doctrine/Common/Collections/Expr/Expression.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php
new file mode 100644
index 0000000..566d6d5
--- /dev/null
+++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Doctrine\Common\Collections\Expr;
+
+/**
+ * Expression for the {@link Selectable} interface.
+ */
+interface Expression
+{
+ /** @return mixed */
+ public function visit(ExpressionVisitor $visitor);
+}
diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php
new file mode 100644
index 0000000..72bb153
--- /dev/null
+++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Doctrine\Common\Collections\Expr;
+
+use RuntimeException;
+
+use function get_class;
+
+/**
+ * An Expression visitor walks a graph of expressions and turns them into a
+ * query for the underlying implementation.
+ */
+abstract class ExpressionVisitor
+{
+ /**
+ * Converts a comparison expression into the target query language output.
+ *
+ * @return mixed
+ */
+ abstract public function walkComparison(Comparison $comparison);
+
+ /**
+ * Converts a value expression into the target query language part.
+ *
+ * @return mixed
+ */
+ abstract public function walkValue(Value $value);
+
+ /**
+ * Converts a composite expression into the target query language output.
+ *
+ * @return mixed
+ */
+ abstract public function walkCompositeExpression(CompositeExpression $expr);
+
+ /**
+ * Dispatches walking an expression to the appropriate handler.
+ *
+ * @return mixed
+ *
+ * @throws RuntimeException
+ */
+ public function dispatch(Expression $expr)
+ {
+ switch (true) {
+ case $expr instanceof Comparison:
+ return $this->walkComparison($expr);
+
+ case $expr instanceof Value:
+ return $this->walkValue($expr);
+
+ case $expr instanceof CompositeExpression:
+ return $this->walkCompositeExpression($expr);
+
+ default:
+ throw new RuntimeException('Unknown Expression ' . get_class($expr));
+ }
+ }
+}
diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php
new file mode 100644
index 0000000..693d345
--- /dev/null
+++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Doctrine\Common\Collections\Expr;
+
+class Value implements Expression
+{
+ /** @var mixed */
+ private $value;
+
+ /** @param mixed $value */
+ public function __construct($value)
+ {
+ $this->value = $value;
+ }
+
+ /** @return mixed */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function visit(ExpressionVisitor $visitor)
+ {
+ return $visitor->walkValue($this);
+ }
+}