diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7c32f5c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[{*.php,*.json,*.md,*.html,*.css,*.js,*.yml}] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore index 50b321e..175efb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ vendor composer.lock +.phpunit.cache .phpunit.result.cache +.php-cs-fixer.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..1e91a46 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,25 @@ +ignoreDotFiles(false) + ->ignoreVCSIgnored(true) + ->in(__DIR__) +; + +return (new PhpCsFixer\Config()) + ->setRiskyAllowed(true) + ->setRules([ + '@PHP74Migration' => true, + '@PHP74Migration:risky' => true, + '@PHPUnit100Migration:risky' => true, + '@PhpCsFixer' => true, + '@PhpCsFixer:risky' => true, + 'yoda_style' => true, + 'general_phpdoc_annotation_remove' => ['annotations' => ['expectedDeprecation']], // one should use PHPUnit built-in method instead + 'modernize_strpos' => true, // needs PHP 8+ or polyfill + 'no_useless_concat_operator' => false, // TODO switch back on when the `src/Console/Application.php` no longer needs the concat + ]) + ->setFinder($finder) +; diff --git a/composer.json b/composer.json index b0cd9cd..1ec1b18 100644 --- a/composer.json +++ b/composer.json @@ -1,54 +1,58 @@ { - "name": "symftony/xpression", - "authors": [ - { - "name": "Anthony", - "email": "symftony@gmail.com" + "name": "symftony/xpression", + "authors": [ + { + "name": "Anthony", + "email": "symftony@gmail.com" + } + ], + "type": "library", + "description": "Xpression is a simple PHP implementation of Specification pattern", + "keywords": [ + "dsl", + "doctrine", + "orm", + "mongodb", + "collections", + "specification", + "expression", + "parser", + "lexer", + "query", + "builder", + "query builder" + ], + "homepage": "https://github.com/symftony/Xpression", + "require": { + "php": "^8.0", + "doctrine/lexer": "^1.0" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/orm": "^2.4.0", + "phpspec/prophecy": "^1.18", + "phpunit/phpunit": "^9.6", + "friendsofphp/php-cs-fixer": "^3.41", + "phpstan/phpstan": "^1.10" + }, + "scripts": { + "test": "vendor/bin/phpunit", + "cs": "vendor/bin/php-cs-fixer fix", + "stan": "vendor/bin/phpstan analyse" + }, + "suggest": { + "doctrine/collections": "If you want filter an ArrayCollection", + "doctrine/orm": "If you want filter an ORM query builder" + }, + "license": "MIT", + "autoload": { + "psr-4": { + "Symftony\\Xpression\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\Symftony\\Xpression\\": "tests/" + } } - ], - "type": "library", - "description": "Xpression is a simple PHP implementation of Specification pattern", - "keywords": [ - "dsl", - "doctrine", - "orm", - "mongodb", - "collections", - "specification", - "expression", - "parser", - "lexer", - "query", - "builder", - "query builder" - ], - "homepage": "https://github.com/symftony/Xpression", - "require": { - "php": "^8.0", - "doctrine/lexer": "^1.0" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/orm": "^2.4.0", - "phpspec/prophecy": "^1.18", - "phpunit/phpunit": "^9.6" - }, - "scripts": { - "test": "vendor/bin/phpunit" - }, - "suggest": { - "doctrine/collections": "If you want filter an ArrayCollection", - "doctrine/orm": "If you want filter an ORM query builder" - }, - "license": "MIT", - "autoload": { - "psr-4": { - "Symftony\\Xpression\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Tests\\Symftony\\Xpression\\": "tests/" - } - } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..7aa9876 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,8 @@ +parameters: + level: 6 + paths: + - src + ignoreErrors: + - '/Method .*::(in|notIn|andX|nandX|orX|norX|xorX)\(\) has parameter .* with no value type specified in iterable type array\./' + - '/Method Symftony\\Xpression\\Parser::getNextToken\(\) return type has no value type specified in iterable type array\./' + - '/Method Symftony\\Xpression\\Lexer::get(Non)?CatchablePatterns\(\) return type has no value type specified in iterable type array\./' diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f865b8e..f0b84b3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,20 +1,11 @@ - - - src - - - src/Exception - - + bootstrap="vendor/autoload.php"> + @@ -23,4 +14,12 @@ tests + + + src + + + src/Exception + + diff --git a/src/Bridge/Doctrine/Common/ExpressionBuilderAdapter.php b/src/Bridge/Doctrine/Common/ExpressionBuilderAdapter.php index 1869139..f14593a 100644 --- a/src/Bridge/Doctrine/Common/ExpressionBuilderAdapter.php +++ b/src/Bridge/Doctrine/Common/ExpressionBuilderAdapter.php @@ -1,20 +1,19 @@ expressionBuilder = $expressionBuilder; - } + public function __construct( + private ExpressionBuilder $expressionBuilder + ) {} public function getSupportedTokenType(): int { @@ -26,60 +25,60 @@ public function parameter(mixed $value, bool $isValue = false): mixed return $value; } - public function string($value): mixed + public function string(mixed $value): mixed { return $value; } - public function isNull(string $field) + public function isNull(string $field): mixed { return $this->expressionBuilder->isNull($field); } - public function eq(string $field, mixed $value) + public function eq(string $field, mixed $value): mixed { return $this->expressionBuilder->eq($field, $value); } - public function neq(string $field, mixed $value) + public function neq(string $field, mixed $value): mixed { return $this->expressionBuilder->neq($field, $value); } - public function gt(string $field, mixed $value) + public function gt(string $field, mixed $value): mixed { return $this->expressionBuilder->gt($field, $value); } - public function gte(string $field, mixed $value) + public function gte(string $field, mixed $value): mixed { return $this->expressionBuilder->gte($field, $value); } - public function lt(string $field, mixed $value) + public function lt(string $field, mixed $value): mixed { return $this->expressionBuilder->lt($field, $value); } - public function lte(string $field, mixed $value) + public function lte(string $field, mixed $value): mixed { return $this->expressionBuilder->lte($field, $value); } - public function in(string $field, array $values) + public function in(string $field, array $values): mixed { return $this->expressionBuilder->in($field, $values); } - public function notIn(string $field, array $values) + public function notIn(string $field, array $values): mixed { return $this->expressionBuilder->notIn($field, $values); } /** - * /!\ Contains operator appear only in doctrine/common v1.1 /!\ + * /!\ Contains operator appear only in doctrine/common v1.1 /!\. */ - public function contains(string $field, mixed $value) + public function contains(string $field, mixed $value): mixed { if (!method_exists($this->expressionBuilder, 'contains')) { throw new UnsupportedExpressionTypeException('contains'); @@ -88,32 +87,32 @@ public function contains(string $field, mixed $value) return $this->expressionBuilder->contains($field, $value); } - public function notContains(string $field, mixed $value) + public function notContains(string $field, mixed $value): mixed { throw new UnsupportedExpressionTypeException('notContains'); } - public function andX(array $expressions) + public function andX(array $expressions): mixed { - return call_user_func_array([$this->expressionBuilder, 'andX'], $expressions); + return $this->expressionBuilder->andX(...$expressions); } - public function nandX(array $expressions) + public function nandX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('nandX'); } - public function orX(array $expressions) + public function orX(array $expressions): mixed { - return call_user_func_array([$this->expressionBuilder, 'orX'], $expressions); + return $this->expressionBuilder->orX(...$expressions); } - public function norX(array $expressions) + public function norX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('norX'); } - public function xorX(array $expressions) + public function xorX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('xorX'); } diff --git a/src/Bridge/Doctrine/ORM/ExprAdapter.php b/src/Bridge/Doctrine/ORM/ExprAdapter.php index 3a1bf59..1e1ac12 100644 --- a/src/Bridge/Doctrine/ORM/ExprAdapter.php +++ b/src/Bridge/Doctrine/ORM/ExprAdapter.php @@ -1,10 +1,12 @@ expr->literal($value) : $value; } - public function string($value): mixed + public function string(mixed $value): mixed { return $this->expr->literal($value); } - public function isNull(string $field) + public function isNull(string $field): mixed { return $this->expr->isNull($field); } - public function eq(string $field, mixed $value) + public function eq(string $field, mixed $value): mixed { return $this->expr->eq($field, $value); } - public function neq(string $field, mixed $value) + public function neq(string $field, mixed $value): mixed { return $this->expr->neq($field, $value); } - public function gt(string $field, mixed $value) + public function gt(string $field, mixed $value): mixed { return $this->expr->gt($field, $value); } - public function gte(string $field, mixed $value) + public function gte(string $field, mixed $value): mixed { return $this->expr->gte($field, $value); } - public function lt(string $field, mixed $value) + public function lt(string $field, mixed $value): mixed { return $this->expr->lt($field, $value); } - public function lte(string $field, mixed $value) + public function lte(string $field, mixed $value): mixed { return $this->expr->lte($field, $value); } - public function in($field, array $values) + public function in(string $field, array $values): mixed { return $this->expr->in($field, $values); } - public function notIn($field, array $values) + public function notIn(string $field, array $values): mixed { return $this->expr->notIn($field, $values); } - public function contains(string $field, mixed $value) + public function contains(string $field, mixed $value): mixed { return $this->expr->like($field, $value); } - public function notContains(string $field, mixed $value) + public function notContains(string $field, mixed $value): mixed { return $this->expr->notLike($field, $value); } - public function andX(array $expressions) + public function andX(array $expressions): mixed { - return call_user_func_array([$this->expr, 'andX'], $expressions); + return $this->expr->andX(...$expressions); } - public function nandX(array $expressions) + public function nandX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('nandX'); } - public function orX(array $expressions) + public function orX(array $expressions): mixed { - return call_user_func_array([$this->expr, 'orX'], $expressions); + return $this->expr->orX(...$expressions); } - public function norX(array $expressions) + public function norX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('norX'); } - public function xorX(array $expressions) + public function xorX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('xorX'); } diff --git a/src/Bridge/MongoDB/ExprBuilder.php b/src/Bridge/MongoDB/ExprBuilder.php index f41130f..209f9e2 100644 --- a/src/Bridge/MongoDB/ExprBuilder.php +++ b/src/Bridge/MongoDB/ExprBuilder.php @@ -1,5 +1,7 @@ null]; } - public function eq(string $field, mixed $value) + public function eq(string $field, mixed $value): mixed { return [$field => ['$eq' => $value]]; } - public function neq(string $field, mixed $value) + public function neq(string $field, mixed $value): mixed { return [$field => ['$ne' => $value]]; } - public function gt(string $field, mixed $value) + public function gt(string $field, mixed $value): mixed { return [$field => ['$gt' => $value]]; } - public function gte(string $field, mixed $value) + public function gte(string $field, mixed $value): mixed { return [$field => ['$gte' => $value]]; } - public function lt(string $field, mixed $value) + public function lt(string $field, mixed $value): mixed { return [$field => ['$lt' => $value]]; } - public function lte(string $field, mixed $value) + public function lte(string $field, mixed $value): mixed { return [$field => ['$lte' => $value]]; } - public function in($field, array $values) + public function in(string $field, array $values): mixed { return [$field => ['$in' => $values]]; } - public function notIn($field, array $values) + public function notIn(string $field, array $values): mixed { return [$field => ['$nin' => $values]]; } - public function contains(string $field, mixed $value) + public function contains(string $field, mixed $value): mixed { return [$field => ['$regex' => $value]]; } - public function notContains(string $field, mixed $value) + public function notContains(string $field, mixed $value): mixed { return ['$not' => $this->contains($field, $value)]; } - public function andX(array $expressions) + public function andX(array $expressions): mixed { return ['$and' => $expressions]; } // Not A AND B = Not A OR Not B - public function nandX(array $expressions) + public function nandX(array $expressions): mixed { - return $this->orX(array_map(function ($expression) { - return ['$not' => $expression]; - }, $expressions)); + return $this->orX(array_map(static fn ($expression) => ['$not' => $expression], $expressions)); } - public function orX(array $expressions) + public function orX(array $expressions): mixed { return ['$or' => $expressions]; } - public function norX(array $expressions) + public function norX(array $expressions): mixed { return ['$nor' => $expressions]; } - public function xorX(array $expressions) + public function xorX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('xorX'); } diff --git a/src/Exception/Expr/UnsupportedExpressionTypeException.php b/src/Exception/Expr/UnsupportedExpressionTypeException.php index 1f4064b..09996d1 100644 --- a/src/Exception/Expr/UnsupportedExpressionTypeException.php +++ b/src/Exception/Expr/UnsupportedExpressionTypeException.php @@ -1,29 +1,20 @@ expressionType = $expressionType; } - /** - * @return string - */ - public function getExpressionType() + public function getExpressionType(): string { return $this->expressionType; } diff --git a/src/Exception/Lexer/LexerException.php b/src/Exception/Lexer/LexerException.php index cf51842..8eca046 100644 --- a/src/Exception/Lexer/LexerException.php +++ b/src/Exception/Lexer/LexerException.php @@ -1,7 +1,7 @@ tokenType = $tokenType; } - /** - * @return string - */ - public function getTokenType() + public function getTokenType(): string { return $this->tokenType; } diff --git a/src/Exception/Parser/ForbiddenTokenException.php b/src/Exception/Parser/ForbiddenTokenException.php index ba679c7..05d4231 100644 --- a/src/Exception/Parser/ForbiddenTokenException.php +++ b/src/Exception/Parser/ForbiddenTokenException.php @@ -1,23 +1,22 @@ allowedTokenTypes = $allowedTokenTypes; } /** - * @return string + * @return string[] */ - public function getAllowedTokenTypes() + public function getAllowedTokenTypes(): array { return $this->allowedTokenTypes; } diff --git a/src/Exception/Parser/InvalidExpressionException.php b/src/Exception/Parser/InvalidExpressionException.php index 5de241c..0a21833 100644 --- a/src/Exception/Parser/InvalidExpressionException.php +++ b/src/Exception/Parser/InvalidExpressionException.php @@ -1,31 +1,21 @@ input = $input; + public function __construct( + private string $input, + string $message = '', + int $code = 0, + \Exception $previous = null, + ) { + parent::__construct('' !== $message ? $message : 'Invalid expression.', $code, $previous); } - /** - * @return string - */ - public function getInput() + public function getInput(): string { return $this->input; } diff --git a/src/Exception/Parser/ParserException.php b/src/Exception/Parser/ParserException.php index e17525f..a4ff1e3 100644 --- a/src/Exception/Parser/ParserException.php +++ b/src/Exception/Parser/ParserException.php @@ -1,7 +1,7 @@ token = $token; } /** - * @return array + * @return string[] */ - public function getToken() + public function getToken(): array { return $this->token; } diff --git a/src/Exception/Parser/UnexpectedTokenException.php b/src/Exception/Parser/UnexpectedTokenException.php index c0970be..85581af 100644 --- a/src/Exception/Parser/UnexpectedTokenException.php +++ b/src/Exception/Parser/UnexpectedTokenException.php @@ -1,23 +1,22 @@ expectedTokenTypes = $expectedTokenTypes; } /** - * @return string + * @return string[] */ - public function getExpectedTokenTypes() + public function getExpectedTokenTypes(): array { return $this->expectedTokenTypes; } diff --git a/src/Exception/Parser/UnknowCompositeTypeException.php b/src/Exception/Parser/UnknowCompositeTypeException.php deleted file mode 100644 index d9e2a36..0000000 --- a/src/Exception/Parser/UnknowCompositeTypeException.php +++ /dev/null @@ -1,32 +0,0 @@ -unknownType = $unknownType; - } - - /** - * @return string - */ - public function getUnknownType() - { - return $this->unknownType; - } -} diff --git a/src/Exception/Parser/UnknownCompositeTypeException.php b/src/Exception/Parser/UnknownCompositeTypeException.php new file mode 100644 index 0000000..a815bb4 --- /dev/null +++ b/src/Exception/Parser/UnknownCompositeTypeException.php @@ -0,0 +1,22 @@ +unknownType; + } +} diff --git a/src/Exception/Parser/UnsupportedTokenTypeException.php b/src/Exception/Parser/UnsupportedTokenTypeException.php index cdf0f55..48900c0 100644 --- a/src/Exception/Parser/UnsupportedTokenTypeException.php +++ b/src/Exception/Parser/UnsupportedTokenTypeException.php @@ -1,23 +1,22 @@ supportedTokenTypes = $supportedTokenTypes; } /** - * @return string + * @return string[] */ - public function getSupportedTokenTypes() + public function getSupportedTokenTypes(): array { return $this->supportedTokenTypes; } diff --git a/src/Expr/ClosureExpressionBuilder.php b/src/Expr/ClosureExpressionBuilder.php index 5a10bfc..91a550e 100644 --- a/src/Expr/ClosureExpressionBuilder.php +++ b/src/Expr/ClosureExpressionBuilder.php @@ -1,20 +1,16 @@ $accessor(); + return $object->{$accessor}(); } // __call should be triggered for get. - $accessor = $accessors[0] . $field; + $accessor = $accessors[0].$field; if (method_exists($object, '__call')) { - return $object->$accessor(); + return $object->{$accessor}(); } if ($object instanceof \ArrayAccess) { return $object[$field]; } - if (isset($object->$field)) { - return $object->$field; + if (isset($object->{$field})) { + return $object->{$field}; } // camelcase field name to support different variable naming conventions - $ccField = preg_replace_callback('/_(.?)/', function ($matches) { - return strtoupper($matches[1]); - }, $field); + $ccField = preg_replace_callback('/_(.?)/', static fn ($matches) => strtoupper($matches[1]), $field); foreach ($accessors as $accessor) { $accessor .= $ccField; - if (!method_exists($object, $accessor)) { continue; } - return $object->$accessor(); + return $object->{$accessor}(); } - return $object->$field; + return $object->{$field}; } - /** - * @return int - */ public function getSupportedTokenType(): int { return Lexer::T_ALL; } /** - * @param $value - * @param bool $isValue - * - * @return mixed + * @param bool $isValue + * @param mixed $value */ public function parameter($value, $isValue = false): mixed { return $value; } - public function string($value): mixed + public function string(mixed $value): mixed { return $value; } - public function isNull($field) + public function isNull(string $field): mixed { - return function ($object) use ($field) { - return ClosureExpressionBuilder::getObjectFieldValue($object, $field) === null; - }; + return static fn ($object) => null === ClosureExpressionBuilder::getObjectFieldValue($object, $field); } - public function eq(string $field, mixed $value) + public function eq(string $field, mixed $value): mixed { - return function ($object) use ($field, $value) { - return ClosureExpressionBuilder::getObjectFieldValue($object, $field) === $value; - }; + return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) === $value; } - public function neq(string $field, mixed $value) + public function neq(string $field, mixed $value): mixed { - return function ($object) use ($field, $value) { - return ClosureExpressionBuilder::getObjectFieldValue($object, $field) !== $value; - }; + return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) !== $value; } - public function gt(string $field, mixed $value) + public function gt(string $field, mixed $value): mixed { - return function ($object) use ($field, $value) { - return ClosureExpressionBuilder::getObjectFieldValue($object, $field) > $value; - }; + return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) > $value; } - public function gte(string $field, mixed $value) + public function gte(string $field, mixed $value): mixed { - return function ($object) use ($field, $value) { - return ClosureExpressionBuilder::getObjectFieldValue($object, $field) >= $value; - }; + return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) >= $value; } - public function lt(string $field, mixed $value) + public function lt(string $field, mixed $value): mixed { - return function ($object) use ($field, $value) { - return ClosureExpressionBuilder::getObjectFieldValue($object, $field) < $value; - }; + return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) < $value; } - public function lte(string $field, mixed $value) + public function lte(string $field, mixed $value): mixed { - return function ($object) use ($field, $value) { - return ClosureExpressionBuilder::getObjectFieldValue($object, $field) <= $value; - }; + return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) <= $value; } - public function in($field, array $values) + public function in(string $field, array $values): mixed { - return function ($object) use ($field, $values) { - return in_array(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $values); - }; + return static fn ($object) => \in_array(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $values, true); } - public function notIn($field, array $values) + public function notIn(string $field, array $values): mixed { - return function ($object) use ($field, $values) { - return !in_array(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $values); - }; + return static fn ($object) => !\in_array(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $values, true); } - public function contains(string $field, mixed $value) + public function contains(string $field, mixed $value): mixed { - return function ($object) use ($field, $value) { - return false !== strpos(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $value); - }; + return static fn ($object) => str_contains(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $value); } - public function notContains(string $field, mixed $value) + public function notContains(string $field, mixed $value): mixed { - $self = $this; - return function ($object) use ($self, $field, $value) { - $contains = $self->contains($field, $value); - return !$contains($object); - }; + return static fn ($object) => !str_contains(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $value); } - public function andX(array $expressions) + public function andX(array $expressions): mixed { - return function ($object) use ($expressions) { + return static function ($object) use ($expressions) { foreach ($expressions as $expression) { if (!$expression($object)) { return false; @@ -180,18 +144,16 @@ public function andX(array $expressions) }; } - public function nandX(array $expressions) + public function nandX(array $expressions): mixed { $self = $this; - return function ($object) use ($self, $expressions) { - $andX = $self->andX($expressions); - return !$andX($object); - }; + + return static fn ($object) => !$self->andX($expressions)($object); } - public function orX(array $expressions) + public function orX(array $expressions): mixed { - return function ($object) use ($expressions) { + return static function ($object) use ($expressions) { foreach ($expressions as $expression) { if ($expression($object)) { return true; @@ -202,27 +164,26 @@ public function orX(array $expressions) }; } - public function norX(array $expressions) + public function norX(array $expressions): mixed { $self = $this; - return function ($object) use ($self, $expressions) { - $orX = $self->orX($expressions); - return !$orX($object); - }; + + return static fn ($object) => !$self->orX($expressions)($object); } - public function xorX(array $expressions) + public function xorX(array $expressions): mixed { - return function ($object) use ($expressions) { + return static function ($object) use ($expressions) { $result = 0; foreach ($expressions as $expression) { if ($expression($object)) { - $result++; + ++$result; } } - $countExpressions = count($expressions); - return $result === 1 | (2 < $countExpressions & $result === $countExpressions); + $countExpressions = \count($expressions); + + return 1 === $result | (2 < $countExpressions & $result === $countExpressions); }; } } diff --git a/src/Expr/ExpressionBuilderInterface.php b/src/Expr/ExpressionBuilderInterface.php index 19ca0bb..38e0743 100644 --- a/src/Expr/ExpressionBuilderInterface.php +++ b/src/Expr/ExpressionBuilderInterface.php @@ -1,50 +1,49 @@ comparisonHtmlBuilder = $comparisonHtmlBuilder ?: function ($field, $operator, $value) { - return sprintf('
%s %s %s
', $field, $operator, $value); - }; - $this->compositeHtmlBuilder = $compositeHtmlBuilder ?: function (array $expressions, $type) { - return str_replace( - ['{type}', '{expressions}'], - [$type, implode('', $expressions)], - '
{type}{expressions}
' - ); - }; + $this->comparisonHtmlBuilder = $comparisonHtmlBuilder ?: static fn ($field, $operator, $value) => sprintf('
%s %s %s
', $field, $operator, $value); + $this->compositeHtmlBuilder = $compositeHtmlBuilder ?: static fn (array $expressions, $type) => str_replace( + ['{type}', '{expressions}'], + [$type, implode('', $expressions)], + '
{type}{expressions}
' + ); } public function getSupportedTokenType(): int @@ -52,88 +46,88 @@ public function parameter(mixed $value, bool $isValue = false): mixed return $value; } - public function string($value): mixed + public function string(mixed $value): mixed { - return '"' . $value . '"'; + return '"'.$value.'"'; } - public function isNull($field) + public function isNull(string $field): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, 'is', 'null']); + return ($this->comparisonHtmlBuilder)($field, 'is', 'null'); } - public function eq(string $field, mixed $value) + public function eq(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, '=', $value]); + return ($this->comparisonHtmlBuilder)($field, '=', $value); } - public function neq(string $field, mixed $value) + public function neq(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, '≠', $value]); + return ($this->comparisonHtmlBuilder)($field, '≠', $value); } - public function gt(string $field, mixed $value) + public function gt(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, '>', $value]); + return ($this->comparisonHtmlBuilder)($field, '>', $value); } - public function gte(string $field, mixed $value) + public function gte(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, '≥', $value]); + return ($this->comparisonHtmlBuilder)($field, '≥', $value); } - public function lt(string $field, mixed $value) + public function lt(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, '<', $value]); + return ($this->comparisonHtmlBuilder)($field, '<', $value); } - public function lte(string $field, mixed $value) + public function lte(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, '≤', $value]); + return ($this->comparisonHtmlBuilder)($field, '≤', $value); } - public function in($field, array $values) + public function in(string $field, array $values): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, 'value in', implode(', ', $values)]); + return ($this->comparisonHtmlBuilder)($field, 'value in', implode(', ', $values)); } - public function notIn($field, array $values) + public function notIn(string $field, array $values): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, 'value not in', implode(', ', $values)]); + return ($this->comparisonHtmlBuilder)($field, 'value not in', implode(', ', $values)); } - public function contains(string $field, mixed $value) + public function contains(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, 'contains', $value]); + return ($this->comparisonHtmlBuilder)($field, 'contains', $value); } - public function notContains(string $field, mixed $value) + public function notContains(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, [$field, 'notContains', $value]); + return ($this->comparisonHtmlBuilder)($field, 'notContains', $value); } - public function andX(array $expressions) + public function andX(array $expressions): mixed { - return call_user_func_array($this->compositeHtmlBuilder, [$expressions, 'and']); + return ($this->compositeHtmlBuilder)($expressions, 'and'); } - public function nandX(array $expressions) + public function nandX(array $expressions): mixed { - return call_user_func_array($this->compositeHtmlBuilder, [$expressions, 'not-and']); + return ($this->compositeHtmlBuilder)($expressions, 'not-and'); } - public function orX(array $expressions) + public function orX(array $expressions): mixed { - return call_user_func_array($this->compositeHtmlBuilder, [$expressions, 'or']); + return ($this->compositeHtmlBuilder)($expressions, 'or'); } - public function norX(array $expressions) + public function norX(array $expressions): mixed { - return call_user_func_array($this->compositeHtmlBuilder, [$expressions, 'not-or']); + return ($this->compositeHtmlBuilder)($expressions, 'not-or'); } - public function xorX(array $expressions) + public function xorX(array $expressions): mixed { - return call_user_func_array($this->compositeHtmlBuilder, [$expressions, 'exclusive-or']); + return ($this->compositeHtmlBuilder)($expressions, 'exclusive-or'); } } diff --git a/src/Expr/MapperExpressionBuilder.php b/src/Expr/MapperExpressionBuilder.php index 5e4c5c1..f983da8 100644 --- a/src/Expr/MapperExpressionBuilder.php +++ b/src/Expr/MapperExpressionBuilder.php @@ -1,22 +1,18 @@ expressionBuilder = $expressionBuilder; - $this->fieldMapping = $fieldMapping; - } + public function __construct( + private ExpressionBuilderInterface $expressionBuilder, + private array $fieldMapping = [], + ) {} public function getSupportedTokenType(): int { @@ -28,105 +24,105 @@ public function parameter(mixed $value, bool $isValue = false): mixed return $this->expressionBuilder->parameter($value, $isValue); } - public function string($value): mixed + public function string(mixed $value): mixed { return $this->expressionBuilder->string($value); } - public function isNull($field) + public function isNull(string $field): mixed { return $this->expressionBuilder->isNull($this->mapField($field)); } - public function eq(string $field, mixed $value) + public function eq(string $field, mixed $value): mixed { return $this->expressionBuilder->eq($this->mapField($field), $value); } - public function neq(string $field, mixed $value) + public function neq(string $field, mixed $value): mixed { return $this->expressionBuilder->neq($this->mapField($field), $value); } - public function gt(string $field, mixed $value) + public function gt(string $field, mixed $value): mixed { return $this->expressionBuilder->gt($this->mapField($field), $value); } - public function gte(string $field, mixed $value) + public function gte(string $field, mixed $value): mixed { return $this->expressionBuilder->gte($this->mapField($field), $value); } - public function lt(string $field, mixed $value) + public function lt(string $field, mixed $value): mixed { return $this->expressionBuilder->lt($this->mapField($field), $value); } - public function lte(string $field, mixed $value) + public function lte(string $field, mixed $value): mixed { return $this->expressionBuilder->lte($this->mapField($field), $value); } - public function in($field, array $values) + public function in(string $field, array $values): mixed { return $this->expressionBuilder->in($this->mapField($field), $values); } - public function notIn($field, array $values) + public function notIn(string $field, array $values): mixed { return $this->expressionBuilder->notIn($this->mapField($field), $values); } - public function contains(string $field, mixed $value) + public function contains(string $field, mixed $value): mixed { return $this->expressionBuilder->contains($this->mapField($field), $value); } - public function notContains(string $field, mixed $value) + public function notContains(string $field, mixed $value): mixed { return $this->expressionBuilder->notContains($this->mapField($field), $value); } - public function andX(array $expressions) + public function andX(array $expressions): mixed { return $this->expressionBuilder->andX($expressions); } - public function nandX(array $expressions) + public function nandX(array $expressions): mixed { return $this->expressionBuilder->nandX($expressions); } - public function orX(array $expressions) + public function orX(array $expressions): mixed { return $this->expressionBuilder->orX($expressions); } - public function norX(array $expressions) + public function norX(array $expressions): mixed { return $this->expressionBuilder->norX($expressions); } - public function xorX(array $expressions) + public function xorX(array $expressions): mixed { return $this->expressionBuilder->xorX($expressions); } - private function mapField($field) + private function mapField(mixed $field): mixed { if ( - is_array($field) || - is_object($field) && !method_exists($field, '__toString') + \is_array($field) + || \is_object($field) && !method_exists($field, '__toString') ) { return $field; } - if (array_key_exists((string)$field, $this->fieldMapping)) { - return sprintf($this->fieldMapping[(string)$field], $field); + if (\array_key_exists((string) $field, $this->fieldMapping)) { + return sprintf($this->fieldMapping[(string) $field], $field); } - if (array_key_exists('*', $this->fieldMapping)) { + if (\array_key_exists('*', $this->fieldMapping)) { return sprintf($this->fieldMapping['*'], $field); } diff --git a/src/Lexer.php b/src/Lexer.php index 980b584..701d446 100644 --- a/src/Lexer.php +++ b/src/Lexer.php @@ -1,5 +1,7 @@ '; + } + if ($tokenType & self::T_GREATER_THAN_EQUALS) { + $tokenSyntax[] = '≥ or >='; + } + if ($tokenType & self::T_LOWER_THAN) { + $tokenSyntax[] = '<'; + } + if ($tokenType & self::T_LOWER_THAN_EQUALS) { + $tokenSyntax[] = '≤ or <='; + } + + // Composite operator + if ($tokenType & self::T_AND) { + $tokenSyntax[] = '&'; + } + if ($tokenType & self::T_NOT_AND) { + $tokenSyntax[] = '!&'; + } + if ($tokenType & self::T_OR) { + $tokenSyntax[] = '|'; + } + if ($tokenType & self::T_NOT_OR) { + $tokenSyntax[] = '!|'; + } + if ($tokenType & self::T_XOR) { + $tokenSyntax[] = '⊕ or ^|'; + } + + // Brace + if ($tokenType & self::T_OPEN_PARENTHESIS) { + $tokenSyntax[] = '('; + } + if ($tokenType & self::T_CLOSE_PARENTHESIS) { + $tokenSyntax[] = ')'; + } + if ($tokenType & self::T_OPEN_SQUARE_BRACKET) { + $tokenSyntax[] = '['; + } + if ($tokenType & self::T_NOT_OPEN_SQUARE_BRACKET) { + $tokenSyntax[] = '!['; + } + if ($tokenType & self::T_CLOSE_SQUARE_BRACKET) { + $tokenSyntax[] = ']'; + } + if ($tokenType & self::T_DOUBLE_OPEN_CURLY_BRACKET) { + $tokenSyntax[] = '{{'; + } + if ($tokenType & self::T_NOT_DOUBLE_OPEN_CURLY_BRACKET) { + $tokenSyntax[] = '!{{'; + } + if ($tokenType & self::T_DOUBLE_CLOSE_CURLY_BRACKET) { + $tokenSyntax[] = '}}'; + } + + return $tokenSyntax; + } /** * @return array */ protected function getCatchablePatterns() { - return array( + return [ "'(?:[^']|'')*'", // quoted strings '"(?:[^"]|"")*"', // quoted strings '\^\||⊕|!&|&|!\||\|', // Composite operator '≤|≥|≠|<=|>=|!=|<|>|=|\[|!\[|\]|!{{|{{|}}', // Comparison operator '[a-z_][a-z0-9_\.\-]*', // identifier or qualified name '(?:[+-]?[0-9]*(?:[\.][0-9]+)*)', // numbers - ); + ]; } /** @@ -67,10 +162,10 @@ protected function getCatchablePatterns() */ protected function getNonCatchablePatterns() { - return array( + return [ '\s+', '(.)', - ); + ]; } /** @@ -84,209 +179,156 @@ protected function getType(&$value) { switch (true) { // Punctuation - case ($value[0] === ','): + case ',' === $value[0]: $type = self::T_COMMA; + break; - // Recognize numeric values - case (is_numeric($value)): - if (strpos($value, '.') !== false || stripos($value, 'e') !== false) { - $value = (float)$value; + // Recognize numeric values + case is_numeric($value): + if (str_contains($value, '.') || false !== stripos($value, 'e')) { + $value = (float) $value; $type = self::T_FLOAT; + break; } - $value = (int)$value; + $value = (int) $value; $type = self::T_INTEGER; + break; - // Recognize quoted strings - case ($value[0] === '"'): - $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2)); + // Recognize quoted strings + case '"' === $value[0]: + $value = str_replace('""', '"', substr($value, 1, \strlen($value) - 2)); $type = self::T_STRING; + break; - case ($value[0] === "'"): - $value = str_replace("''", "'", substr($value, 1, strlen($value) - 2)); + + case "'" === $value[0]: + $value = str_replace("''", "'", substr($value, 1, \strlen($value) - 2)); $type = self::T_STRING; + break; - case (preg_match('/[a-z_][a-z0-9_]*/i', $value)): + case preg_match('/[a-z_][a-z0-9_]*/i', $value): $type = self::T_INPUT_PARAMETER; + break; - // Comparison operator - case ($value === '='): + // Comparison operator + case '=' === $value: $type = self::T_EQUALS; + break; - case ($value === '≠'): - case ($value === '!='): + + case '≠' === $value: + case '!=' === $value: $value = '≠'; $type = self::T_NOT_EQUALS; + break; - case ($value === '>'): + + case '>' === $value: $type = self::T_GREATER_THAN; + break; - case ($value === '>='): - case ($value === '≥'): + + case '>=' === $value: + case '≥' === $value: $value = '≥'; $type = self::T_GREATER_THAN_EQUALS; + break; - case ($value === '<'): + + case '<' === $value: $type = self::T_LOWER_THAN; + break; - case ($value === '<='): - case ($value === '≤'): + + case '<=' === $value: + case '≤' === $value: $value = '≤'; $type = self::T_LOWER_THAN_EQUALS; + break; - // Composite operator - case ($value === '&'): + // Composite operator + case '&' === $value: $type = self::T_AND; + break; - case ($value === '!&'): + + case '!&' === $value: $type = self::T_NOT_AND; + break; - case ($value === '|'): + + case '|' === $value: $type = self::T_OR; + break; - case ($value === '!|'): + + case '!|' === $value: $type = self::T_NOT_OR; + break; - case ($value === '^|'): - case ($value === '⊕'): + + case '^|' === $value: + case '⊕' === $value: $value = '⊕'; $type = self::T_XOR; + break; - // Brace - case ($value === '('): + // Brace + case '(' === $value: $type = self::T_OPEN_PARENTHESIS; + break; - case ($value === ')'): + + case ')' === $value: $type = self::T_CLOSE_PARENTHESIS; + break; - case ($value === '['): + + case '[' === $value: $type = self::T_OPEN_SQUARE_BRACKET; + break; - case ($value === '!['): + + case '![' === $value: $type = self::T_NOT_OPEN_SQUARE_BRACKET; + break; - case ($value === ']'): + + case ']' === $value: $type = self::T_CLOSE_SQUARE_BRACKET; + break; - case ($value === '{{'): + + case '{{' === $value: $type = self::T_DOUBLE_OPEN_CURLY_BRACKET; + break; - case ($value === '!{{'): + + case '!{{' === $value: $type = self::T_NOT_DOUBLE_OPEN_CURLY_BRACKET; + break; - case ($value === '}}'): + + case '}}' === $value: $type = self::T_DOUBLE_CLOSE_CURLY_BRACKET; + break; - // Default + // Default default: throw new UnknownTokenTypeException($value); } return $type; } - - /** - * @param $tokenType - * - * @return array - */ - public static function getTokenSyntax($tokenType) - { - $tokenSyntax = array(); - // Punctuation - if ($tokenType & self::T_COMMA) { - $tokenSyntax[] = ','; - } - - // Recognize numeric values - if ($tokenType & self::T_FLOAT) { - $tokenSyntax[] = 'simple float'; - } - if ($tokenType & self::T_INTEGER) { - $tokenSyntax[] = 'simple integer'; - } - if ($tokenType & self::T_INPUT_PARAMETER) { - $tokenSyntax[] = '/[a-z_][a-z0-9_]*/'; - } - - // Recognize quoted strings - if ($tokenType & self::T_STRING) { - $tokenSyntax[] = '"value" or \'value\''; - } - - // Comparison operator - if ($tokenType & self::T_EQUALS) { - $tokenSyntax[] = '='; - } - if ($tokenType & self::T_NOT_EQUALS) { - $tokenSyntax[] = '≠ or !='; - } - if ($tokenType & self::T_GREATER_THAN) { - $tokenSyntax[] = '>'; - } - if ($tokenType & self::T_GREATER_THAN_EQUALS) { - $tokenSyntax[] = '≥ or >='; - } - if ($tokenType & self::T_LOWER_THAN) { - $tokenSyntax[] = '<'; - } - if ($tokenType & self::T_LOWER_THAN_EQUALS) { - $tokenSyntax[] = '≤ or <='; - } - - // Composite operator - if ($tokenType & self::T_AND) { - $tokenSyntax[] = '&'; - } - if ($tokenType & self::T_NOT_AND) { - $tokenSyntax[] = '!&'; - } - if ($tokenType & self::T_OR) { - $tokenSyntax[] = '|'; - } - if ($tokenType & self::T_NOT_OR) { - $tokenSyntax[] = '!|'; - } - if ($tokenType & self::T_XOR) { - $tokenSyntax[] = '⊕ or ^|'; - } - - // Brace - if ($tokenType & self::T_OPEN_PARENTHESIS) { - $tokenSyntax[] = '('; - } - if ($tokenType & self::T_CLOSE_PARENTHESIS) { - $tokenSyntax[] = ')'; - } - if ($tokenType & self::T_OPEN_SQUARE_BRACKET) { - $tokenSyntax[] = '['; - } - if ($tokenType & self::T_NOT_OPEN_SQUARE_BRACKET) { - $tokenSyntax[] = '!['; - } - if ($tokenType & self::T_CLOSE_SQUARE_BRACKET) { - $tokenSyntax[] = ']'; - } - if ($tokenType & self::T_DOUBLE_OPEN_CURLY_BRACKET) { - $tokenSyntax[] = '{{'; - } - if ($tokenType & self::T_NOT_DOUBLE_OPEN_CURLY_BRACKET) { - $tokenSyntax[] = '!{{'; - } - if ($tokenType & self::T_DOUBLE_CLOSE_CURLY_BRACKET) { - $tokenSyntax[] = '}}'; - } - - return $tokenSyntax; - } } diff --git a/src/Parser.php b/src/Parser.php index ec89052..9d1cce7 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1,21 +1,27 @@ 15, @@ -25,16 +31,8 @@ class Parser Lexer::T_NOT_OR => 8, ]; - /** - * @var int Keep the lexer current index - */ - public int $lexerIndex = 0; - private Lexer $lexer; - /** - * @var ExpressionBuilderInterface - */ private ExpressionBuilderInterface $expressionBuilder; /** @@ -43,13 +41,10 @@ class Parser private int $allowedTokenType; /** - * @var int Bitwise of ExpressionBuilder supported operator. + * @var int bitwise of ExpressionBuilder supported operator */ private int $supportedTokenType; - /** - * @param ExpressionBuilderInterface $expressionBuilder - */ public function __construct(ExpressionBuilderInterface $expressionBuilder) { $this->lexer = new Lexer(); @@ -64,7 +59,7 @@ public function getAllowedTokenType(): int /** * @throws InvalidExpressionException */ - public function parse(string $input, ?int $allowedTokenType = null) + public function parse(string $input, ?int $allowedTokenType = null): mixed { $this->allowedTokenType = $allowedTokenType ?: Lexer::T_ALL; $this->supportedTokenType = $this->expressionBuilder->getSupportedTokenType(); @@ -82,15 +77,11 @@ public function parse(string $input, ?int $allowedTokenType = null) } /** - * @param null $previousExpression - * - * @return mixed - * * @throws ForbiddenTokenException * @throws UnexpectedTokenException * @throws UnsupportedTokenTypeException */ - private function getExpression($previousExpression = null) + private function getExpression(mixed $previousExpression = null): mixed { $expression = $previousExpression ?: null; $expectedTokenType = null !== $previousExpression ? Lexer::T_COMPOSITE : Lexer::T_OPEN_PARENTHESIS | Lexer::T_INPUT_PARAMETER; @@ -109,7 +100,7 @@ private function getExpression($previousExpression = null) while ($currentToken = $this->getNextToken()) { $currentTokenType = $currentToken['type']; $currentTokenIndex = $this->lexerIndex; - $this->lexerIndex++; + ++$this->lexerIndex; if (!($this->supportedTokenType & $currentTokenType)) { throw new UnsupportedTokenTypeException($currentToken, $this->lexer->getTokenSyntax($this->supportedTokenType)); @@ -128,7 +119,9 @@ private function getExpression($previousExpression = null) $expression = $this->getExpression(); $hasOpenParenthesis = true; $expectedTokenType = Lexer::T_CLOSE_PARENTHESIS; + break; + case Lexer::T_CLOSE_PARENTHESIS: if (!$hasOpenParenthesis) { $this->lexerIndex = $currentTokenIndex; @@ -139,16 +132,24 @@ private function getExpression($previousExpression = null) } $hasOpenParenthesis = false; $expectedTokenType = Lexer::T_COMPOSITE | Lexer::T_CLOSE_PARENTHESIS; + break; + case Lexer::T_COMMA: $expectedTokenType = Lexer::T_OPERANDE; + break; + case Lexer::T_INPUT_PARAMETER: $currentTokenValue = $this->expressionBuilder->parameter($currentToken['value'], null !== $comparisonFirstOperande); + + // no break case Lexer::T_STRING: if (!isset($currentTokenValue)) { $currentTokenValue = $this->expressionBuilder->string($currentToken['value']); } + + // no break case Lexer::T_INTEGER: case Lexer::T_FLOAT: if (!isset($currentTokenValue)) { @@ -158,13 +159,15 @@ private function getExpression($previousExpression = null) $comparisonFirstOperande = $currentTokenValue; $expectedTokenType = Lexer::T_COMPARISON; $currentTokenValue = null; + break; } - if (is_array($comparisonMultipleOperande)) { + if (\is_array($comparisonMultipleOperande)) { $comparisonMultipleOperande[] = $currentTokenValue; $expectedTokenType = Lexer::T_COMMA | Lexer::T_CLOSE_SQUARE_BRACKET; $currentTokenValue = null; + break; } @@ -172,71 +175,97 @@ private function getExpression($previousExpression = null) $containsValue = $currentTokenValue; $expectedTokenType = Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET; $currentTokenValue = null; + break; } - $expression = call_user_func_array([$this->expressionBuilder, $comparisonMethod], [$comparisonFirstOperande, $currentTokenValue]); + $expression = \call_user_func_array([$this->expressionBuilder, $comparisonMethod], [$comparisonFirstOperande, $currentTokenValue]); $comparisonFirstOperande = null; $comparisonMethod = null; $currentTokenValue = null; $expectedTokenType = Lexer::T_COMPOSITE | Lexer::T_CLOSE_PARENTHESIS; + break; + case Lexer::T_EQUALS: $comparisonMethod = 'eq'; $expectedTokenType = Lexer::T_OPERANDE; + break; + case Lexer::T_NOT_EQUALS: $comparisonMethod = 'neq'; $expectedTokenType = Lexer::T_OPERANDE; + break; + case Lexer::T_GREATER_THAN: $comparisonMethod = 'gt'; $expectedTokenType = Lexer::T_OPERANDE; + break; + case Lexer::T_GREATER_THAN_EQUALS: $comparisonMethod = 'gte'; $expectedTokenType = Lexer::T_OPERANDE; + break; + case Lexer::T_LOWER_THAN: $comparisonMethod = 'lt'; $expectedTokenType = Lexer::T_OPERANDE; + break; + case Lexer::T_LOWER_THAN_EQUALS: $comparisonMethod = 'lte'; $expectedTokenType = Lexer::T_OPERANDE; + break; + case Lexer::T_NOT_OPEN_SQUARE_BRACKET: $comparisonMethod = 'notIn'; $comparisonMultipleOperande = []; $expectedTokenType = Lexer::T_OPERANDE | Lexer::T_CLOSE_SQUARE_BRACKET; + break; + case Lexer::T_OPEN_SQUARE_BRACKET: $comparisonMethod = 'in'; $comparisonMultipleOperande = []; $expectedTokenType = Lexer::T_OPERANDE | Lexer::T_CLOSE_SQUARE_BRACKET; + break; + case Lexer::T_CLOSE_SQUARE_BRACKET: - $expression = call_user_func_array([$this->expressionBuilder, $comparisonMethod], [$comparisonFirstOperande, $comparisonMultipleOperande]); + $expression = \call_user_func_array([$this->expressionBuilder, $comparisonMethod], [$comparisonFirstOperande, $comparisonMultipleOperande]); $comparisonMethod = null; $comparisonFirstOperande = null; $comparisonMultipleOperande = false; $expectedTokenType = Lexer::T_COMPOSITE | Lexer::T_CLOSE_PARENTHESIS; + break; + case Lexer::T_DOUBLE_OPEN_CURLY_BRACKET: $comparisonMethod = 'contains'; $contains = true; $expectedTokenType = Lexer::T_OPERANDE | Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET; + break; + case Lexer::T_NOT_DOUBLE_OPEN_CURLY_BRACKET: $comparisonMethod = 'notContains'; $contains = true; $expectedTokenType = Lexer::T_OPERANDE | Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET; + break; + case Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET: - $expression = call_user_func_array([$this->expressionBuilder, $comparisonMethod], [$comparisonFirstOperande, $containsValue]); + $expression = \call_user_func_array([$this->expressionBuilder, $comparisonMethod], [$comparisonFirstOperande, $containsValue]); $comparisonMethod = null; $comparisonFirstOperande = null; $contains = false; $expectedTokenType = Lexer::T_COMPOSITE | Lexer::T_CLOSE_PARENTHESIS; + break; case Lexer::T_AND: @@ -251,6 +280,7 @@ private function getExpression($previousExpression = null) $compositeOperator = $currentTokenType; $tokenPrecedence = $currentTokenPrecedence; $expectedTokenType = Lexer::T_OPEN_PARENTHESIS | Lexer::T_INPUT_PARAMETER; + break; } @@ -261,6 +291,7 @@ private function getExpression($previousExpression = null) $compositeOperator = $currentTokenType; $tokenPrecedence = $currentTokenPrecedence; $expectedTokenType = Lexer::T_OPEN_PARENTHESIS | Lexer::T_INPUT_PARAMETER; + break; } @@ -269,6 +300,7 @@ private function getExpression($previousExpression = null) $this->lexer->resetPosition($currentTokenIndex); $this->lexer->moveNext(); $expression = $this->getExpression($expression); + break; } @@ -277,6 +309,7 @@ private function getExpression($previousExpression = null) $currentTokenPrecedence, $tokenPrecedence )); + default: throw new \LogicException(sprintf( 'Token mismatch. Expected token %s given %s', @@ -290,7 +323,7 @@ private function getExpression($previousExpression = null) $expressions[] = $expression; } - if (count($expressions) === 1) { + if (1 === \count($expressions)) { return $expressions[0]; } @@ -298,35 +331,40 @@ private function getExpression($previousExpression = null) } /** - * @param $type - * @param $expressions + * @param mixed $type + * @param mixed $expressions * * @return mixed * - * @throws UnknowCompositeTypeException + * @throws UnknownCompositeTypeException */ private function buildComposite($type, $expressions) { switch ($type) { case Lexer::T_AND: return $this->expressionBuilder->andX($expressions); + case Lexer::T_NOT_AND: return $this->expressionBuilder->nandX($expressions); + case Lexer::T_OR: return $this->expressionBuilder->orX($expressions); + case Lexer::T_NOT_OR: return $this->expressionBuilder->norX($expressions); + case Lexer::T_XOR: return $this->expressionBuilder->xorX($expressions); + default: - throw new UnknowCompositeTypeException($type); + throw new UnknownCompositeTypeException($type); } } /** - * @return array + * @return null|array */ - private function getNextToken() + private function getNextToken(): mixed { $this->lexer->moveNext(); diff --git a/src/QueryStringParser.php b/src/QueryStringParser.php index 02f2b10..e9396c6 100644 --- a/src/QueryStringParser.php +++ b/src/QueryStringParser.php @@ -1,10 +1,12 @@ $matches[1].urlencode($matches[2]).($matches[3] ?? ''), + urldecode($queryString) ); } } diff --git a/tests/Bridge/Doctrine/Common/ExpressionBuilderAdapterTest.php b/tests/Bridge/Doctrine/Common/ExpressionBuilderAdapterTest.php index bcb776d..0dbc2af 100644 --- a/tests/Bridge/Doctrine/Common/ExpressionBuilderAdapterTest.php +++ b/tests/Bridge/Doctrine/Common/ExpressionBuilderAdapterTest.php @@ -1,22 +1,29 @@ markTestSkipped('This test is run when you have "doctrine/orm" installed.'); + self::markTestSkipped('This test is run when you have "doctrine/orm" installed.'); } $this->expressionBuilderAdapter = new ExpressionBuilderAdapter(new ExpressionBuilder()); @@ -24,34 +31,32 @@ public function setUp(): void public function testParameter(): void { - $this->assertEquals('my_fake_data', $this->expressionBuilderAdapter->parameter('my_fake_data')); + self::assertSame('my_fake_data', $this->expressionBuilderAdapter->parameter('my_fake_data')); } public function testString(): void { - $this->assertEquals('my_fake_data', $this->expressionBuilderAdapter->string('my_fake_data')); + self::assertSame('my_fake_data', $this->expressionBuilderAdapter->string('my_fake_data')); } public function testIsNull(): void { - $isv0 = !defined('Doctrine\Common\Collections\Expr\Comparison::CONTAINS'); + $isv0 = !\defined('Doctrine\Common\Collections\Expr\Comparison::CONTAINS'); $field = 'fake_field'; - $this->assertEquals( + self::assertEquals( new Comparison('fake_field', $isv0 ? 'IS' : '=', null), $this->expressionBuilderAdapter->isNull($field) ); } - public function comparisonDataProvider(): array + public static function comparisonDataProvider(): iterable { if (!class_exists('Doctrine\Common\Collections\ExpressionBuilder')) { return []; } - return [ - ['field', 'value'], - ]; + yield ['field', 'value']; } /** @@ -59,7 +64,7 @@ public function comparisonDataProvider(): array */ public function testEq(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '=', 'value'), $this->expressionBuilderAdapter->eq($field, $value) ); @@ -70,7 +75,7 @@ public function testEq(string $field, string $value): void */ public function testNeq(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '<>', 'value'), $this->expressionBuilderAdapter->neq($field, $value) ); @@ -81,7 +86,7 @@ public function testNeq(string $field, string $value): void */ public function testGt(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '>', 'value'), $this->expressionBuilderAdapter->gt($field, $value) ); @@ -92,7 +97,7 @@ public function testGt(string $field, string $value): void */ public function testGte(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '>=', 'value'), $this->expressionBuilderAdapter->gte($field, $value) ); @@ -103,7 +108,7 @@ public function testGte(string $field, string $value): void */ public function testLt(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '<', 'value'), $this->expressionBuilderAdapter->lt($field, $value) ); @@ -114,7 +119,7 @@ public function testLt(string $field, string $value): void */ public function testLte(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '<=', 'value'), $this->expressionBuilderAdapter->lte($field, $value) ); @@ -125,7 +130,7 @@ public function testLte(string $field, string $value): void */ public function testIn(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', 'IN', ['value']), $this->expressionBuilderAdapter->in($field, [$value]) ); @@ -136,7 +141,7 @@ public function testIn(string $field, string $value): void */ public function testNotIn(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', 'NIN', ['value']), $this->expressionBuilderAdapter->notIn($field, [$value]) ); @@ -147,15 +152,16 @@ public function testNotIn(string $field, string $value): void */ public function testContains(string $field, string $value): void { + $expected = new Comparison('field', 'CONTAINS', 'value'); + if (!method_exists('Doctrine\Common\Collections\ExpressionBuilder', 'contains')) { $this->expectException(UnsupportedExpressionTypeException::class); $this->expectExceptionMessage('Unsupported expression type "contains".'); - - $this->assertNull($this->expressionBuilderAdapter->contains($field, $value)); + $expected = null; } - $this->assertEquals( - new Comparison('field', 'CONTAINS', 'value'), + self::assertEquals( + $expected, $this->expressionBuilderAdapter->contains($field, $value) ); } @@ -166,21 +172,20 @@ public function testContains(string $field, string $value): void public function testNotContains(string $field, string $value): void { $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "notContains".'); $this->expressionBuilderAdapter->notContains($field, $value); } - public function compositeDataProvider(): array + public static function compositeDataProvider(): iterable { if (!class_exists('Doctrine\Common\Collections\ExpressionBuilder')) { return []; } - return [ + yield [ [ - [ - new Comparison('fieldA', '=', 1), - new Comparison('fieldB', '>', 2), - ], + new Comparison('fieldA', '=', 1), + new Comparison('fieldB', '>', 2), ], ]; } @@ -190,7 +195,7 @@ public function compositeDataProvider(): array */ public function testAndX(array $expressions): void { - $this->assertEquals( + self::assertEquals( new CompositeExpression('AND', $expressions), $this->expressionBuilderAdapter->andX($expressions) ); @@ -202,6 +207,7 @@ public function testAndX(array $expressions): void public function testNandX(array $expressions): void { $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "nandX".'); $this->expressionBuilderAdapter->nandX($expressions); } @@ -210,7 +216,7 @@ public function testNandX(array $expressions): void */ public function testOrX(array $expressions): void { - $this->assertEquals( + self::assertEquals( new CompositeExpression('OR', $expressions), $this->expressionBuilderAdapter->orX($expressions) ); @@ -222,6 +228,7 @@ public function testOrX(array $expressions): void public function testNorX(array $expressions): void { $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "norX".'); $this->expressionBuilderAdapter->norX($expressions); } @@ -231,6 +238,7 @@ public function testNorX(array $expressions): void public function testXorX(array $expressions): void { $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "xorX".'); $this->expressionBuilderAdapter->xorX($expressions); } } diff --git a/tests/Bridge/Doctrine/Common/ParserTest.php b/tests/Bridge/Doctrine/Common/ParserTest.php index cc84976..d844a6d 100644 --- a/tests/Bridge/Doctrine/Common/ParserTest.php +++ b/tests/Bridge/Doctrine/Common/ParserTest.php @@ -1,17 +1,24 @@ markTestSkipped('This test is run when you have "doctrine/collection" installed.'); + self::markTestSkipped('This test is run when you have "doctrine/collection" installed.'); } $this->expressionBuilderAdapter = new ExpressionBuilderAdapter(new ExpressionBuilder()); $this->parser = new Parser($this->expressionBuilderAdapter); } - public function parseSuccessDataProvider(): array + public static function provideParserCases(): iterable { if (!class_exists('Doctrine\Common\Collections\ExpressionBuilder')) { return []; @@ -133,7 +140,7 @@ public function parseSuccessDataProvider(): array ]), ], - //Parenthesis + // Parenthesis [ '((fieldA=1))', new Comparison('fieldA', '=', 1), @@ -162,14 +169,14 @@ public function parseSuccessDataProvider(): array } /** - * @dataProvider parseSuccessDataProvider + * @dataProvider provideParserCases */ public function testParser(string $input, Expression $expectedExpression): void { - $this->assertEquals($expectedExpression, $this->parser->parse($input)); + self::assertEquals($expectedExpression, $this->parser->parse($input)); } - public function unsupportedExpressionTypeDataProvider(): array + public static function provideParserThrowUnsupportedExpressionTypeExceptionCases(): iterable { if (!class_exists('Doctrine\Common\Collections\ExpressionBuilder')) { return []; @@ -188,7 +195,7 @@ public function unsupportedExpressionTypeDataProvider(): array } /** - * @dataProvider unsupportedExpressionTypeDataProvider + * @dataProvider provideParserThrowUnsupportedExpressionTypeExceptionCases */ public function testParserThrowUnsupportedExpressionTypeException(string $input): void { diff --git a/tests/Bridge/Doctrine/ORM/ExprAdapterTest.php b/tests/Bridge/Doctrine/ORM/ExprAdapterTest.php index 3837acf..b47021e 100644 --- a/tests/Bridge/Doctrine/ORM/ExprAdapterTest.php +++ b/tests/Bridge/Doctrine/ORM/ExprAdapterTest.php @@ -1,24 +1,28 @@ markTestSkipped('This test is run when you have "doctrine/orm" installed.'); + self::markTestSkipped('This test is run when you have "doctrine/orm" installed.'); } $this->exprAdapter = new ExprAdapter(new Expr()); @@ -27,18 +31,16 @@ public function setUp(): void public function testIsNull(): void { $field = 'fake_field'; - $this->assertEquals('fake_field IS NULL', $this->exprAdapter->isNull($field)); + self::assertSame('fake_field IS NULL', $this->exprAdapter->isNull($field)); } - public function comparisonDataProvider() + public static function comparisonDataProvider(): iterable { if (!class_exists('Doctrine\ORM\Query\Expr')) { return []; } - return [ - ['field', 'value'], - ]; + yield ['field', 'value']; } /** @@ -46,7 +48,7 @@ public function comparisonDataProvider() */ public function testEq(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Comparison($field, Expr\Comparison::EQ, $value), $this->exprAdapter->eq($field, $value) ); @@ -57,7 +59,7 @@ public function testEq(string $field, string $value): void */ public function testNeq(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Comparison($field, Expr\Comparison::NEQ, $value), $this->exprAdapter->neq($field, $value) ); @@ -68,7 +70,7 @@ public function testNeq(string $field, string $value): void */ public function testGt(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Comparison($field, Expr\Comparison::GT, $value), $this->exprAdapter->gt($field, $value) ); @@ -79,7 +81,7 @@ public function testGt(string $field, string $value): void */ public function testGte(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Comparison($field, Expr\Comparison::GTE, $value), $this->exprAdapter->gte($field, $value) ); @@ -90,7 +92,7 @@ public function testGte(string $field, string $value): void */ public function testLt(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Comparison($field, Expr\Comparison::LT, $value), $this->exprAdapter->lt($field, $value) ); @@ -101,7 +103,7 @@ public function testLt(string $field, string $value): void */ public function testLte(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Comparison($field, Expr\Comparison::LTE, $value), $this->exprAdapter->lte($field, $value) ); @@ -112,7 +114,7 @@ public function testLte(string $field, string $value): void */ public function testIn(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Func('field IN', ["'value'"]), $this->exprAdapter->in($field, [$value]) ); @@ -123,7 +125,7 @@ public function testIn(string $field, string $value): void */ public function testNotIn(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Func('field NOT IN', ["'value'"]), $this->exprAdapter->notIn($field, [$value]) ); @@ -134,7 +136,7 @@ public function testNotIn(string $field, string $value): void */ public function testContains(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Comparison('field', 'LIKE', $value), $this->exprAdapter->contains($field, $value) ); @@ -145,40 +147,41 @@ public function testContains(string $field, string $value): void */ public function testNotContains(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Comparison('field', 'NOT LIKE', $value), $this->exprAdapter->notContains($field, $value) ); } - public function compositeDataProvider(): array + public static function compositeDataProvider(): iterable { if (!class_exists('Doctrine\ORM\Query\Expr')) { return []; } - return [ - [[ - new Expr\Comparison('field', Expr\Comparison::EQ, 'value'), - ]], - [[ - new Expr\Func('field', ['value']), - ]], - [[ - new Expr\Andx( - [ - new Expr\Comparison('field', Expr\Comparison::EQ, 'value'), - ] - ), - ]], - [[ - new Expr\Orx( - [ - new Expr\Comparison('field', Expr\Comparison::EQ, 'value'), - ] - ), - ]], - ]; + yield [[ + new Expr\Comparison('field', Expr\Comparison::EQ, 'value'), + ]]; + + yield [[ + new Expr\Func('field', ['value']), + ]]; + + yield [[ + new Expr\Andx( + [ + new Expr\Comparison('field', Expr\Comparison::EQ, 'value'), + ] + ), + ]]; + + yield [[ + new Expr\Orx( + [ + new Expr\Comparison('field', Expr\Comparison::EQ, 'value'), + ] + ), + ]]; } /** @@ -188,7 +191,7 @@ public function compositeDataProvider(): array */ public function testAndX(array $expressions): void { - $this->assertEquals( + self::assertEquals( new Expr\Andx($expressions), $this->exprAdapter->andX($expressions) ); @@ -202,6 +205,7 @@ public function testAndX(array $expressions): void public function testNandX(array $expressions): void { $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "nandX".'); $this->exprAdapter->nandX($expressions); } @@ -212,7 +216,7 @@ public function testNandX(array $expressions): void */ public function testOrX(array $expressions): void { - $this->assertEquals( + self::assertEquals( new Expr\Orx($expressions), $this->exprAdapter->orX($expressions) ); @@ -226,6 +230,7 @@ public function testOrX(array $expressions): void public function testNorX(array $expressions): void { $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "norX".'); $this->exprAdapter->norX($expressions); } @@ -237,6 +242,7 @@ public function testNorX(array $expressions): void public function testXorX(array $expressions): void { $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "xorX".'); $this->exprAdapter->xorX($expressions); } } diff --git a/tests/Bridge/Doctrine/ORM/ParserTest.php b/tests/Bridge/Doctrine/ORM/ParserTest.php index 3f0e782..da9c97b 100644 --- a/tests/Bridge/Doctrine/ORM/ParserTest.php +++ b/tests/Bridge/Doctrine/ORM/ParserTest.php @@ -1,196 +1,225 @@ markTestSkipped('This test is run when you have "doctrine/orm" installed.'); + self::markTestSkipped('This test is run when you have "doctrine/orm" installed.'); } $this->exprAdapter = new ExprAdapter(new Expr()); $this->parser = new Parser($this->exprAdapter); } - public function parseSuccessDataProvider(): array + public static function provideParserCases(): iterable { if (!class_exists('Doctrine\ORM\Query\Expr')) { return []; } - return [ - [ - 'fieldA=1', + yield [ + 'fieldA=1', + new Expr\Comparison('fieldA', '=', 1), + ]; + + yield [ + 'fieldA="string"', + new Expr\Comparison('fieldA', '=', (new Expr())->literal('string')), + ]; + + yield [ + 'fieldA≥1', + new Expr\Comparison('fieldA', '>=', 1), + ]; + + yield [ + 'fieldA>=1', + new Expr\Comparison('fieldA', '>=', 1), + ]; + + yield [ + 'fieldA≤1', + new Expr\Comparison('fieldA', '<=', 1), + ]; + + yield [ + 'fieldA<=1', + new Expr\Comparison('fieldA', '<=', 1), + ]; + + yield [ + 'fieldA≠1', + new Expr\Comparison('fieldA', '<>', 1), + ]; + + yield [ + 'fieldA!=1', + new Expr\Comparison('fieldA', '<>', 1), + ]; + + yield [ + 'fieldA[1,2]', + new Expr\Func('fieldA IN', [1, 2]), + ]; + + yield [ + 'fieldA![1,2]', + new Expr\Func('fieldA NOT IN', [1, 2]), + ]; + + yield [ + 'fieldA{{1}}', + new Expr\Comparison('fieldA', 'LIKE', 1), + ]; + + yield [ + 'fieldA!{{1}}', + new Expr\Comparison('fieldA', 'NOT LIKE', 1), + ]; + + yield [ + 'fieldA=1|fieldB=2|fieldC=3', + new Expr\Orx([ new Expr\Comparison('fieldA', '=', 1), - ], - [ - 'fieldA="string"', - new Expr\Comparison('fieldA', '=', (new Expr())->literal('string')), - ], - [ - 'fieldA≥1', - new Expr\Comparison('fieldA', '>=', 1), - ], - [ - 'fieldA>=1', - new Expr\Comparison('fieldA', '>=', 1), - ], - [ - 'fieldA≤1', - new Expr\Comparison('fieldA', '<=', 1), - ], - [ - 'fieldA<=1', - new Expr\Comparison('fieldA', '<=', 1), - ], - [ - 'fieldA≠1', - new Expr\Comparison('fieldA', '<>', 1), - ], - [ - 'fieldA!=1', - new Expr\Comparison('fieldA', '<>', 1), - ], - [ - 'fieldA[1,2]', - new Expr\Func('fieldA IN', [1, 2]), - ], - [ - 'fieldA![1,2]', - new Expr\Func('fieldA NOT IN', [1, 2]), - ], - [ - 'fieldA{{1}}', - new Expr\Comparison('fieldA', 'LIKE', 1), - ], - [ - 'fieldA!{{1}}', - new Expr\Comparison('fieldA', 'NOT LIKE', 1), - ], - [ - 'fieldA=1|fieldB=2|fieldC=3', - new Expr\Orx([ - new Expr\Comparison('fieldA', '=', 1), - new Expr\Comparison('fieldB', '=', 2), + new Expr\Comparison('fieldB', '=', 2), + new Expr\Comparison('fieldC', '=', 3), + ]), + ]; + + yield [ + 'fieldA=1&fieldB=2&fieldC=3', + new Expr\Andx([ + new Expr\Comparison('fieldA', '=', 1), + new Expr\Comparison('fieldB', '=', 2), + new Expr\Comparison('fieldC', '=', 3), + ]), + ]; + + // Precedences + yield [ + 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', + new Expr\Orx([ + new Expr\Comparison('fieldA', '=', 1), + new Expr\Comparison('fieldB', '=', 2), + new Expr\Andx([ new Expr\Comparison('fieldC', '=', 3), + new Expr\Comparison('fieldD', '=', 4), ]), - ], - [ - 'fieldA=1&fieldB=2&fieldC=3', + ]), + ]; + + yield [ + 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', + new Expr\Orx([ new Expr\Andx([ new Expr\Comparison('fieldA', '=', 1), new Expr\Comparison('fieldB', '=', 2), new Expr\Comparison('fieldC', '=', 3), ]), - ], + new Expr\Comparison('fieldD', '=', 4), + ]), + ]; - // Precedences - [ - 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', - new Expr\Orx([ + yield [ + 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', + new Expr\Orx([ + new Expr\Andx([ new Expr\Comparison('fieldA', '=', 1), new Expr\Comparison('fieldB', '=', 2), - new Expr\Andx([ - new Expr\Comparison('fieldC', '=', 3), - new Expr\Comparison('fieldD', '=', 4), - ]), ]), - ], - [ - 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', - new Expr\Orx([ - new Expr\Andx([ - new Expr\Comparison('fieldA', '=', 1), - new Expr\Comparison('fieldB', '=', 2), - new Expr\Comparison('fieldC', '=', 3), - ]), + new Expr\Andx([ + new Expr\Comparison('fieldC', '=', 3), new Expr\Comparison('fieldD', '=', 4), ]), - ], - [ - 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', + ]), + ]; + + // Parenthesis + yield [ + '((fieldA=1))', + new Expr\Comparison('fieldA', '=', 1), + ]; + + yield [ + '(fieldA=1|fieldB=2)&fieldC=3', + new Expr\Andx([ new Expr\Orx([ - new Expr\Andx([ - new Expr\Comparison('fieldA', '=', 1), - new Expr\Comparison('fieldB', '=', 2), - ]), - new Expr\Andx([ - new Expr\Comparison('fieldC', '=', 3), - new Expr\Comparison('fieldD', '=', 4), - ]), + new Expr\Comparison('fieldA', '=', 1), + new Expr\Comparison('fieldB', '=', 2), ]), - ], + new Expr\Comparison('fieldC', '=', 3), + ]), + ]; - //Parenthesis - [ - '((fieldA=1))', + yield [ + 'fieldA=1|(fieldB=2&fieldC=3)', + new Expr\Orx([ new Expr\Comparison('fieldA', '=', 1), - ], - [ - '(fieldA=1|fieldB=2)&fieldC=3', new Expr\Andx([ - new Expr\Orx([ - new Expr\Comparison('fieldA', '=', 1), - new Expr\Comparison('fieldB', '=', 2), - ]), + new Expr\Comparison('fieldB', '=', 2), new Expr\Comparison('fieldC', '=', 3), ]), - ], - [ - 'fieldA=1|(fieldB=2&fieldC=3)', - new Expr\Orx([ - new Expr\Comparison('fieldA', '=', 1), - new Expr\Andx([ - new Expr\Comparison('fieldB', '=', 2), - new Expr\Comparison('fieldC', '=', 3), - ]), - ]), - ], + ]), ]; } /** - * @dataProvider parseSuccessDataProvider + * @dataProvider provideParserCases + * + * @param mixed $expectedExpression */ public function testParser(string $input, $expectedExpression): void { - $this->assertEquals($expectedExpression, $this->parser->parse($input)); + self::assertEquals($expectedExpression, $this->parser->parse($input)); } - public function unsupportedExpressionTypeDataProvider(): array + public static function provideParserThrowUnsupportedExpressionTypeExceptionCases(): iterable { if (!class_exists('Doctrine\ORM\Query\Expr')) { return []; } - return [ - ['fieldA=1!|fieldB=2!|fieldC=3'], - ['fieldA=1^|fieldB=2'], - ['fieldA=1⊕fieldB=2'], - ['fieldA=1!&fieldB=2!&fieldC=3'], - ['fieldA=1&fieldB=2&fieldC=3!&fieldD=4'], - ['fieldA=1|fieldB=2|fieldC=3!|fieldD=4'], - ['fieldA=1|fieldB=2|fieldC=3⊕fieldD=4'], - ]; + yield ['fieldA=1!|fieldB=2!|fieldC=3']; + + yield ['fieldA=1^|fieldB=2']; + + yield ['fieldA=1⊕fieldB=2']; + + yield ['fieldA=1!&fieldB=2!&fieldC=3']; + + yield ['fieldA=1&fieldB=2&fieldC=3!&fieldD=4']; + + yield ['fieldA=1|fieldB=2|fieldC=3!|fieldD=4']; + + yield ['fieldA=1|fieldB=2|fieldC=3⊕fieldD=4']; } /** - * @dataProvider unsupportedExpressionTypeDataProvider + * @dataProvider provideParserThrowUnsupportedExpressionTypeExceptionCases */ public function testParserThrowUnsupportedExpressionTypeException(string $input): void { $this->expectException(InvalidExpressionException::class); + $this->expectExceptionMessage('Invalid expression.'); $this->parser->parse($input); } } diff --git a/tests/Bridge/MongoDB/ExprBuilderTest.php b/tests/Bridge/MongoDB/ExprBuilderTest.php index 3768dbd..f0aebd1 100644 --- a/tests/Bridge/MongoDB/ExprBuilderTest.php +++ b/tests/Bridge/MongoDB/ExprBuilderTest.php @@ -1,5 +1,7 @@ exprAdapter = new ExprBuilder(); } public function testGetSupportedTokenType(): void { - $this->assertEquals(Lexer::T_ALL - Lexer::T_XOR, $this->exprAdapter->getSupportedTokenType()); + self::assertSame(Lexer::T_ALL - Lexer::T_XOR, $this->exprAdapter->getSupportedTokenType()); } public function testIsNull(): void { $field = 'fake_field'; - $this->assertEquals(['fake_field' => null], $this->exprAdapter->isNull($field)); + self::assertSame(['fake_field' => null], $this->exprAdapter->isNull($field)); } public function testEq(): void { - $this->assertEquals( + self::assertSame( ['fieldA' => ['$eq' => 1]], $this->exprAdapter->eq('fieldA', 1) ); @@ -37,7 +44,7 @@ public function testEq(): void public function testNeq(): void { - $this->assertEquals( + self::assertSame( ['fieldA' => ['$ne' => 1]], $this->exprAdapter->neq('fieldA', 1) ); @@ -45,7 +52,7 @@ public function testNeq(): void public function testGt(): void { - $this->assertEquals( + self::assertSame( ['fieldA' => ['$gt' => 1]], $this->exprAdapter->gt('fieldA', 1) ); @@ -53,7 +60,7 @@ public function testGt(): void public function testGte(): void { - $this->assertEquals( + self::assertSame( ['fieldA' => ['$gte' => 1]], $this->exprAdapter->gte('fieldA', 1) ); @@ -61,7 +68,7 @@ public function testGte(): void public function testLt(): void { - $this->assertEquals( + self::assertSame( ['fieldA' => ['$lt' => 1]], $this->exprAdapter->lt('fieldA', 1) ); @@ -69,7 +76,7 @@ public function testLt(): void public function testLte(): void { - $this->assertEquals( + self::assertSame( ['fieldA' => ['$lte' => 1]], $this->exprAdapter->lte('fieldA', 1) ); @@ -77,7 +84,7 @@ public function testLte(): void public function testIn(): void { - $this->assertEquals( + self::assertSame( ['fieldA' => ['$in' => [1, 2]]], $this->exprAdapter->in('fieldA', [1, 2]) ); @@ -85,7 +92,7 @@ public function testIn(): void public function testNin(): void { - $this->assertEquals( + self::assertSame( ['fieldA' => ['$nin' => [1, 2]]], $this->exprAdapter->notIn('fieldA', [1, 2]) ); @@ -93,7 +100,7 @@ public function testNin(): void public function testAnd(): void { - $this->assertEquals( + self::assertSame( ['$and' => [['expression1'], ['expression2']]], $this->exprAdapter->andX([['expression1'], ['expression2']]) ); @@ -101,7 +108,7 @@ public function testAnd(): void public function testNand(): void { - $this->assertEquals( + self::assertSame( ['$or' => [['$not' => ['expression1']], ['$not' => ['expression2']]]], $this->exprAdapter->nandX([['expression1'], ['expression2']]) ); @@ -109,7 +116,7 @@ public function testNand(): void public function testOr(): void { - $this->assertEquals( + self::assertSame( ['$or' => [['expression1'], ['expression2']]], $this->exprAdapter->orX([['expression1'], ['expression2']]) ); @@ -117,7 +124,7 @@ public function testOr(): void public function testNor(): void { - $this->assertEquals( + self::assertSame( ['$nor' => [['expression1'], ['expression2']]], $this->exprAdapter->norX([['expression1'], ['expression2']]) ); @@ -126,6 +133,7 @@ public function testNor(): void public function testXorX(): void { $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "xorX".'); $this->exprAdapter->xorX([]); } } diff --git a/tests/Bridge/MongoDB/ParserTest.php b/tests/Bridge/MongoDB/ParserTest.php index 6e50f3a..eaeed7b 100644 --- a/tests/Bridge/MongoDB/ParserTest.php +++ b/tests/Bridge/MongoDB/ParserTest.php @@ -1,5 +1,7 @@ exprBuilder = new ExprBuilder(); $this->parser = new Parser($this->exprBuilder); } - public function parseSuccessDataProvider(): array + public static function provideParserCases(): iterable { - return [ - [ - 'fieldA=1', - [ - 'fieldA' => ['$eq' => 1], - ], - ], - [ - 'fieldA="string"', - [ - 'fieldA' => ['$eq' => 'string'], - ], - ], - [ - 'fieldA≥1', - [ - 'fieldA' => ['$gte' => 1], - ], - ], - [ - 'fieldA>=1', - [ - 'fieldA' => ['$gte' => 1], - ], - ], - [ - 'fieldA≤1', - [ - 'fieldA' => ['$lte' => 1], - ], - ], - [ - 'fieldA<=1', - [ - 'fieldA' => ['$lte' => 1], - ], - ], - [ - 'fieldA≠1', - [ - 'fieldA' => ['$ne' => 1], - ], - ], - [ - 'fieldA!=1', - [ - 'fieldA' => ['$ne' => 1], - ], - ], - [ - 'fieldA[1,2]', - [ - 'fieldA' => ['$in' => [1, 2]], - ], - ], - [ - 'fieldA![1,2]', - [ - 'fieldA' => ['$nin' => [1, 2]], - ], - ], - [ - 'fieldA{{1}}', - [ - 'fieldA' => ['$regex' => 1], - ], - ], - [ - 'fieldA!{{1}}', - [ - '$not' => ['fieldA' => ['$regex' => 1]], - ], - ], + yield [ + 'fieldA=1', + ['fieldA' => ['$eq' => 1]], + ]; + + yield [ + 'fieldA="string"', + ['fieldA' => ['$eq' => 'string']], + ]; + + yield [ + 'fieldA≥1', + ['fieldA' => ['$gte' => 1]], + ]; + + yield [ + 'fieldA>=1', + ['fieldA' => ['$gte' => 1]], + ]; + + yield [ + 'fieldA≤1', + ['fieldA' => ['$lte' => 1]], + ]; + + yield [ + 'fieldA<=1', + ['fieldA' => ['$lte' => 1]], + ]; + + yield [ + 'fieldA≠1', + ['fieldA' => ['$ne' => 1]], + ]; + + yield [ + 'fieldA!=1', + ['fieldA' => ['$ne' => 1]], + ]; + + yield [ + 'fieldA[1,2]', + ['fieldA' => ['$in' => [1, 2]]], + ]; + + yield [ + 'fieldA![1,2]', + ['fieldA' => ['$nin' => [1, 2]]], + ]; + + yield [ + 'fieldA{{1}}', + ['fieldA' => ['$regex' => 1]], + ]; + + yield [ + 'fieldA!{{1}}', + ['$not' => ['fieldA' => ['$regex' => 1]]], + ]; + + yield [ + 'fieldA=1|fieldB=2|fieldC=3', [ - 'fieldA=1|fieldB=2|fieldC=3', - [ - '$or' => [ - ['fieldA' => ['$eq' => 1]], - ['fieldB' => ['$eq' => 2]], - ['fieldC' => ['$eq' => 3]], - ], + '$or' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], + ['fieldC' => ['$eq' => 3]], ], ], + ]; + + yield [ + 'fieldA=1&fieldB=2&fieldC=3', [ - 'fieldA=1&fieldB=2&fieldC=3', - [ - '$and' => [ - ['fieldA' => ['$eq' => 1]], - ['fieldB' => ['$eq' => 2]], - ['fieldC' => ['$eq' => 3]], - ], + '$and' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], + ['fieldC' => ['$eq' => 3]], ], ], + ]; - // Precedences - [ - 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', - [ - '$or' => [ - ['fieldA' => ['$eq' => 1]], - ['fieldB' => ['$eq' => 2]], - [ - '$and' => [ - ['fieldC' => ['$eq' => 3]], - ['fieldD' => ['$eq' => 4]], - ], + // Precedences + yield [ + 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', + [ + '$or' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], + [ + '$and' => [ + ['fieldC' => ['$eq' => 3]], + ['fieldD' => ['$eq' => 4]], ], ], ], ], - [ - 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', - [ - '$or' => [ - [ - '$and' => [ - ['fieldA' => ['$eq' => 1]], - ['fieldB' => ['$eq' => 2]], - ['fieldC' => ['$eq' => 3]], - ], + ]; + + yield [ + 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', + [ + '$or' => [ + [ + '$and' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], + ['fieldC' => ['$eq' => 3]], ], - ['fieldD' => ['$eq' => 4]], ], + ['fieldD' => ['$eq' => 4]], ], ], - [ - 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', - [ - '$or' => [ - [ - '$and' => [ - ['fieldA' => ['$eq' => 1]], - ['fieldB' => ['$eq' => 2]], - ], + ]; + + yield [ + 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', + [ + '$or' => [ + [ + '$and' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], ], - [ - '$and' => [ - ['fieldC' => ['$eq' => 3]], - ['fieldD' => ['$eq' => 4]], - ], + ], + [ + '$and' => [ + ['fieldC' => ['$eq' => 3]], + ['fieldD' => ['$eq' => 4]], ], ], ], ], + ]; - //Parenthesis - [ - '((fieldA=1))', - ['fieldA' => ['$eq' => 1]], - ], - [ - '(fieldA=1|fieldB=2)&fieldC=3', - [ - '$and' => [ - [ - '$or' => [ - ['fieldA' => ['$eq' => 1]], - ['fieldB' => ['$eq' => 2]], - ], + // Parenthesis + yield [ + '((fieldA=1))', + ['fieldA' => ['$eq' => 1]], + ]; + + yield [ + '(fieldA=1|fieldB=2)&fieldC=3', + [ + '$and' => [ + [ + '$or' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], ], - ['fieldC' => ['$eq' => 3]], ], + ['fieldC' => ['$eq' => 3]], ], ], - [ - 'fieldA=1|(fieldB=2&fieldC=3)', - [ - '$or' => [ - ['fieldA' => ['$eq' => 1]], - [ - '$and' => [ - ['fieldB' => ['$eq' => 2]], - ['fieldC' => ['$eq' => 3]], - ], + ]; + + yield [ + 'fieldA=1|(fieldB=2&fieldC=3)', + [ + '$or' => [ + ['fieldA' => ['$eq' => 1]], + [ + '$and' => [ + ['fieldB' => ['$eq' => 2]], + ['fieldC' => ['$eq' => 3]], ], ], ], @@ -203,28 +201,31 @@ public function parseSuccessDataProvider(): array } /** - * @dataProvider parseSuccessDataProvider + * @dataProvider provideParserCases + * + * @param mixed $expectedExpression */ public function testParser(string $input, $expectedExpression): void { - $this->assertEquals($expectedExpression, $this->parser->parse($input)); + self::assertSame($expectedExpression, $this->parser->parse($input)); } - public function unsupportedExpressionTypeDataProvider(): array + public static function provideParserThrowUnsupportedExpressionTypeExceptionCases(): iterable { - return [ - ['fieldA=1^|fieldB=2'], - ['fieldA=1⊕fieldB=2'], - ['fieldA=1|fieldB=2|fieldC=3⊕fieldD=4'], - ]; + yield ['fieldA=1^|fieldB=2']; + + yield ['fieldA=1⊕fieldB=2']; + + yield ['fieldA=1|fieldB=2|fieldC=3⊕fieldD=4']; } /** - * @dataProvider unsupportedExpressionTypeDataProvider + * @dataProvider provideParserThrowUnsupportedExpressionTypeExceptionCases */ public function testParserThrowUnsupportedExpressionTypeException(string $input): void { $this->expectException(InvalidExpressionException::class); + $this->expectExceptionMessage('Invalid expression.'); $this->parser->parse($input); } } diff --git a/tests/Expr/ClosureExpressionBuilderTest.php b/tests/Expr/ClosureExpressionBuilderTest.php index e63294f..6430274 100644 --- a/tests/Expr/ClosureExpressionBuilderTest.php +++ b/tests/Expr/ClosureExpressionBuilderTest.php @@ -1,18 +1,25 @@ exampleData = [ 'field_null' => null, @@ -23,232 +30,235 @@ public function setUp(): void $this->closureExpressionBuilder = new ClosureExpressionBuilder(); } - public function getObjectFieldValueDataProvider(): array + public static function provideGetObjectFieldValueCases(): iterable { + yield [ + ['fake_key' => 'fake_value'], + 'fake_key', + 'fake_value', + ]; + + yield [ + new FakeClass(), + 'property1', + 'fake_is_property', + ]; + + yield [ + new FakeClass(), + 'property2', + 'fake_get_property', + ]; + + yield [ + new FakeClass(), + 'callProperty', + 'getcallProperty', + ]; + + yield [ + new FakeClass2(), + '_property4', + 'property4', + ]; + + yield [ + new FakeArrayAccess(), + 'callProperty', + 'callProperty', + ]; + $object = new \stdClass(); $object->property = 'fake_property'; $object->_property4 = 'fake_property'; - return [ - [ - ['fake_key' => 'fake_value'], - 'fake_key', - 'fake_value', - ], - [ - new FakeClass(), - 'property1', - 'fake_is_property', - ], - [ - new FakeClass(), - 'property2', - 'fake_get_property', - ], - [ - new FakeClass(), - 'callProperty', - 'getcallProperty', - ], - [ - new FakeArrayAccess(), - 'callProperty', - 'callProperty', - ], - [ - $object, - 'property', - 'fake_property', - ], - [ - new FakeClass2(), - '_property4', - 'property4', - ], - [ - $object, - '_property4', - 'fake_property', - ], + yield [ + $object, + 'property', + 'fake_property', + ]; + + yield [ + $object, + '_property4', + 'fake_property', ]; } /** - * @dataProvider getObjectFieldValueDataProvider + * @dataProvider provideGetObjectFieldValueCases */ public function testGetObjectFieldValue(mixed $object, mixed $value, mixed $expectedResult): void { - $this->assertEquals($expectedResult, ClosureExpressionBuilder::getObjectFieldValue($object, $value)); + self::assertSame($expectedResult, ClosureExpressionBuilder::getObjectFieldValue($object, $value)); } public function testGetSupportedTokenType(): void { - $this->assertEquals(Lexer::T_ALL, $this->closureExpressionBuilder->getSupportedTokenType()); + self::assertSame(Lexer::T_ALL, $this->closureExpressionBuilder->getSupportedTokenType()); } public function testParameter(): void { - $this->assertEquals('my_fake_data', $this->closureExpressionBuilder->parameter('my_fake_data')); - $this->assertEquals('my_fake_data', $this->closureExpressionBuilder->parameter('my_fake_data', true)); + self::assertSame('my_fake_data', $this->closureExpressionBuilder->parameter('my_fake_data')); + self::assertSame('my_fake_data', $this->closureExpressionBuilder->parameter('my_fake_data', true)); } public function testString(): void { - $this->assertEquals('my_fake_data', $this->closureExpressionBuilder->string('my_fake_data')); + self::assertSame('my_fake_data', $this->closureExpressionBuilder->string('my_fake_data')); } - public function isNullDataProvider(): array + public static function provideIsNullCases(): iterable { - return [ - ['field_null', true], - ['field_number_5', false], - ]; + yield ['field_null', true]; + + yield ['field_number_5', false]; } /** - * @dataProvider isNullDataProvider + * @dataProvider provideIsNullCases */ public function testIsNull(mixed $field, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->isNull($field); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); } - public function eqDataProvider(): array + public static function provideEqCases(): iterable { - return [ - ['field_number_5', 1, false], - ['field_number_5', 5, true], - ['field_number_5', 10, false], - ]; + yield ['field_number_5', 1, false]; + + yield ['field_number_5', 5, true]; + + yield ['field_number_5', 10, false]; } /** - * @dataProvider eqDataProvider + * @dataProvider provideEqCases */ public function testEq(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->eq($field, $value); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); } - public function neqDataProvider(): array + public static function provideNeqCases(): iterable { - return [ - ['field_number_5', 1, true], - ['field_number_5', 5, false], - ['field_number_5', 10, true], - ]; + yield ['field_number_5', 1, true]; + + yield ['field_number_5', 5, false]; + + yield ['field_number_5', 10, true]; } /** - * @dataProvider neqDataProvider + * @dataProvider provideNeqCases */ public function testNeq(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->neq($field, $value); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); } - public function gtDataProvider(): array + public static function provideGtCases(): iterable { - return [ - ['field_number_5', 1, true], - ['field_number_5', 5, false], - ['field_number_5', 10, false], - ]; + yield ['field_number_5', 1, true]; + + yield ['field_number_5', 5, false]; + + yield ['field_number_5', 10, false]; } /** - * @dataProvider gtDataProvider + * @dataProvider provideGtCases */ public function testGt(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->gt($field, $value); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); } - public function gteDataProvider(): array + public static function provideGteCases(): iterable { - return [ - ['field_number_5', 1, true], - ['field_number_5', 5, true], - ['field_number_5', 10, false], - ]; + yield ['field_number_5', 1, true]; + + yield ['field_number_5', 5, true]; + + yield ['field_number_5', 10, false]; } /** - * @dataProvider gteDataProvider + * @dataProvider provideGteCases */ public function testGte(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->gte($field, $value); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); } - public function ltDataProvider(): array + public static function provideLtCases(): iterable { - return [ - ['field_number_5', 1, false], - ['field_number_5', 5, false], - ['field_number_5', 10, true], - ]; + yield ['field_number_5', 1, false]; + + yield ['field_number_5', 5, false]; + + yield ['field_number_5', 10, true]; } /** - * @dataProvider ltDataProvider + * @dataProvider provideLtCases */ public function testLt(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->lt($field, $value); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); } - public function lteDataProvider(): array + public static function provideLteCases(): iterable { - return [ - ['field_number_5', 1, false], - ['field_number_5', 5, true], - ['field_number_5', 10, true], - ]; + yield ['field_number_5', 1, false]; + + yield ['field_number_5', 5, true]; + + yield ['field_number_5', 10, true]; } /** - * @dataProvider lteDataProvider + * @dataProvider provideLteCases */ public function testLte(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->lte($field, $value); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); } - public function inDataProvider(): array + public static function inDataProvider(): iterable { - return [ - ['field_number_5', [1], false], - ['field_number_5', [1, 2, 3, 4, 5], true], - ]; + yield ['field_number_5', [1], false]; + + yield ['field_number_5', [1, 2, 3, 4, 5], true]; } /** @@ -257,7 +267,7 @@ public function inDataProvider(): array public function testIn(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->in($field, $value); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); @@ -269,18 +279,17 @@ public function testIn(mixed $field, mixed $value, mixed $expectedResult): void public function testNotIn(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->notIn($field, $value); - $this->assertEquals( + self::assertSame( !$expectedResult, $expression($this->exampleData) ); } - public function containsDataProvider(): array + public static function containsDataProvider(): iterable { - return [ - ['field_string', 'toto', false], - ['field_string', 'fake', true], - ]; + yield ['field_string', 'toto', false]; + + yield ['field_string', 'fake', true]; } /** @@ -289,7 +298,7 @@ public function containsDataProvider(): array public function testContains(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->contains($field, $value); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); @@ -301,20 +310,21 @@ public function testContains(mixed $field, mixed $value, mixed $expectedResult): public function testNotContains(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->notContains($field, $value); - $this->assertEquals( + self::assertSame( !$expectedResult, $expression($this->exampleData) ); } - public function andXDataProvider(): array + public static function andXDataProvider(): iterable { - return [ - [[false, false], false], - [[false, true], false], - [[true, false], false], - [[true, true], true], - ]; + yield [[false, false], false]; + + yield [[false, true], false]; + + yield [[true, false], false]; + + yield [[true, true], true]; } /** @@ -322,13 +332,9 @@ public function andXDataProvider(): array */ public function testAndX(array $expressions, mixed $expectedResult): void { - $expressionsCallable = array_map(function ($value) { - return function () use ($value) { - return $value; - }; - }, $expressions); + $expressionsCallable = array_map(static fn ($value) => static fn () => $value, $expressions); $expression = $this->closureExpressionBuilder->andX($expressionsCallable); - $this->assertEquals( + self::assertSame( $expectedResult, $expression('useless_data') ); @@ -339,26 +345,23 @@ public function testAndX(array $expressions, mixed $expectedResult): void */ public function testNandX(array $expressions, mixed $expectedResult): void { - $expressionsCallable = array_map(function ($value) { - return function () use ($value) { - return $value; - }; - }, $expressions); + $expressionsCallable = array_map(static fn ($value) => static fn () => $value, $expressions); $expression = $this->closureExpressionBuilder->nandX($expressionsCallable); - $this->assertEquals( + self::assertSame( !$expectedResult, $expression('useless_data') ); } - public function orXDataProvider(): array + public static function orXDataProvider(): iterable { - return [ - [[false, false], false], - [[false, true], true], - [[true, false], true], - [[true, true], true], - ]; + yield [[false, false], false]; + + yield [[false, true], true]; + + yield [[true, false], true]; + + yield [[true, true], true]; } /** @@ -366,13 +369,9 @@ public function orXDataProvider(): array */ public function testOrX(array $expressions, mixed $expectedResult): void { - $expressionsCallable = array_map(function ($value) { - return function () use ($value) { - return $value; - }; - }, $expressions); + $expressionsCallable = array_map(static fn ($value) => static fn () => $value, $expressions); $expression = $this->closureExpressionBuilder->orX($expressionsCallable); - $this->assertEquals( + self::assertSame( $expectedResult, $expression('useless_data') ); @@ -383,49 +382,49 @@ public function testOrX(array $expressions, mixed $expectedResult): void */ public function testNorX(array $expressions, mixed $expectedResult): void { - $expressionsCallable = array_map(function ($value) { - return function () use ($value) { - return $value; - }; - }, $expressions); + $expressionsCallable = array_map(static fn ($value) => static fn () => $value, $expressions); $expression = $this->closureExpressionBuilder->norX($expressionsCallable); - $this->assertEquals( + self::assertSame( !$expectedResult, $expression('useless_data') ); } - public function xorXDataProvider(): array + public static function provideXorXCases(): iterable { - return [ - [[false, false], false], - [[false, true], true], - [[true, false], true], - [[true, true], false], + yield [[false, false], false]; - [[false, false, false], false], - [[false, false, true], true], - [[false, true, false], true], - [[false, true, true], false], - [[true, false, false], true], - [[true, false, true], false], - [[true, true, false], false], - [[true, true, true], true], - ]; + yield [[false, true], true]; + + yield [[true, false], true]; + + yield [[true, true], false]; + + yield [[false, false, false], false]; + + yield [[false, false, true], true]; + + yield [[false, true, false], true]; + + yield [[false, true, true], false]; + + yield [[true, false, false], true]; + + yield [[true, false, true], false]; + + yield [[true, true, false], false]; + + yield [[true, true, true], true]; } /** - * @dataProvider xorXDataProvider + * @dataProvider provideXorXCases */ public function testXorX(array $expressions, mixed $expectedResult): void { - $expressionsCallable = array_map(function ($value) { - return function () use ($value) { - return $value; - }; - }, $expressions); + $expressionsCallable = array_map(static fn ($value) => static fn () => $value, $expressions); $expression = $this->closureExpressionBuilder->xorX($expressionsCallable); - $this->assertEquals( + self::assertEquals( $expectedResult, $expression('useless_data') ); @@ -434,6 +433,11 @@ public function testXorX(array $expressions, mixed $expectedResult): void class FakeClass { + public function __call($name, $arguments) + { + return $name.implode(', ', $arguments); + } + public function isProperty1() { return 'fake_is_property'; @@ -453,11 +457,6 @@ public function getPROPERTY4() { return 'property4'; } - - public function __call($name, $arguments) - { - return $name . implode(', ', $arguments); - } } class FakeClass2 @@ -485,11 +484,7 @@ public function offsetGet($offset): mixed return $offset; } - public function offsetSet($offset, $value): void - { - } + public function offsetSet($offset, $value): void {} - public function offsetUnset($offset): void - { - } + public function offsetUnset($offset): void {} } diff --git a/tests/Expr/HtmlExpressionBuilderTest.php b/tests/Expr/HtmlExpressionBuilderTest.php index 9d309dd..fe1e97b 100644 --- a/tests/Expr/HtmlExpressionBuilderTest.php +++ b/tests/Expr/HtmlExpressionBuilderTest.php @@ -1,63 +1,62 @@ closureExpressionBuilder = new HtmlExpressionBuilder(); + $this->htmlExpressionBuilder = new HtmlExpressionBuilder(); } - public function testGetSupportedTokenType() + public function testGetSupportedTokenType(): void { - $this->assertEquals(Lexer::T_ALL, $this->closureExpressionBuilder->getSupportedTokenType()); + self::assertSame(Lexer::T_ALL, $this->htmlExpressionBuilder->getSupportedTokenType()); } - public function testParameter() + public function testParameter(): void { - $this->assertEquals('my_fake_data', $this->closureExpressionBuilder->parameter('my_fake_data')); - $this->assertEquals('my_fake_data', $this->closureExpressionBuilder->parameter('my_fake_data', true)); + self::assertSame('my_fake_data', $this->htmlExpressionBuilder->parameter('my_fake_data')); + self::assertSame('my_fake_data', $this->htmlExpressionBuilder->parameter('my_fake_data', true)); } - public function testString() + public function testString(): void { - $this->assertEquals('"my_fake_data"', $this->closureExpressionBuilder->string('my_fake_data')); + self::assertSame('"my_fake_data"', $this->htmlExpressionBuilder->string('my_fake_data')); } - public function isNullDataProvider() + public static function provideIsNullCases(): iterable { - return [ - ['field_null', '
field_null is null
'], - ['field_number_5', '
field_number_5 is null
'], - ]; + yield ['field_null', '
field_null is null
']; + + yield ['field_number_5', '
field_number_5 is null
']; } /** - * @dataProvider isNullDataProvider - * - * @param $field - * @param $expectedResult + * @dataProvider provideIsNullCases */ - public function testIsNull($field, $expectedResult) + public function testIsNull(string $field, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->isNull($field) + $this->htmlExpressionBuilder->isNull($field) ); } - public function eqDataProvider() + public static function provideEqCases(): iterable { return [ ['field_number_5', 1, '
field_number_5 = 1
'], @@ -67,210 +66,175 @@ public function eqDataProvider() } /** - * @dataProvider eqDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideEqCases */ - public function testEq($field, $value, $expectedResult) + public function testEq(string $field, mixed $value, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->eq($field, $value) + $this->htmlExpressionBuilder->eq($field, $value) ); } - public function neqDataProvider() + public static function provideNeqCases(): iterable { - return [ - ['field_number_5', 1, '
field_number_5 ≠ 1
'], - ['field_number_5', 5, '
field_number_5 ≠ 5
'], - ['field_number_5', 10, '
field_number_5 ≠ 10
'], - ]; + yield ['field_number_5', 1, '
field_number_5 ≠ 1
']; + + yield ['field_number_5', 5, '
field_number_5 ≠ 5
']; + + yield ['field_number_5', 10, '
field_number_5 ≠ 10
']; } /** - * @dataProvider neqDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideNeqCases */ - public function testNeq($field, $value, $expectedResult) + public function testNeq(string $field, mixed $value, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->neq($field, $value) + $this->htmlExpressionBuilder->neq($field, $value) ); } - public function gtDataProvider() + public static function provideGtCases(): iterable { - return [ - ['field_number_5', 1, '
field_number_5 > 1
'], - ['field_number_5', 5, '
field_number_5 > 5
'], - ['field_number_5', 10, '
field_number_5 > 10
'], - ]; + yield ['field_number_5', 1, '
field_number_5 > 1
']; + + yield ['field_number_5', 5, '
field_number_5 > 5
']; + + yield ['field_number_5', 10, '
field_number_5 > 10
']; } /** - * @dataProvider gtDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideGtCases */ - public function testGt($field, $value, $expectedResult) + public function testGt(string $field, mixed $value, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->gt($field, $value) + $this->htmlExpressionBuilder->gt($field, $value) ); } - public function gteDataProvider() + public static function provideGteCases(): iterable { - return [ - ['field_number_5', 1, '
field_number_5 ≥ 1
'], - ['field_number_5', 5, '
field_number_5 ≥ 5
'], - ['field_number_5', 10, '
field_number_5 ≥ 10
'], - ]; + yield ['field_number_5', 1, '
field_number_5 ≥ 1
']; + + yield ['field_number_5', 5, '
field_number_5 ≥ 5
']; + + yield ['field_number_5', 10, '
field_number_5 ≥ 10
']; } /** - * @dataProvider gteDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideGteCases */ - public function testGte($field, $value, $expectedResult) + public function testGte(string $field, mixed $value, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->gte($field, $value) + $this->htmlExpressionBuilder->gte($field, $value) ); } - public function ltDataProvider() + public static function provideLtCases(): iterable { - return [ - ['field_number_5', 1, '
field_number_5 < 1
'], - ['field_number_5', 5, '
field_number_5 < 5
'], - ['field_number_5', 10, '
field_number_5 < 10
'], - ]; + yield ['field_number_5', 1, '
field_number_5 < 1
']; + + yield ['field_number_5', 5, '
field_number_5 < 5
']; + + yield ['field_number_5', 10, '
field_number_5 < 10
']; } /** - * @dataProvider ltDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideLtCases */ - public function testLt($field, $value, $expectedResult) + public function testLt(string $field, mixed $value, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->lt($field, $value) + $this->htmlExpressionBuilder->lt($field, $value) ); } - public function lteDataProvider() + public static function provideLteCases(): iterable { - return [ - ['field_number_5', 1, '
field_number_5 ≤ 1
'], - ['field_number_5', 5, '
field_number_5 ≤ 5
'], - ['field_number_5', 10, '
field_number_5 ≤ 10
'], - ]; + yield ['field_number_5', 1, '
field_number_5 ≤ 1
']; + + yield ['field_number_5', 5, '
field_number_5 ≤ 5
']; + + yield ['field_number_5', 10, '
field_number_5 ≤ 10
']; } /** - * @dataProvider lteDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideLteCases */ - public function testLte($field, $value, $expectedResult) + public function testLte(string $field, mixed $value, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->lte($field, $value) + $this->htmlExpressionBuilder->lte($field, $value) ); } - public function inDataProvider() + public static function provideInCases(): iterable { - return [ - ['field_number_5', [1], '
field_number_5 value in 1
'], - ['field_number_5', [1, 2, 3, 4, 5], '
field_number_5 value in 1, 2, 3, 4, 5
'], - ]; + yield ['field_number_5', [1], '
field_number_5 value in 1
']; + + yield ['field_number_5', [1, 2, 3, 4, 5], '
field_number_5 value in 1, 2, 3, 4, 5
']; } /** - * @dataProvider inDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideInCases */ - public function testIn($field, $value, $expectedResult) + public function testIn(string $field, mixed $value, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->in($field, $value) + $this->htmlExpressionBuilder->in($field, $value) ); } - public function notInDataProvider() + public static function provideNotInCases(): iterable { - return [ - ['field_number_5', [1], '
field_number_5 value not in 1
'], - ['field_number_5', [1, 2, 3, 4, 5], '
field_number_5 value not in 1, 2, 3, 4, 5
'], - ]; + yield ['field_number_5', [1], '
field_number_5 value not in 1
']; + + yield ['field_number_5', [1, 2, 3, 4, 5], '
field_number_5 value not in 1, 2, 3, 4, 5
']; } /** - * @dataProvider notInDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideNotInCases */ - public function testNotIn($field, $value, $expectedResult) + public function testNotIn(string $field, mixed $value, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->notIn($field, $value) + $this->htmlExpressionBuilder->notIn($field, $value) ); } - public function containsDataProvider() + public static function provideContainsCases(): iterable { - return [ - ['field_string', 'toto', '
field_string contains toto
'], - ['field_string', 'fake', '
field_string contains fake
'], - ]; + yield ['field_string', 'toto', '
field_string contains toto
']; + + yield ['field_string', 'fake', '
field_string contains fake
']; } /** - * @dataProvider containsDataProvider + * @dataProvider provideContainsCases * - * @param $field - * @param $value - * @param $expectedResult + * @param mixed $field + * @param mixed $value + * @param mixed $expectedResult */ - public function testContains($field, $value, $expectedResult) + public function testContains($field, $value, $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->contains($field, $value) + $this->htmlExpressionBuilder->contains($field, $value) ); } - public function notContainsDataProvider() + public static function provideNotContainsCases(): iterable { return [ ['field_string', 'toto', '
field_string notContains toto
'], @@ -279,143 +243,139 @@ public function notContainsDataProvider() } /** - * @dataProvider notContainsDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideNotContainsCases */ - public function testNotContains($field, $value, $expectedResult) + public function testNotContains(string $field, mixed $value, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->notContains($field, $value) + $this->htmlExpressionBuilder->notContains($field, $value) ); } - public function andXDataProvider() + public static function provideAndXCases(): iterable { - return [ - [['false', 'false'], '
andfalsefalse
'], - [['false', 'true'], '
andfalsetrue
'], - [['true', 'false'], '
andtruefalse
'], - [['true', 'true'], '
andtruetrue
'], - ]; + yield [['false', 'false'], '
andfalsefalse
']; + + yield [['false', 'true'], '
andfalsetrue
']; + + yield [['true', 'false'], '
andtruefalse
']; + + yield [['true', 'true'], '
andtruetrue
']; } /** - * @dataProvider andXDataProvider - * - * @param array $expressions - * @param $expectedResult + * @dataProvider provideAndXCases */ - public function testAndX(array $expressions, $expectedResult) + public function testAndX(array $expressions, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->andX($expressions) + $this->htmlExpressionBuilder->andX($expressions) ); } - public function nandXDataProvider() + public static function provideNandXCases(): iterable { - return [ - [['false', 'false'], '
not-andfalsefalse
'], - [['false', 'true'], '
not-andfalsetrue
'], - [['true', 'false'], '
not-andtruefalse
'], - [['true', 'true'], '
not-andtruetrue
'], - ]; + yield [['false', 'false'], '
not-andfalsefalse
']; + + yield [['false', 'true'], '
not-andfalsetrue
']; + + yield [['true', 'false'], '
not-andtruefalse
']; + + yield [['true', 'true'], '
not-andtruetrue
']; } /** - * @dataProvider nandXDataProvider - * - * @param array $expressions - * @param $expectedResult + * @dataProvider provideNandXCases */ - public function testNandX(array $expressions, $expectedResult) + public function testNandX(array $expressions, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->nandX($expressions) + $this->htmlExpressionBuilder->nandX($expressions) ); } - public function orXDataProvider() + public static function provideOrXCases(): iterable { - return [ - [['false', 'false'], '
orfalsefalse
'], - [['false', 'true'], '
orfalsetrue
'], - [['true', 'false'], '
ortruefalse
'], - [['true', 'true'], '
ortruetrue
'], - ]; + yield [['false', 'false'], '
orfalsefalse
']; + + yield [['false', 'true'], '
orfalsetrue
']; + + yield [['true', 'false'], '
ortruefalse
']; + + yield [['true', 'true'], '
ortruetrue
']; } /** - * @dataProvider orXDataProvider - * - * @param array $expressions - * @param $expectedResult + * @dataProvider provideOrXCases */ - public function testOrX(array $expressions, $expectedResult) + public function testOrX(array $expressions, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->orX($expressions) + $this->htmlExpressionBuilder->orX($expressions) ); } - public function norXDataProvider() + public static function provideNorXCases(): iterable { - return [ - [['false', 'false'], '
not-orfalsefalse
'], - [['false', 'true'], '
not-orfalsetrue
'], - [['true', 'false'], '
not-ortruefalse
'], - [['true', 'true'], '
not-ortruetrue
'], - ]; + yield [['false', 'false'], '
not-orfalsefalse
']; + + yield [['false', 'true'], '
not-orfalsetrue
']; + + yield [['true', 'false'], '
not-ortruefalse
']; + + yield [['true', 'true'], '
not-ortruetrue
']; } /** - * @dataProvider norXDataProvider - * - * @param array $expressions - * @param $expectedResult + * @dataProvider provideNorXCases */ - public function testNorX(array $expressions, $expectedResult) + public function testNorX(array $expressions, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->norX($expressions) + $this->htmlExpressionBuilder->norX($expressions) ); } - public function xorXDataProvider() + public static function provideXorXCases(): iterable { - return [ - [['false', 'false'], '
exclusive-orfalsefalse
'], - [['false', 'true'], '
exclusive-orfalsetrue
'], - [['true', 'false'], '
exclusive-ortruefalse
'], - [['true', 'true'], '
exclusive-ortruetrue
'], - - [['false', 'false', 'false'], '
exclusive-orfalsefalsefalse
'], - [['false', 'false', 'true'], '
exclusive-orfalsefalsetrue
'], - [['false', 'true', 'false'], '
exclusive-orfalsetruefalse
'], - [['false', 'true', 'true'], '
exclusive-orfalsetruetrue
'], - [['true', 'false', 'false'], '
exclusive-ortruefalsefalse
'], - [['true', 'false', 'true'], '
exclusive-ortruefalsetrue
'], - [['true', 'true', 'false'], '
exclusive-ortruetruefalse
'], - [['true', 'true', 'true'], '
exclusive-ortruetruetrue
'], - ]; + yield [['false', 'false'], '
exclusive-orfalsefalse
']; + + yield [['false', 'true'], '
exclusive-orfalsetrue
']; + + yield [['true', 'false'], '
exclusive-ortruefalse
']; + + yield [['true', 'true'], '
exclusive-ortruetrue
']; + + yield [['false', 'false', 'false'], '
exclusive-orfalsefalsefalse
']; + + yield [['false', 'false', 'true'], '
exclusive-orfalsefalsetrue
']; + + yield [['false', 'true', 'false'], '
exclusive-orfalsetruefalse
']; + + yield [['false', 'true', 'true'], '
exclusive-orfalsetruetrue
']; + + yield [['true', 'false', 'false'], '
exclusive-ortruefalsefalse
']; + + yield [['true', 'false', 'true'], '
exclusive-ortruefalsetrue
']; + + yield [['true', 'true', 'false'], '
exclusive-ortruetruefalse
']; + + yield [['true', 'true', 'true'], '
exclusive-ortruetruetrue
']; } /** - * @dataProvider xorXDataProvider + * @dataProvider provideXorXCases */ - public function testXorX(array $expressions, mixed $expectedResult) + public function testXorX(array $expressions, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->xorX($expressions) + $this->htmlExpressionBuilder->xorX($expressions) ); } } diff --git a/tests/Expr/MapperExpressionBuilderTest.php b/tests/Expr/MapperExpressionBuilderTest.php index 507d43c..98d11c0 100644 --- a/tests/Expr/MapperExpressionBuilderTest.php +++ b/tests/Expr/MapperExpressionBuilderTest.php @@ -1,5 +1,7 @@ prophet = new Prophet(); $this->expressionBuilderMock = $this->prophet->prophesize(ExpressionBuilderInterface::class); @@ -40,7 +47,7 @@ public function testParameter(): void { $this->expressionBuilderMock->parameter('fake_field', false)->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->parameter('fake_field') ); @@ -50,7 +57,7 @@ public function testParameterAsValue(): void { $this->expressionBuilderMock->parameter('fake_field', true)->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->parameter('fake_field', true) ); @@ -60,14 +67,14 @@ public function testString(): void { $this->expressionBuilderMock->string('fake_field')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->string('fake_field') ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testIsNull(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -75,14 +82,14 @@ public function testIsNull(array $fieldMapping, mixed $field, mixed $expectedMap $this->expressionBuilderMock->isNull($expectedMappedField)->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->isNull($field) ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testEq(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -90,14 +97,14 @@ public function testEq(array $fieldMapping, mixed $field, mixed $expectedMappedF $this->expressionBuilderMock->eq($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->eq($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testNeq(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -105,14 +112,14 @@ public function testNeq(array $fieldMapping, mixed $field, mixed $expectedMapped $this->expressionBuilderMock->neq($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->neq($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testGt(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -120,14 +127,14 @@ public function testGt(array $fieldMapping, mixed $field, mixed $expectedMappedF $this->expressionBuilderMock->gt($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->gt($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testGte(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -135,14 +142,14 @@ public function testGte(array $fieldMapping, mixed $field, mixed $expectedMapped $this->expressionBuilderMock->gte($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->gte($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testLt(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -150,14 +157,14 @@ public function testLt(array $fieldMapping, mixed $field, mixed $expectedMappedF $this->expressionBuilderMock->lt($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->lt($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testLte(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -165,14 +172,14 @@ public function testLte(array $fieldMapping, mixed $field, mixed $expectedMapped $this->expressionBuilderMock->lte($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->lte($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testIn(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -180,14 +187,14 @@ public function testIn(array $fieldMapping, mixed $field, mixed $expectedMappedF $this->expressionBuilderMock->in($expectedMappedField, ['fake_value'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->in($field, ['fake_value']) ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testNotIn(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -195,14 +202,14 @@ public function testNotIn(array $fieldMapping, mixed $field, mixed $expectedMapp $this->expressionBuilderMock->notIn($expectedMappedField, ['fake_value'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->notIn($field, ['fake_value']) ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testContains(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -210,14 +217,14 @@ public function testContains(array $fieldMapping, mixed $field, mixed $expectedM $this->expressionBuilderMock->contains($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->contains($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider + * @dataProvider fieldMappingDataProvider */ public function testNotContains(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { @@ -225,35 +232,36 @@ public function testNotContains(array $fieldMapping, mixed $field, mixed $expect $this->expressionBuilderMock->notContains($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->notContains($field, 'fake_value') ); } - public function fieldMappingProvider(): array + public static function fieldMappingDataProvider(): iterable { - return [ - [ - [], - 'fake_field', - 'fake_field', - ], - [ - ['*' => 'fake_%s_mapping'], - 'fake_field', - 'fake_fake_field_mapping', - ], - [ - ['other' => 'fake_%s_mapping'], - 'fake_field', - 'fake_field', - ], - [ - ['fake_field' => 'fake_%s_mapping'], - 'fake_field', - 'fake_fake_field_mapping', - ], + yield [ + [], + 'fake_field', + 'fake_field', + ]; + + yield [ + ['*' => 'fake_%s_mapping'], + 'fake_field', + 'fake_fake_field_mapping', + ]; + + yield [ + ['other' => 'fake_%s_mapping'], + 'fake_field', + 'fake_field', + ]; + + yield [ + ['fake_field' => 'fake_%s_mapping'], + 'fake_field', + 'fake_fake_field_mapping', ]; } @@ -261,7 +269,7 @@ public function testAndX(): void { $this->expressionBuilderMock->andX(['fake_expression'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->andX(['fake_expression']) ); @@ -271,7 +279,7 @@ public function testNandX(): void { $this->expressionBuilderMock->nandX(['fake_expression'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->nandX(['fake_expression']) ); @@ -281,7 +289,7 @@ public function testOrX(): void { $this->expressionBuilderMock->orX(['fake_expression'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->orX(['fake_expression']) ); @@ -291,7 +299,7 @@ public function testNorX(): void { $this->expressionBuilderMock->norX(['fake_expression'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->norX(['fake_expression']) ); @@ -301,7 +309,7 @@ public function testXorX(): void { $this->expressionBuilderMock->xorX(['fake_expression'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->xorX(['fake_expression']) ); diff --git a/tests/LexerTest.php b/tests/LexerTest.php index a5fbfd0..2151aa1 100644 --- a/tests/LexerTest.php +++ b/tests/LexerTest.php @@ -1,230 +1,244 @@ lexer = new Lexer(); } - public function setInputSuccessDataProvider():array + public static function provideSetInputSuccessCases(): iterable { - return [ + yield [ + 'a=1', [ - 'a=1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 2], - ], + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 2], ], + ]; + + yield [ + 'a.b=1', [ - 'a.b=1', - [ - ['value' => 'a.b', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 3], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4], - ], + ['value' => 'a.b', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 3], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 4], ], + ]; + + yield [ + 'a-b=1', [ - 'a-b=1', - [ - ['value' => 'a-b', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 3], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4], - ], + ['value' => 'a-b', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 3], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 4], ], + ]; + + yield [ + 'a≠1', [ - 'a≠1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '≠', 'type' => Lexer::T_NOT_EQUALS, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4], - ], + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '≠', 'type' => Lexer::T_NOT_EQUALS, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 4], ], + ]; + + yield [ + 'a!=1', [ - 'a!=1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '≠', 'type' => Lexer::T_NOT_EQUALS, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3], - ], - ], - [ - 'a="value"', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 1], - ['value' => 'value', 'type' => Lexer::T_STRING, 'position' => 2], - ], - ], - [ - "a='value'", - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 1], - ['value' => 'value', 'type' => Lexer::T_STRING, 'position' => 2], - ], - ], - [ - 'a>1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '>', 'type' => Lexer::T_GREATER_THAN, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 2], - ], - ], - [ - 'a>=1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '≥', 'type' => Lexer::T_GREATER_THAN_EQUALS, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3], - ], - ], - [ - 'a≥1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '≥', 'type' => Lexer::T_GREATER_THAN_EQUALS, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4], - ], - ], - [ - 'a<1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '<', 'type' => Lexer::T_LOWER_THAN, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 2], - ], - ], - [ - 'a<=1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '≤', 'type' => Lexer::T_LOWER_THAN_EQUALS, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3], - ], - ], - [ - 'a≤1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '≤', 'type' => Lexer::T_LOWER_THAN_EQUALS, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4], - ], - ], - [ - 'a|1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '|', 'type' => Lexer::T_OR, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 2], - ], - ], - [ - 'a!|1', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '!|', 'type' => Lexer::T_NOT_OR, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3], - ], - ], - [ - 'a[1,2]', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '[', 'type' => Lexer::T_OPEN_SQUARE_BRACKET, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 2], - ['value' => ',', 'type' => Lexer::T_COMMA, 'position' => 3], - ['value' => '2', 'type' => Lexer::T_INTEGER, 'position' => 4], - ['value' => ']', 'type' => Lexer::T_CLOSE_SQUARE_BRACKET, 'position' => 5], - ], + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '≠', 'type' => Lexer::T_NOT_EQUALS, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 3], ], + ]; + + yield [ + 'a="value"', [ - 'a![1,2]', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '![', 'type' => Lexer::T_NOT_OPEN_SQUARE_BRACKET, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3], - ['value' => ',', 'type' => Lexer::T_COMMA, 'position' => 4], - ['value' => '2', 'type' => Lexer::T_INTEGER, 'position' => 5], - ['value' => ']', 'type' => Lexer::T_CLOSE_SQUARE_BRACKET, 'position' => 6], - ], - ], - [ - 'a{{1}}', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '{{', 'type' => Lexer::T_DOUBLE_OPEN_CURLY_BRACKET, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3], - ['value' => '}}', 'type' => Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET, 'position' => 4], - ], - ], - [ - 'a!{{1}}', - [ - ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], - ['value' => '!{{', 'type' => Lexer::T_NOT_DOUBLE_OPEN_CURLY_BRACKET, 'position' => 1], - ['value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4], - ['value' => '}}', 'type' => Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET, 'position' => 5], - ], + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 1], + ['value' => 'value', 'type' => Lexer::T_STRING, 'position' => 2], ], ]; - } - /** - * @dataProvider setInputSuccessDataProvider - */ - public function testSetInputSuccess(string $input, array $expectedTokens) - { - $this->lexer->setInput($input); - $this->lexer->moveNext(); - $this->lexer->moveNext(); - $i = 0; - while ($currentToken = $this->lexer->token) { - $this->assertEquals($expectedTokens[$i], $currentToken); - $this->lexer->moveNext(); - $i++; - } - } + yield [ + "a='value'", + [ + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 1], + ['value' => 'value', 'type' => Lexer::T_STRING, 'position' => 2], + ], + ]; - public function unexpectedValueExceptionProvider() - { - return [ + yield [ + 'a>1', + [ + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '>', 'type' => Lexer::T_GREATER_THAN, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 2], + ], + ]; + + yield [ + 'a>=1', [ - '!', + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '≥', 'type' => Lexer::T_GREATER_THAN_EQUALS, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 3], ], + ]; + + yield [ + 'a≥1', [ - '§', + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '≥', 'type' => Lexer::T_GREATER_THAN_EQUALS, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 4], ], + ]; + + yield [ + 'a<1', [ - '^', + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '<', 'type' => Lexer::T_LOWER_THAN, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 2], ], + ]; + + yield [ + 'a<=1', [ - ';', + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '≤', 'type' => Lexer::T_LOWER_THAN_EQUALS, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 3], ], + ]; + + yield [ + 'a≤1', [ - ':', + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '≤', 'type' => Lexer::T_LOWER_THAN_EQUALS, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 4], ], + ]; + + yield [ + 'a|1', [ - '/', + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '|', 'type' => Lexer::T_OR, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 2], ], ]; + + yield [ + 'a!|1', + [ + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '!|', 'type' => Lexer::T_NOT_OR, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 3], + ], + ]; + + yield [ + 'a[1,2]', + [ + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '[', 'type' => Lexer::T_OPEN_SQUARE_BRACKET, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 2], + ['value' => ',', 'type' => Lexer::T_COMMA, 'position' => 3], + ['value' => 2, 'type' => Lexer::T_INTEGER, 'position' => 4], + ['value' => ']', 'type' => Lexer::T_CLOSE_SQUARE_BRACKET, 'position' => 5], + ], + ]; + + yield [ + 'a![1,2]', + [ + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '![', 'type' => Lexer::T_NOT_OPEN_SQUARE_BRACKET, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 3], + ['value' => ',', 'type' => Lexer::T_COMMA, 'position' => 4], + ['value' => 2, 'type' => Lexer::T_INTEGER, 'position' => 5], + ['value' => ']', 'type' => Lexer::T_CLOSE_SQUARE_BRACKET, 'position' => 6], + ], + ]; + + yield [ + 'a{{1}}', + [ + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '{{', 'type' => Lexer::T_DOUBLE_OPEN_CURLY_BRACKET, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 3], + ['value' => '}}', 'type' => Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET, 'position' => 4], + ], + ]; + + yield [ + 'a!{{1}}', + [ + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '!{{', 'type' => Lexer::T_NOT_DOUBLE_OPEN_CURLY_BRACKET, 'position' => 1], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 4], + ['value' => '}}', 'type' => Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET, 'position' => 5], + ], + ]; + } + + /** + * @dataProvider provideSetInputSuccessCases + */ + public function testSetInputSuccess(string $input, array $expectedTokens): void + { + $this->lexer->setInput($input); + $this->lexer->moveNext(); + $this->lexer->moveNext(); + $i = 0; + while ($currentToken = $this->lexer->token) { + self::assertSame($expectedTokens[$i], $currentToken); + $this->lexer->moveNext(); + ++$i; + } + } + + public static function provideUnexpectedValueExceptionCases(): iterable + { + yield ['!']; + + yield ['§']; + + yield ['^']; + + yield [';']; + + yield [':']; + + yield ['/']; } /** - * @dataProvider unexpectedValueExceptionProvider + * @dataProvider provideUnexpectedValueExceptionCases */ - public function testUnexpectedValueException(string $input) + public function testUnexpectedValueException(string $input): void { $this->expectException(UnknownTokenTypeException::class); $this->expectExceptionMessageMatches('/Unknown token type ".+"\./'); diff --git a/tests/ParserTest.php b/tests/ParserTest.php index 5db8e6f..4faaa4f 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -1,5 +1,7 @@ prophet = new Prophet(); - $this->expressionBuilderMock = $this->prophet->prophesize(ExpressionBuilderInterface::class); + $this->expressionBuilderMock = (new Prophet())->prophesize(ExpressionBuilderInterface::class); $this->expressionBuilderMock->getSupportedTokenType()->willReturn(Lexer::T_ALL); $this->parser = new Parser($this->expressionBuilderMock->reveal()); } - public function parseSuccessDataProvider(): array + public static function provideParseSuccessCases(): iterable { - $expr1 = $this->createMock(Expression::class); - $expr2 = $this->createMock(Expression::class); - $expr3 = $this->createMock(Expression::class); - $expr4 = $this->createMock(Expression::class); - $composite1 = $this->createMock(CompositeExpression::class); - $composite2 = $this->createMock(CompositeExpression::class); - $composite3 = $this->createMock(CompositeExpression::class); - return [ - [ - 'fieldA=1', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], -// [ -// 'fieldA="string"', -// [['eq', 'fieldA', 'my_fake_string', 'my_fake_comparison_A']], -// [['valueAsString', 'string', 'my_fake_string']], -// 'my_fake_comparison_A', -// ], - [ - 'fieldA>1', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->gt('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA≥1', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->gte('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA>=1', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->gte('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA<1', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->lt('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA≤1', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->lte('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA<=1', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->lte('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA≠1', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->neq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA!=1', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->neq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA[1,2]', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->in('fieldA', [1, 2])->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA![1,2]', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->notIn('fieldA', [1, 2])->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA{{1}}', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->contains('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - 'fieldA!{{1}}', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1) { - $mock->notContains('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - - // Composite - [ - 'fieldA=1|fieldB=2|fieldC=3', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->orX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - }, - $composite1, - ], - [ - 'fieldA=1!|fieldB=2!|fieldC=3', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->norX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - }, - $composite1, - ], - [ - 'fieldA=1^|fieldB=2^|fieldC=3', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->xorX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - }, - $composite1, - ], - [ - 'fieldA=1⊕fieldB=2⊕fieldC=3', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->xorX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - }, - $composite1, - ], - [ - 'fieldA=1&fieldB=2&fieldC=3', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->andX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - }, - $composite1, - ], - [ - 'fieldA{{value}}&fieldB=2', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $composite1) { - $mock->contains('fieldA', 'value')->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->andX([$expr1, $expr2])->willReturn($composite1)->shouldBeCalled(); - }, - $composite1, - ], - [ - 'fieldA=1!&fieldB=2!&fieldC=3', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->nandX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - }, - $composite1, - ], - - // Precedences - [ - 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); - // And was before OR - $mock->andX([$expr3, $expr4])->willReturn($composite1)->shouldBeCalled(); - $mock->orX([$expr1, $expr2, $composite1])->willReturn($composite2)->shouldBeCalled(); - }, - $composite2, - ], - [ - 'fieldA=1&fieldB=2&fieldC=3!&fieldD=4', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); - $mock->andX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - $mock->nandX([$composite1, $expr4])->willReturn($composite2)->shouldBeCalled(); - }, - $composite2, - ], - [ - 'fieldA=1|fieldB=2|fieldC=3!|fieldD=4', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); - $mock->orX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - $mock->norX([$composite1, $expr4])->willReturn($composite2)->shouldBeCalled(); - }, - $composite2, - ], - [ - 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); - $mock->andX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - $mock->orX([$composite1, $expr4])->willReturn($composite2)->shouldBeCalled(); - }, - $composite2, - ], - [ - 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2, $composite3) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); - $mock->andX([$expr1, $expr2])->willReturn($composite1)->shouldBeCalled(); - $mock->andX([$expr3, $expr4])->willReturn($composite2)->shouldBeCalled(); - $mock->orX([$composite1, $composite2])->willReturn($composite3)->shouldBeCalled(); - }, - $composite3, - ], - [ - 'fieldA=1|fieldB=2|fieldC=3⊕fieldD=4', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); - $mock->orX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - $mock->xorX([$composite1, $expr4])->willReturn($composite2)->shouldBeCalled(); - }, - $composite2, - ], - - //Parenthesis - [ - '((fieldA=1))', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - }, - $expr1, - ], - [ - '(fieldA=1|fieldB=2)&fieldC=3', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1, $composite2) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->orX([$expr1, $expr2])->willReturn($composite1)->shouldBeCalled(); - $mock->andX([$composite1, $expr3])->willReturn($composite2)->shouldBeCalled(); - }, - $composite2, - ], - [ - 'fieldA=1|(fieldB=2&fieldC=3)', - function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1, $composite2) { - $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); - $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); - $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); - $mock->andX([$expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); - $mock->orX([$expr1, $composite1])->willReturn($composite2)->shouldBeCalled(); - }, - $composite2, - ], + $prophet = new Prophet(); + + $expr1 = $prophet->prophesize(Expression::class)->reveal(); + $expr2 = $prophet->prophesize(Expression::class)->reveal(); + $expr3 = $prophet->prophesize(Expression::class)->reveal(); + $expr4 = $prophet->prophesize(Expression::class)->reveal(); + $composite1 = $prophet->prophesize(CompositeExpression::class)->reveal(); + $composite2 = $prophet->prophesize(CompositeExpression::class)->reveal(); + $composite3 = $prophet->prophesize(CompositeExpression::class)->reveal(); + + yield [ + 'fieldA=1', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + // [ + // 'fieldA="string"', + // [['eq', 'fieldA', 'my_fake_string', 'my_fake_comparison_A']], + // [['valueAsString', 'string', 'my_fake_string']], + // 'my_fake_comparison_A', + // ], + yield [ + 'fieldA>1', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->gt('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA≥1', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->gte('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA>=1', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->gte('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA<1', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->lt('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA≤1', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->lte('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA<=1', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->lte('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA≠1', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->neq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA!=1', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->neq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA[1,2]', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->in('fieldA', [1, 2])->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA![1,2]', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->notIn('fieldA', [1, 2])->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA{{1}}', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->contains('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + 'fieldA!{{1}}', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->notContains('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + // Composite + yield [ + 'fieldA=1|fieldB=2|fieldC=3', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->orX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + }, + $composite1, + ]; + + yield [ + 'fieldA=1!|fieldB=2!|fieldC=3', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->norX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + }, + $composite1, + ]; + + yield [ + 'fieldA=1^|fieldB=2^|fieldC=3', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->xorX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + }, + $composite1, + ]; + + yield [ + 'fieldA=1⊕fieldB=2⊕fieldC=3', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->xorX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + }, + $composite1, + ]; + + yield [ + 'fieldA=1&fieldB=2&fieldC=3', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->andX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + }, + $composite1, + ]; + + yield [ + 'fieldA{{value}}&fieldB=2', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $composite1): void { + $mock->contains('fieldA', 'value')->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->andX([$expr1, $expr2])->willReturn($composite1)->shouldBeCalled(); + }, + $composite1, + ]; + + yield [ + 'fieldA=1!&fieldB=2!&fieldC=3', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->nandX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + }, + $composite1, + ]; + + // Precedences + yield [ + 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); + // And was before OR + $mock->andX([$expr3, $expr4])->willReturn($composite1)->shouldBeCalled(); + $mock->orX([$expr1, $expr2, $composite1])->willReturn($composite2)->shouldBeCalled(); + }, + $composite2, + ]; + + yield [ + 'fieldA=1&fieldB=2&fieldC=3!&fieldD=4', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); + $mock->andX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + $mock->nandX([$composite1, $expr4])->willReturn($composite2)->shouldBeCalled(); + }, + $composite2, + ]; + + yield [ + 'fieldA=1|fieldB=2|fieldC=3!|fieldD=4', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); + $mock->orX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + $mock->norX([$composite1, $expr4])->willReturn($composite2)->shouldBeCalled(); + }, + $composite2, + ]; + + yield [ + 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); + $mock->andX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + $mock->orX([$composite1, $expr4])->willReturn($composite2)->shouldBeCalled(); + }, + $composite2, + ]; + + yield [ + 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2, $composite3): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); + $mock->andX([$expr1, $expr2])->willReturn($composite1)->shouldBeCalled(); + $mock->andX([$expr3, $expr4])->willReturn($composite2)->shouldBeCalled(); + $mock->orX([$composite1, $composite2])->willReturn($composite3)->shouldBeCalled(); + }, + $composite3, + ]; + + yield [ + 'fieldA=1|fieldB=2|fieldC=3⊕fieldD=4', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $expr4, $composite1, $composite2): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->eq('fieldD', 4)->willReturn($expr4)->shouldBeCalled(); + $mock->orX([$expr1, $expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + $mock->xorX([$composite1, $expr4])->willReturn($composite2)->shouldBeCalled(); + }, + $composite2, + ]; + + // Parenthesis + yield [ + '((fieldA=1))', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + }, + $expr1, + ]; + + yield [ + '(fieldA=1|fieldB=2)&fieldC=3', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1, $composite2): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->orX([$expr1, $expr2])->willReturn($composite1)->shouldBeCalled(); + $mock->andX([$composite1, $expr3])->willReturn($composite2)->shouldBeCalled(); + }, + $composite2, + ]; + + yield [ + 'fieldA=1|(fieldB=2&fieldC=3)', + static function (ExpressionBuilderInterface|ObjectProphecy $mock) use ($expr1, $expr2, $expr3, $composite1, $composite2): void { + $mock->eq('fieldA', 1)->willReturn($expr1)->shouldBeCalled(); + $mock->eq('fieldB', 2)->willReturn($expr2)->shouldBeCalled(); + $mock->eq('fieldC', 3)->willReturn($expr3)->shouldBeCalled(); + $mock->andX([$expr2, $expr3])->willReturn($composite1)->shouldBeCalled(); + $mock->orX([$expr1, $composite1])->willReturn($composite2)->shouldBeCalled(); + }, + $composite2, ]; } /** - * @dataProvider parseSuccessDataProvider + * @dataProvider provideParseSuccessCases + * + * @param mixed $expectedResult */ public function testParseSuccess(string $input, callable $configureExpressionBuilderMock, $expectedResult): void { $configureExpressionBuilderMock($this->expressionBuilderMock); - $this->assertEquals($expectedResult, $this->parser->parse($input)); + self::assertSame($expectedResult, $this->parser->parse($input)); } - public function forbiddenTokenDataProvider(): array + public static function provideForbiddenTokenCases(): iterable { - return [ - [','], - ['9'], - ['"string"'], - ["'string'"], - ['param'], - ['4.5'], - ['='], - ['≠'], - ['>'], - ['≥'], - ['<'], - ['≤'], - ['&'], - ['!&'], - ['|'], - ['!|'], - ['^|'], - ['⊕'], - ['('], - [')'], - ['['], - ['!['], - [']'], - ['{{'], - ['!{{'], - ['}}'], - ]; + yield [',']; + + yield ['9']; + + yield ['"string"']; + + yield ["'string'"]; + + yield ['param']; + + yield ['4.5']; + + yield ['=']; + + yield ['≠']; + + yield ['>']; + + yield ['≥']; + + yield ['<']; + + yield ['≤']; + + yield ['&']; + + yield ['!&']; + + yield ['|']; + + yield ['!|']; + + yield ['^|']; + + yield ['⊕']; + + yield ['(']; + + yield [')']; + + yield ['[']; + + yield ['![']; + + yield [']']; + + yield ['{{']; + + yield ['!{{']; + + yield ['}}']; } /** - * @dataProvider forbiddenTokenDataProvider + * @dataProvider provideForbiddenTokenCases */ public function testForbiddenToken(string $input): void { $this->expectException(InvalidExpressionException::class); + $this->expectExceptionMessage('Invalid expression.'); $this->parser->parse($input, Lexer::T_NONE); } public function testUnexpectedToken(): void { $this->expectException(InvalidExpressionException::class); - + $this->expectExceptionMessage('Invalid expression.'); $this->parser->parse('fieldA==foo=1'); } public function testUnsupportedToken(): void { $this->expectException(InvalidExpressionException::class); + $this->expectExceptionMessage('Invalid expression.'); $this->expressionBuilderMock->getSupportedTokenType()->willReturn(Lexer::T_NONE)->shouldBeCalled(); $this->parser->parse('fieldA=1'); diff --git a/tests/QueryStringParserTest.php b/tests/QueryStringParserTest.php index 558acf9..879aae2 100644 --- a/tests/QueryStringParserTest.php +++ b/tests/QueryStringParserTest.php @@ -1,201 +1,226 @@ '', - ], + 'param-A' => '', ], + ]; + + yield [ + 'param-A=', + 'param-A=', [ - 'param-A=', - 'param-A=', - [ - 'param-A' => '', - ], + 'param-A' => '', ], + ]; + + yield [ + 'param-A=valueA', + 'param-A=valueA', [ - 'param-A=valueA', - 'param-A=valueA', - [ - 'param-A' => 'valueA', - ], + 'param-A' => 'valueA', ], + ]; + + yield [ + 'param-A[]=valueA', + 'param-A[]=valueA', [ - 'param-A[]=valueA', - 'param-A[]=valueA', - [ - 'param-A' => ['valueA'], - ], + 'param-A' => ['valueA'], ], + ]; + + yield [ + 'param-A[subA]=valueA', + 'param-A[subA]=valueA', [ - 'param-A[subA]=valueA', - 'param-A[subA]=valueA', - [ - 'param-A' => ['subA' => 'valueA'], - ], + 'param-A' => ['subA' => 'valueA'], ], + ]; + + yield [ + 'param-A¶m-B', + 'param-A¶m-B', [ - 'param-A¶m-B', - 'param-A¶m-B', - [ - 'param-A' => '', - 'param-B' => '', - ], + 'param-A' => '', + 'param-B' => '', ], + ]; + + yield [ + 'param-A=¶m-B', + 'param-A=¶m-B', [ - 'param-A=¶m-B', - 'param-A=¶m-B', - [ - 'param-A' => '', - 'param-B' => '', - ], + 'param-A' => '', + 'param-B' => '', ], + ]; + + yield [ + 'param-A=valueA¶m-B', + 'param-A=valueA¶m-B', [ - 'param-A=valueA¶m-B', - 'param-A=valueA¶m-B', - [ - 'param-A' => 'valueA', - 'param-B' => '', - ], + 'param-A' => 'valueA', + 'param-B' => '', ], + ]; + + yield [ + 'param-A[]=valueA¶m-B', + 'param-A[]=valueA¶m-B', [ - 'param-A[]=valueA¶m-B', - 'param-A[]=valueA¶m-B', - [ - 'param-A' => ['valueA'], - 'param-B' => '', - ], + 'param-A' => ['valueA'], + 'param-B' => '', ], + ]; + + yield [ + 'param-A[subA]=valueA¶m-B', + 'param-A[subA]=valueA¶m-B', [ - 'param-A[subA]=valueA¶m-B', - 'param-A[subA]=valueA¶m-B', - [ - 'param-A' => ['subA' => 'valueA'], - 'param-B' => '', - ], + 'param-A' => ['subA' => 'valueA'], + 'param-B' => '', ], + ]; - // With Xpression + // With Xpression + yield [ + 'query{{valueA}}', + 'query{{valueA}}', [ - 'query{{valueA}}', - 'query{{valueA}}', - [ - 'query{{valueA}}' => '', - ], + 'query{{valueA}}' => '', ], + ]; + + yield [ + 'query={price{{test}}&price=6}', + 'query=price%7B%7Btest%7D%7D%26price%3D6', [ - 'query={price{{test}}&price=6}', - 'query=price%7B%7Btest%7D%7D%26price%3D6', - [ - 'query' => 'price{{test}}&price=6', - ], + 'query' => 'price{{test}}&price=6', ], + ]; + + yield [ + 'query={name{{test 2}}}', + 'query=name%7B%7Btest+2%7D%7D', [ - 'query={name{{test 2}}}', - 'query=name%7B%7Btest+2%7D%7D', - [ - 'query' => 'name{{test 2}}', - ], + 'query' => 'name{{test 2}}', ], + ]; + + yield [ + 'query={valueA}', + 'query=valueA', [ - 'query={valueA}', - 'query=valueA', - [ - 'query' => 'valueA', - ], + 'query' => 'valueA', ], + ]; + + yield [ + 'query[]={valueA}', + 'query[]=valueA', [ - 'query[]={valueA}', - 'query[]=valueA', - [ - 'query' => ['valueA'], - ], + 'query' => ['valueA'], ], + ]; + + yield [ + 'query[subA]={valueA}', + 'query[subA]=valueA', [ - 'query[subA]={valueA}', - 'query[subA]=valueA', - [ - 'query' => ['subA' => 'valueA'], - ], + 'query' => ['subA' => 'valueA'], ], + ]; + + yield [ + 'query-A={valueA}&query-B={valueB}', + 'query-A=valueA&query-B=valueB', [ - 'query-A={valueA}&query-B={valueB}', - 'query-A=valueA&query-B=valueB', - [ - 'query-A' => 'valueA', - 'query-B' => 'valueB', - ], + 'query-A' => 'valueA', + 'query-B' => 'valueB', ], + ]; + + yield [ + 'query-A[]={valueA1}&query-A[]={valueA2}&query-B={valueB}', + 'query-A[]=valueA1&query-A[]=valueA2&query-B=valueB', [ - 'query-A[]={valueA1}&query-A[]={valueA2}&query-B={valueB}', - 'query-A[]=valueA1&query-A[]=valueA2&query-B=valueB', - [ - 'query-A' => ['valueA1', 'valueA2'], - 'query-B' => 'valueB', - ], + 'query-A' => ['valueA1', 'valueA2'], + 'query-B' => 'valueB', ], + ]; + + yield [ + 'query-A[subA]={valueA}&query-B={valueB}', + 'query-A[subA]=valueA&query-B=valueB', [ - 'query-A[subA]={valueA}&query-B={valueB}', - 'query-A[subA]=valueA&query-B=valueB', - [ - 'query-A' => ['subA' => 'valueA'], - 'query-B' => 'valueB', - ], + 'query-A' => ['subA' => 'valueA'], + 'query-B' => 'valueB', ], + ]; - // Fail + // Fail + yield [ + 'query-A=valueA}', + 'query-A=valueA}', [ - 'query-A=valueA}', - 'query-A=valueA}', - [ - 'query-A' => 'valueA}', - ], + 'query-A' => 'valueA}', ], + ]; + + yield [ + 'query-A={valueA', + 'query-A={valueA', [ - 'query-A={valueA', - 'query-A={valueA', - [ - 'query-A' => '{valueA', - ], + 'query-A' => '{valueA', ], + ]; + + yield [ + 'query-A={}valueA', + 'query-A={}valueA', [ - 'query-A={}valueA', - 'query-A={}valueA', - [ - 'query-A' => '{}valueA', - ], + 'query-A' => '{}valueA', ], + ]; + + yield [ + 'query-A={{valueA}}', + 'query-A={{valueA}}', [ - 'query-A={{valueA}}', - 'query-A={{valueA}}', - [ - 'query-A' => '{{valueA}}', - ], + 'query-A' => '{{valueA}}', ], ]; } /** - * @dataProvider parseDataProvider + * @dataProvider provideParseCases */ - public function testParse(string $queryString, string $expectedQueryString, array $expectedGET) + public function testParse(string $queryString, string $expectedQueryString, array $expectedGET): void { $_SERVER['QUERY_STRING'] = $queryString; QueryStringParser::correctServerQueryString(); - $this->assertEquals($expectedQueryString, $_SERVER['QUERY_STRING']); - $this->assertEquals($expectedGET, $_GET); + self::assertSame($expectedQueryString, $_SERVER['QUERY_STRING']); + self::assertSame($expectedGET, $_GET); } }