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/.github/workflows/php.yml b/.github/workflows/php.yml new file mode 100644 index 0000000..586be4a --- /dev/null +++ b/.github/workflows/php.yml @@ -0,0 +1,64 @@ +name: PHP Composer + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + tests: + + runs-on: ubuntu-latest + + strategy: + matrix: + dependencies: [ "lowest", "normal" ] + + steps: + - uses: actions/checkout@v4 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php-${{ matrix.dependencies }}- + + - name: Install dependencies (mode ${{ matrix.dependencies }}) + run: | + if [ "${{ matrix.dependencies }}" = "lowest" ]; then + composer update --prefer-lowest --prefer-dist --no-progress + else + composer install --prefer-dist --no-progress + fi + + - name: Enable Xdebug + run: echo "XDEBUG_MODE=coverage" >> $GITHUB_ENV + + - name: Run test suite + run: vendor/bin/phpunit --coverage-clover coverage.xml --log-junit junit.xml + + - name: Upload test results to Codecov + if: ${{ !cancelled() && matrix.dependencies == 'normal' }} + uses: codecov/test-results-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + + - name: Upload coverage to Codecov + if: ${{ !cancelled() && matrix.dependencies == 'normal' }} + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.xml + flags: php + fail_ci_if_error: true diff --git a/.gitignore b/.gitignore index 7579f74..df1639f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ vendor composer.lock +.phpunit.cache +.phpunit.result.cache +.php-cs-fixer.cache +clover.xml +junit.xml +coverage.xml diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..5b3b013 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,27 @@ +ignoreDotFiles(false) + ->ignoreVCSIgnored(true) + ->in(__DIR__) +; + +return (new Config()) + ->setRiskyAllowed(true) + ->setRules([ + '@PHP74Migration' => true, + '@PHP74Migration:risky' => true, + '@PHPUnit100Migration:risky' => true, + '@PhpCsFixer' => true, + '@PhpCsFixer:risky' => false, + '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/.travis.yml b/.travis.yml deleted file mode 100644 index 5ad82f9..0000000 --- a/.travis.yml +++ /dev/null @@ -1,63 +0,0 @@ -language: php - -cache: - directories: - - vendor/ - -before_install: - # Disable xdebug if php version != 7.2 (only use xdebug for reports) - - if [[ $ENABLE_CODE_COVERAGE == false ]]; then phpenv config-rm xdebug.ini; fi - # Enable mongo/mongodb extension regarding php version - - echo "extension = ${MONGO_EXT_NAME}.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - echo "php ext-${MONGO_EXT_NAME} extension enabled." - # Require alcaeus/mongo-php-adapter for php >= 7 tests - - if [[ $TRAVIS_PHP_VERSION == 7.* ]]; then composer config "platform.ext-mongo" "1.6.16" && composer require alcaeus/mongo-php-adapter; fi - # Require doctine collections/orm/mongodb to launch ALL tests - - composer require doctrine/collections:">=1.0" doctrine/orm:">=2.0" doctrine/mongodb:">=1.0" - -env: - global: - - MONGO_EXT_NAME=mongo - - ENABLE_CODE_COVERAGE=false - -before_script: - - composer install - -script: - - ./vendor/bin/phpunit --coverage-clover=coverage.xml - -after_success: - - if [[ $ENABLE_CODE_COVERAGE ]]; then bash <(curl -s https://codecov.io/bash); fi - -matrix: - include: -#===== TESTS - - php: '5.3' - dist: precise - - php: '5.4' - - php: '5.5' - - php: '5.6' - - php: '7.0' - env: - - MONGO_EXT_NAME=mongodb - - php: '7.1' - env: - - MONGO_EXT_NAME=mongodb - - php: '7.2' - env: - - MONGO_EXT_NAME=mongodb - - ENABLE_CODE_COVERAGE=true -#===== CODE INSPECTION - - stage: 'code-inspection' - php: '7.2' - env: '' - before_install: '' - before_script: - - composer require phpmd/phpmd:"@stable" squizlabs/php_codesniffer:"@stable" - - composer install - install: skip - script: - - ./vendor/bin/phpcs --config-set ignore_warnings_on_exit 1 - - ./vendor/bin/phpcs --standard=PSR1 src/ - - ./vendor/bin/phpcs --standard=PSR2 src/ -# - ./vendor/bin/phpmd src/ text codesize,unusedcode,naming,design,cleancode diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0ca2e83 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +default: install test stan + +install: + composer install + +test: + vendor/bin/phpunit + +cs: + vendor/bin/php-cs-fixer fix -vvv + +analyse: test + vendor/bin/phpmetrics --junit --report-html=docs/phpmetrics/index.html src + vendor/bin/phpstan diff --git a/composer.json b/composer.json index 1e36adf..d3de94d 100644 --- a/composer.json +++ b/composer.json @@ -1,38 +1,54 @@ { - "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.2.1" + }, + "require-dev": { + "doctrine/collections": "^1.8.0", + "doctrine/orm": "^2.8.0", + "phpspec/prophecy": "^1.18", + "phpunit/phpunit": "^9.6", + "friendsofphp/php-cs-fixer": "^3.41", + "phpstan/phpstan": "^1.10", + "phpmetrics/phpmetrics": "^2.8" + }, + "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": ">=5.3", - "doctrine/lexer": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": ">=4.8.35", - "doctrine/collections": "^1.0", - "doctrine/orm": "^2.4.0" - }, - "suggest": { - "doctrine/collections": "If you want filter an ArrayCollection", - "doctrine/orm": "If you want filter an ORM query builder", - "doctrine/mongodb": "If you want filter an Mongodb query builder" - }, - "license": "MIT", - "autoload": { - "psr-4": { - "Symftony\\Xpression\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Tests\\Symftony\\Xpression\\": "tests/" - } - } } diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..b271dad --- /dev/null +++ b/docs/index.html @@ -0,0 +1,10 @@ + + + + + Symftony Xpression + + +WIP github pages + + 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 b/phpunit.xml new file mode 100644 index 0000000..1a73bc9 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,30 @@ + + + + + tests + + + + + + src + + + src/Exception + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index f62072a..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - tests - - - - - - src - - src/Exception - - - - diff --git a/src/Bridge/Doctrine/Common/ExpressionBuilderAdapter.php b/src/Bridge/Doctrine/Common/ExpressionBuilderAdapter.php index 4f59bfa..f14593a 100644 --- a/src/Bridge/Doctrine/Common/ExpressionBuilderAdapter.php +++ b/src/Bridge/Doctrine/Common/ExpressionBuilderAdapter.php @@ -1,161 +1,84 @@ expressionBuilder = $expressionBuilder; - } - - /** - * @return int - */ - public function getSupportedTokenType() + public function getSupportedTokenType(): int { return Lexer::T_ALL - Lexer::T_NOT_AND - Lexer::T_NOT_OR - Lexer::T_XOR - Lexer::T_NOT_DOUBLE_OPEN_CURLY_BRACKET; } - /** - * @param $value - * @param bool $isValue - * - * @return mixed - */ - public function parameter($value, $isValue = false) + public function parameter(mixed $value, bool $isValue = false): mixed { return $value; } - /** - * @param $value - * @return mixed - */ - public function string($value) + public function string(mixed $value): mixed { return $value; } - /** - * @param string $field - * - * @return Comparison - */ - public function isNull($field) + public function isNull(string $field): mixed { return $this->expressionBuilder->isNull($field); } - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function eq($field, $value) + public function eq(string $field, mixed $value): mixed { return $this->expressionBuilder->eq($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function neq($field, $value) + public function neq(string $field, mixed $value): mixed { return $this->expressionBuilder->neq($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function gt($field, $value) + public function gt(string $field, mixed $value): mixed { return $this->expressionBuilder->gt($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function gte($field, $value) + public function gte(string $field, mixed $value): mixed { return $this->expressionBuilder->gte($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function lt($field, $value) + public function lt(string $field, mixed $value): mixed { return $this->expressionBuilder->lt($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function lte($field, $value) + public function lte(string $field, mixed $value): mixed { return $this->expressionBuilder->lte($field, $value); } - /** - * @param string $field - * @param mixed $values - * - * @return Comparison - */ - public function in($field, array $values) + public function in(string $field, array $values): mixed { return $this->expressionBuilder->in($field, $values); } - /** - * @param string $field - * @param mixed $values - * - * @return Comparison - */ - public function notIn($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 /!\ - * - * @param string $field - * @param mixed $value - * - * @return Comparison + * /!\ Contains operator appear only in doctrine/common v1.1 /!\. */ - public function contains($field, $value) + public function contains(string $field, mixed $value): mixed { if (!method_exists($this->expressionBuilder, 'contains')) { throw new UnsupportedExpressionTypeException('contains'); @@ -164,55 +87,32 @@ public function contains($field, $value) return $this->expressionBuilder->contains($field, $value); } - /** - * @param string $field - * @param mixed $value - */ - public function notContains($field, $value) + public function notContains(string $field, mixed $value): mixed { throw new UnsupportedExpressionTypeException('notContains'); } - /** - * @param array $expressions - * - * @return CompositeExpression - */ - public function andX(array $expressions) + public function andX(array $expressions): mixed { - return call_user_func_array(array($this->expressionBuilder, 'andX'), $expressions); + return $this->expressionBuilder->andX(...$expressions); } - /** - * @param array $expressions - */ - public function nandX(array $expressions) + public function nandX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('nandX'); } - /** - * @param array $expressions - * - * @return CompositeExpression - */ - public function orX(array $expressions) + public function orX(array $expressions): mixed { - return call_user_func_array(array($this->expressionBuilder, 'orX'), $expressions); + return $this->expressionBuilder->orX(...$expressions); } - /** - * @param array $expressions - */ - public function norX(array $expressions) + public function norX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('norX'); } - /** - * @param array $expressions - */ - public function xorX(array $expressions) + public function xorX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('xorX'); } diff --git a/src/Bridge/Doctrine/MongoDb/ExprBuilder.php b/src/Bridge/Doctrine/MongoDb/ExprBuilder.php deleted file mode 100644 index 8d49666..0000000 --- a/src/Bridge/Doctrine/MongoDb/ExprBuilder.php +++ /dev/null @@ -1,228 +0,0 @@ -createExpr()->field($field)->equals(null); - } - - /** - * @param string $field - * @param mixed $value - * - * @return array - */ - public function eq($field, $value) - { - return $this->createExpr()->field($field)->equals($value); - } - - /** - * @param string $field - * @param mixed $value - * - * @return array - */ - public function neq($field, $value) - { - return $this->createExpr()->field($field)->notEqual($value); - } - - /** - * @param string $field - * @param mixed $value - * - * @return array - */ - public function gt($field, $value) - { - return $this->createExpr()->field($field)->gt($value); - } - - /** - * @param string $field - * @param mixed $value - * - * @return array - */ - public function gte($field, $value) - { - return $this->createExpr()->field($field)->gte($value); - } - - /** - * @param string $field - * @param mixed $value - * - * @return array - */ - public function lt($field, $value) - { - return $this->createExpr()->field($field)->lt($value); - } - - /** - * @param string $field - * @param mixed $value - * - * @return array - */ - public function lte($field, $value) - { - return $this->createExpr()->field($field)->lte($value); - } - - /** - * @param string $field - * @param mixed $values - * - * @return array - */ - public function in($field, array $values) - { - return $this->createExpr()->field($field)->in($values); - } - - /** - * @param string $field - * @param mixed $values - * - * @return array - */ - public function notIn($field, array $values) - { - return $this->createExpr()->field($field)->notIn($values); - } - - /** - * @param string $field - * @param mixed $value - * - * @return array - */ - public function contains($field, $value) - { - return $this->createExpr()->field($field)->equals(new \MongoRegex(sprintf('/.*%s.*/', $value))); - } - - /** - * @param string $field - * @param mixed $value - * - * @return array - */ - public function notContains($field, $value) - { - return $this->createExpr()->field($field)->equals(new \MongoRegex(sprintf('/^((?!%s).)*$/', $value))); - } - - /** - * @param array $expressions - * - * @return array - */ - public function andX(array $expressions) - { - $expr = $this->createExpr(); - foreach ($expressions as $expression) { - $expr->addAnd($expression); - } - - return $expr; - } - - /** - * @param array $expressions - */ - public function nandX(array $expressions) - { - throw new UnsupportedExpressionTypeException('nandX'); - } - - /** - * @param array $expressions - * - * @return array - */ - public function orX(array $expressions) - { - $expr = $this->createExpr(); - foreach ($expressions as $expression) { - $expr->addOr($expression); - } - - return $expr; - } - - /** - * @param array $expressions - * - * @return array - */ - public function norX(array $expressions) - { - $expr = $this->createExpr(); - foreach ($expressions as $expression) { - $expr->addNor($expression); - } - - return $expr; - } - - /** - * @param array $expressions - */ - public function xorX(array $expressions) - { - throw new UnsupportedExpressionTypeException('xorX'); - } -} diff --git a/src/Bridge/Doctrine/ORM/ExprAdapter.php b/src/Bridge/Doctrine/ORM/ExprAdapter.php index c9cd2de..1e1ac12 100644 --- a/src/Bridge/Doctrine/ORM/ExprAdapter.php +++ b/src/Bridge/Doctrine/ORM/ExprAdapter.php @@ -1,216 +1,114 @@ expr = $expr; } - /** - * @return int - */ - public function getSupportedTokenType() + public function getSupportedTokenType(): int { return Lexer::T_ALL - Lexer::T_NOT_AND - Lexer::T_NOT_OR - Lexer::T_XOR; } - /** - * @param $value - * @param bool $isValue - * - * @return mixed - */ - public function parameter($value, $isValue = false) + public function parameter(mixed $value, bool $isValue = false): mixed { return $isValue ? $this->expr->literal($value) : $value; } - /** - * @param $value - * @return Expr\Literal - */ - public function string($value) + public function string(mixed $value): mixed { return $this->expr->literal($value); } - /** - * @param string $field - * - * @return string - */ - public function isNull($field) + public function isNull(string $field): mixed { return $this->expr->isNull($field); } - /** - * @param string $field - * @param mixed $value - * - * @return Expr\Comparison - */ - public function eq($field, $value) + public function eq(string $field, mixed $value): mixed { return $this->expr->eq($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Expr\Comparison - */ - public function neq($field, $value) + public function neq(string $field, mixed $value): mixed { return $this->expr->neq($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Expr\Comparison - */ - public function gt($field, $value) + public function gt(string $field, mixed $value): mixed { return $this->expr->gt($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Expr\Comparison - */ - public function gte($field, $value) + public function gte(string $field, mixed $value): mixed { return $this->expr->gte($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Expr\Comparison - */ - public function lt($field, $value) + public function lt(string $field, mixed $value): mixed { return $this->expr->lt($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Expr\Comparison - */ - public function lte($field, $value) + public function lte(string $field, mixed $value): mixed { return $this->expr->lte($field, $value); } - /** - * @param string $field - * @param mixed $values - * - * @return Expr\Func - */ - public function in($field, array $values) + public function in(string $field, array $values): mixed { return $this->expr->in($field, $values); } - /** - * @param string $field - * @param mixed $values - * - * @return Expr\Func - */ - public function notIn($field, array $values) + public function notIn(string $field, array $values): mixed { return $this->expr->notIn($field, $values); } - /** - * @param string $field - * @param mixed $value - * - * @return Expr\Comparison - */ - public function contains($field, $value) + public function contains(string $field, mixed $value): mixed { return $this->expr->like($field, $value); } - /** - * @param string $field - * @param mixed $value - * - * @return Expr\Comparison - */ - public function notContains($field, $value) + public function notContains(string $field, mixed $value): mixed { return $this->expr->notLike($field, $value); } - /** - * @param array $expressions - * - * @return Expr\Andx - */ - public function andX(array $expressions) + public function andX(array $expressions): mixed { - return call_user_func_array(array($this->expr, 'andX'), $expressions); + return $this->expr->andX(...$expressions); } - /** - * @param array $expressions - */ - public function nandX(array $expressions) + public function nandX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('nandX'); } - /** - * @param array $expressions - * - * @return Expr\Orx - */ - public function orX(array $expressions) + public function orX(array $expressions): mixed { - return call_user_func_array(array($this->expr, 'orX'), $expressions); + return $this->expr->orX(...$expressions); } - /** - * @param array $expressions - */ - public function norX(array $expressions) + public function norX(array $expressions): mixed { throw new UnsupportedExpressionTypeException('norX'); } - /** - * @param array $expressions - */ - 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 new file mode 100644 index 0000000..209f9e2 --- /dev/null +++ b/src/Bridge/MongoDB/ExprBuilder.php @@ -0,0 +1,108 @@ + null]; + } + + public function eq(string $field, mixed $value): mixed + { + return [$field => ['$eq' => $value]]; + } + + public function neq(string $field, mixed $value): mixed + { + return [$field => ['$ne' => $value]]; + } + + public function gt(string $field, mixed $value): mixed + { + return [$field => ['$gt' => $value]]; + } + + public function gte(string $field, mixed $value): mixed + { + return [$field => ['$gte' => $value]]; + } + + public function lt(string $field, mixed $value): mixed + { + return [$field => ['$lt' => $value]]; + } + + public function lte(string $field, mixed $value): mixed + { + return [$field => ['$lte' => $value]]; + } + + public function in(string $field, array $values): mixed + { + return [$field => ['$in' => $values]]; + } + + public function notIn(string $field, array $values): mixed + { + return [$field => ['$nin' => $values]]; + } + + public function contains(string $field, mixed $value): mixed + { + return [$field => ['$regex' => $value]]; + } + + public function notContains(string $field, mixed $value): mixed + { + return ['$not' => $this->contains($field, $value)]; + } + + public function andX(array $expressions): mixed + { + return ['$and' => $expressions]; + } + + // Not A AND B = Not A OR Not B + public function nandX(array $expressions): mixed + { + return $this->orX(array_map(static fn ($expression) => ['$not' => $expression], $expressions)); + } + + public function orX(array $expressions): mixed + { + return ['$or' => $expressions]; + } + + public function norX(array $expressions): mixed + { + return ['$nor' => $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..8f25a9d 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..0a664b7 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..034b6d4 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..9eb9b87 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..30fdde6 --- /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..0d07256 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 89f1798..91a550e 100644 --- a/src/Expr/ClosureExpressionBuilder.php +++ b/src/Expr/ClosureExpressionBuilder.php @@ -1,24 +1,20 @@ $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() + 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) + public function parameter($value, $isValue = false): mixed { return $value; } - /** - * @param $value - * @return mixed - */ - public function string($value) + public function string(mixed $value): mixed { return $value; } - /** - * @param string $field - * - * @return callable - */ - 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); } - /** - * @param string $field - * @param mixed $value - * - * @return callable - */ - public function eq($field, $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; } - /** - * @param string $field - * @param mixed $value - * - * @return callable - */ - public function neq($field, $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; } - /** - * @param string $field - * @param mixed $value - * - * @return callable - */ - public function gt($field, $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; } - /** - * @param string $field - * @param mixed $value - * - * @return callable - */ - public function gte($field, $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; } - /** - * @param string $field - * @param mixed $value - * - * @return callable - */ - public function lt($field, $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; } - /** - * @param string $field - * @param mixed $value - * - * @return callable - */ - public function lte($field, $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; } - /** - * @param string $field - * @param mixed $values - * - * @return callable - */ - 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); } - /** - * @param string $field - * @param mixed $values - * - * @return callable - */ - 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); } - /** - * @param string $field - * @param mixed $value - * - * @return callable - */ - public function contains($field, $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); } - /** - * @param string $field - * @param mixed $value - * - * @return callable - */ - public function notContains($field, $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); } - /** - * @param array $expressions - * - * @return callable - */ - 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; @@ -254,28 +144,16 @@ public function andX(array $expressions) }; } - /** - * @param array $expressions - * - * @return callable - */ - 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); } - /** - * @param array $expressions - * - * @return callable - */ - 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; @@ -286,37 +164,26 @@ public function orX(array $expressions) }; } - /** - * @param array $expressions - * - * @return callable - */ - 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); } - /** - * @param array $expressions - * - * @return callable - */ - 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 5cbce80..38e0743 100644 --- a/src/Expr/ExpressionBuilderInterface.php +++ b/src/Expr/ExpressionBuilderInterface.php @@ -1,116 +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( - array('{type}', '{expressions}'), - array($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}
' + ); } - /** - * @return int - */ - public function getSupportedTokenType() + public function getSupportedTokenType(): int { return Lexer::T_ALL; } - /** - * @param $value - * @param bool $isValue - * - * @return mixed - */ - public function parameter($value, $isValue = false) + public function parameter(mixed $value, bool $isValue = false): mixed { return $value; } - /** - * @param $value - * @return mixed - */ - public function string($value) + public function string(mixed $value): mixed { - return '"' . $value . '"'; + return '"'.$value.'"'; } - /** - * @param string $field - * - * @return string - */ - public function isNull($field) + public function isNull(string $field): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, 'is', 'null')); + return ($this->comparisonHtmlBuilder)($field, 'is', 'null'); } - /** - * @param string $field - * @param mixed $value - * - * @return string - */ - public function eq($field, $value) + public function eq(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, '=', $value)); + return ($this->comparisonHtmlBuilder)($field, '=', $value); } - /** - * @param string $field - * @param mixed $value - * - * @return string - */ - public function neq($field, $value) + public function neq(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, '≠', $value)); + return ($this->comparisonHtmlBuilder)($field, '≠', $value); } - /** - * @param string $field - * @param mixed $value - * - * @return string - */ - public function gt($field, $value) + public function gt(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, '>', $value)); + return ($this->comparisonHtmlBuilder)($field, '>', $value); } - /** - * @param string $field - * @param mixed $value - * - * @return string - */ - public function gte($field, $value) + public function gte(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, '≥', $value)); + return ($this->comparisonHtmlBuilder)($field, '≥', $value); } - /** - * @param string $field - * @param mixed $value - * - * @return string - */ - public function lt($field, $value) + public function lt(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, '<', $value)); + return ($this->comparisonHtmlBuilder)($field, '<', $value); } - /** - * @param string $field - * @param mixed $value - * - * @return string - */ - public function lte($field, $value) + public function lte(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, '≤', $value)); + return ($this->comparisonHtmlBuilder)($field, '≤', $value); } - /** - * @param string $field - * @param mixed $values - * - * @return string - */ - public function in($field, array $values) + public function in(string $field, array $values): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, 'value in', implode(', ', $values))); + return ($this->comparisonHtmlBuilder)($field, 'value in', implode(', ', $values)); } - /** - * @param string $field - * @param mixed $values - * - * @return string - */ - public function notIn($field, array $values) + public function notIn(string $field, array $values): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, 'value not in', implode(', ', $values))); + return ($this->comparisonHtmlBuilder)($field, 'value not in', implode(', ', $values)); } - /** - * @param string $field - * @param mixed $value - * - * @return string - */ - public function contains($field, $value) + public function contains(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, 'contains', $value)); + return ($this->comparisonHtmlBuilder)($field, 'contains', $value); } - /** - * @param string $field - * @param mixed $value - * - * @return string - */ - public function notContains($field, $value) + public function notContains(string $field, mixed $value): mixed { - return call_user_func_array($this->comparisonHtmlBuilder, array($field, 'notContains', $value)); + return ($this->comparisonHtmlBuilder)($field, 'notContains', $value); } - /** - * @param array $expressions - * - * @return string - */ - public function andX(array $expressions) + public function andX(array $expressions): mixed { - return call_user_func_array($this->compositeHtmlBuilder, array($expressions, 'and')); + return ($this->compositeHtmlBuilder)($expressions, 'and'); } - /** - * @param array $expressions - * - * @return string - */ - public function nandX(array $expressions) + public function nandX(array $expressions): mixed { - return call_user_func_array($this->compositeHtmlBuilder, array($expressions, 'not-and')); + return ($this->compositeHtmlBuilder)($expressions, 'not-and'); } - /** - * @param array $expressions - * - * @return string - */ - public function orX(array $expressions) + public function orX(array $expressions): mixed { - return call_user_func_array($this->compositeHtmlBuilder, array($expressions, 'or')); + return ($this->compositeHtmlBuilder)($expressions, 'or'); } - /** - * @param array $expressions - * - * @return string - */ - public function norX(array $expressions) + public function norX(array $expressions): mixed { - return call_user_func_array($this->compositeHtmlBuilder, array($expressions, 'not-or')); + return ($this->compositeHtmlBuilder)($expressions, 'not-or'); } - /** - * @param array $expressions - * - * @return string - */ - public function xorX(array $expressions) + public function xorX(array $expressions): mixed { - return call_user_func_array($this->compositeHtmlBuilder, array($expressions, 'exclusive-or')); + return ($this->compositeHtmlBuilder)($expressions, 'exclusive-or'); } } diff --git a/src/Expr/MapperExpressionBuilder.php b/src/Expr/MapperExpressionBuilder.php index fc6d6ae..f983da8 100644 --- a/src/Expr/MapperExpressionBuilder.php +++ b/src/Expr/MapperExpressionBuilder.php @@ -1,141 +1,131 @@ expressionBuilder = $expressionBuilder; - $this->fieldMapping = $fieldMapping; - } + public function __construct( + private ExpressionBuilderInterface $expressionBuilder, + private array $fieldMapping = [], + ) {} - public function getSupportedTokenType() + public function getSupportedTokenType(): int { return $this->expressionBuilder->getSupportedTokenType(); } - public function parameter($value, $isValue = false) + public function parameter(mixed $value, bool $isValue = false): mixed { return $this->expressionBuilder->parameter($value, $isValue); } - public function string($value) + 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($field, $value) + public function eq(string $field, mixed $value): mixed { return $this->expressionBuilder->eq($this->mapField($field), $value); } - public function neq($field, $value) + public function neq(string $field, mixed $value): mixed { return $this->expressionBuilder->neq($this->mapField($field), $value); } - public function gt($field, $value) + public function gt(string $field, mixed $value): mixed { return $this->expressionBuilder->gt($this->mapField($field), $value); } - public function gte($field, $value) + public function gte(string $field, mixed $value): mixed { return $this->expressionBuilder->gte($this->mapField($field), $value); } - public function lt($field, $value) + public function lt(string $field, mixed $value): mixed { return $this->expressionBuilder->lt($this->mapField($field), $value); } - public function lte($field, $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($field, $value) + public function contains(string $field, mixed $value): mixed { return $this->expressionBuilder->contains($this->mapField($field), $value); } - public function notContains($field, $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); } return $field; } -} \ No newline at end of file +} diff --git a/src/Lexer.php b/src/Lexer.php index 980b584..82ce0d8 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 - ); + //'(?:[+-]?(?:(?:(?:[0-9]+|(?:[0-9]*[\.][0-9]+)|(?:[0-9]+[\.][0-9]*))[eE][+-]?[0-9]+)|(?:[0-9]*[\.][0-9]+)|(?:[0-9]+[\.][0-9]*)))', // number extended all float (.5 / 1.5 / -1.2e3) + ]; } /** @@ -67,10 +163,10 @@ protected function getCatchablePatterns() */ protected function getNonCatchablePatterns() { - return array( + return [ '\s+', '(.)', - ); + ]; } /** @@ -84,209 +180,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 e7dbc97..44bdb0d 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1,5 +1,7 @@ 15, Lexer::T_NOT_AND => 14, Lexer::T_OR => 10, Lexer::T_XOR => 9, Lexer::T_NOT_OR => 8, - ); + ]; - /** - * @var int Keep the lexer current index - */ - public $lexerIndex = 0; + private Lexer $lexer; - /** - * @var Lexer - */ - private $lexer; - - /** - * @var ExpressionBuilderInterface - */ - private $expressionBuilder; + private ExpressionBuilderInterface $expressionBuilder; /** * @var int Bitwise of all allowed operator. Default was Lexer::T_ALL */ - private $allowedTokenType; + private int $allowedTokenType; /** - * @var int Bitwise of ExpressionBuilder supported operator. + * @var int bitwise of ExpressionBuilder supported operator */ - private $supportedTokenType; + private int $supportedTokenType; - /** - * @param ExpressionBuilderInterface $expressionBuilder - */ public function __construct(ExpressionBuilderInterface $expressionBuilder) { $this->lexer = new Lexer(); @@ -59,28 +52,11 @@ public function __construct(ExpressionBuilderInterface $expressionBuilder) } /** - * @return int - */ - public function getAllowedTokenType() - { - return $this->allowedTokenType; - } - - /** - * @param $input - * @param null $allowedTokenType - * - * @return mixed - * * @throws InvalidExpressionException */ - public function parse($input, $allowedTokenType = null) + public function parse(string $input, ?int $allowedTokenType = null): mixed { - if (null !== $allowedTokenType && !is_integer($allowedTokenType)) { - throw new \InvalidArgumentException('Allowed operator must be an integer.'); - } - - $this->allowedTokenType = $allowedTokenType ?: Lexer::T_ALL; + $this->allowedTokenType = null !== $allowedTokenType ? $allowedTokenType : Lexer::T_ALL; $this->supportedTokenType = $this->expressionBuilder->getSupportedTokenType(); try { @@ -96,19 +72,15 @@ public function parse($input, $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; - $expressions = array(); + $expressions = []; $tokenPrecedence = null; $hasOpenParenthesis = false; @@ -123,7 +95,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)); @@ -142,7 +114,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; @@ -153,16 +127,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; + $expectedTokenType = Lexer::T_OPERAND; + 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)) { @@ -172,13 +154,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; } @@ -186,72 +170,97 @@ private function getExpression($previousExpression = null) $containsValue = $currentTokenValue; $expectedTokenType = Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET; $currentTokenValue = null; + break; } - - $expression = call_user_func_array(array($this->expressionBuilder, $comparisonMethod), array($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; + $expectedTokenType = Lexer::T_OPERAND; + break; + case Lexer::T_NOT_EQUALS: $comparisonMethod = 'neq'; - $expectedTokenType = Lexer::T_OPERANDE; + $expectedTokenType = Lexer::T_OPERAND; + break; + case Lexer::T_GREATER_THAN: $comparisonMethod = 'gt'; - $expectedTokenType = Lexer::T_OPERANDE; + $expectedTokenType = Lexer::T_OPERAND; + break; + case Lexer::T_GREATER_THAN_EQUALS: $comparisonMethod = 'gte'; - $expectedTokenType = Lexer::T_OPERANDE; + $expectedTokenType = Lexer::T_OPERAND; + break; + case Lexer::T_LOWER_THAN: $comparisonMethod = 'lt'; - $expectedTokenType = Lexer::T_OPERANDE; + $expectedTokenType = Lexer::T_OPERAND; + break; + case Lexer::T_LOWER_THAN_EQUALS: $comparisonMethod = 'lte'; - $expectedTokenType = Lexer::T_OPERANDE; + $expectedTokenType = Lexer::T_OPERAND; + break; + case Lexer::T_NOT_OPEN_SQUARE_BRACKET: $comparisonMethod = 'notIn'; - $comparisonMultipleOperande = array(); - $expectedTokenType = Lexer::T_OPERANDE | Lexer::T_CLOSE_SQUARE_BRACKET; + $comparisonMultipleOperande = []; + $expectedTokenType = Lexer::T_OPERAND | Lexer::T_CLOSE_SQUARE_BRACKET; + break; + case Lexer::T_OPEN_SQUARE_BRACKET: $comparisonMethod = 'in'; - $comparisonMultipleOperande = array(); - $expectedTokenType = Lexer::T_OPERANDE | Lexer::T_CLOSE_SQUARE_BRACKET; + $comparisonMultipleOperande = []; + $expectedTokenType = Lexer::T_OPERAND | Lexer::T_CLOSE_SQUARE_BRACKET; + break; + case Lexer::T_CLOSE_SQUARE_BRACKET: - $expression = call_user_func_array(array($this->expressionBuilder, $comparisonMethod), array($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; + $expectedTokenType = Lexer::T_OPERAND | 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; + $expectedTokenType = Lexer::T_OPERAND | Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET; + break; + case Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET: - $expression = call_user_func_array(array($this->expressionBuilder, $comparisonMethod), array($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: @@ -266,16 +275,18 @@ private function getExpression($previousExpression = null) $compositeOperator = $currentTokenType; $tokenPrecedence = $currentTokenPrecedence; $expectedTokenType = Lexer::T_OPEN_PARENTHESIS | Lexer::T_INPUT_PARAMETER; + break; } if ($currentTokenPrecedence < $tokenPrecedence) { $expressions[] = $expression; $expression = null; - $expressions = array($this->buildComposite($compositeOperator, $expressions)); + $expressions = [$this->buildComposite($compositeOperator, $expressions)]; $compositeOperator = $currentTokenType; $tokenPrecedence = $currentTokenPrecedence; $expectedTokenType = Lexer::T_OPEN_PARENTHESIS | Lexer::T_INPUT_PARAMETER; + break; } @@ -284,6 +295,7 @@ private function getExpression($previousExpression = null) $this->lexer->resetPosition($currentTokenIndex); $this->lexer->moveNext(); $expression = $this->getExpression($expression); + break; } @@ -292,6 +304,7 @@ private function getExpression($previousExpression = null) $currentTokenPrecedence, $tokenPrecedence )); + default: throw new \LogicException(sprintf( 'Token mismatch. Expected token %s given %s', @@ -305,7 +318,7 @@ private function getExpression($previousExpression = null) $expressions[] = $expression; } - if (count($expressions) === 1) { + if (1 === \count($expressions)) { return $expressions[0]; } @@ -313,35 +326,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 f8f4ddd..6c93a73 100644 --- a/tests/Bridge/Doctrine/Common/ExpressionBuilderAdapterTest.php +++ b/tests/Bridge/Doctrine/Common/ExpressionBuilderAdapterTest.php @@ -1,70 +1,61 @@ 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()); } - public function testParameter() + 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() + 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() + 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() - { - if (!class_exists('Doctrine\Common\Collections\ExpressionBuilder')) { - return array(); - } - - return array( - array('field', 'value'), - ); - } - /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testEq($field, $value) + public function testEq(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '=', 'value'), $this->expressionBuilderAdapter->eq($field, $value) ); @@ -72,13 +63,10 @@ public function testEq($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testNeq($field, $value) + public function testNeq(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '<>', 'value'), $this->expressionBuilderAdapter->neq($field, $value) ); @@ -86,13 +74,10 @@ public function testNeq($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testGt($field, $value) + public function testGt(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '>', 'value'), $this->expressionBuilderAdapter->gt($field, $value) ); @@ -100,13 +85,10 @@ public function testGt($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testGte($field, $value) + public function testGte(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '>=', 'value'), $this->expressionBuilderAdapter->gte($field, $value) ); @@ -114,13 +96,10 @@ public function testGte($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testLt($field, $value) + public function testLt(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '<', 'value'), $this->expressionBuilderAdapter->lt($field, $value) ); @@ -128,13 +107,10 @@ public function testLt($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testLte($field, $value) + public function testLte(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Comparison('field', '<=', 'value'), $this->expressionBuilderAdapter->lte($field, $value) ); @@ -142,89 +118,70 @@ public function testLte($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testIn($field, $value) + public function testIn(string $field, string $value): void { - $this->assertEquals( - new Comparison('field', 'IN', array('value')), - $this->expressionBuilderAdapter->in($field, array($value)) + self::assertEquals( + new Comparison('field', 'IN', ['value']), + $this->expressionBuilderAdapter->in($field, [$value]) ); } /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testNotIn($field, $value) + public function testNotIn(string $field, string $value): void { - $this->assertEquals( - new Comparison('field', 'NIN', array('value')), - $this->expressionBuilderAdapter->notIn($field, array($value)) + self::assertEquals( + new Comparison('field', 'NIN', ['value']), + $this->expressionBuilderAdapter->notIn($field, [$value]) ); } /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testContains($field, $value) + public function testContains(string $field, string $value): void { + $expected = new Comparison('field', 'CONTAINS', 'value'); + if (!method_exists('Doctrine\Common\Collections\ExpressionBuilder', 'contains')) { - $this->expectException('\Symftony\Xpression\Exception\Expr\UnsupportedExpressionTypeException'); + $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) ); } /** * @dataProvider comparisonDataProvider - * @expectedException \Symftony\Xpression\Exception\Expr\UnsupportedExpressionTypeException - * - * @param $field - * @param $value */ - public function testNotContains($field, $value) + 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() + public static function comparisonDataProvider(): iterable { if (!class_exists('Doctrine\Common\Collections\ExpressionBuilder')) { - return array(); + return []; } - return array( - array( - array( - new Comparison('fieldA', '=', 1), - new Comparison('fieldB', '>', 2) - ), - ), - ); + yield ['field', 'value']; } /** * @dataProvider compositeDataProvider - * - * @param array $expressions */ - public function testAndX(array $expressions) + public function testAndX(array $expressions): void { - $this->assertEquals( + self::assertEquals( new CompositeExpression('AND', $expressions), $this->expressionBuilderAdapter->andX($expressions) ); @@ -232,23 +189,20 @@ public function testAndX(array $expressions) /** * @dataProvider compositeDataProvider - * @expectedException \Symftony\Xpression\Exception\Expr\UnsupportedExpressionTypeException - * - * @param array $expressions */ - public function testNandX(array $expressions) + public function testNandX(array $expressions): void { + $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "nandX".'); $this->expressionBuilderAdapter->nandX($expressions); } /** * @dataProvider compositeDataProvider - * - * @param array $expressions */ - public function testOrX(array $expressions) + public function testOrX(array $expressions): void { - $this->assertEquals( + self::assertEquals( new CompositeExpression('OR', $expressions), $this->expressionBuilderAdapter->orX($expressions) ); @@ -256,23 +210,35 @@ public function testOrX(array $expressions) /** * @dataProvider compositeDataProvider - * @expectedException \Symftony\Xpression\Exception\Expr\UnsupportedExpressionTypeException - * - * @param array $expressions */ - public function testNorX(array $expressions) + public function testNorX(array $expressions): void { + $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "norX".'); $this->expressionBuilderAdapter->norX($expressions); } /** * @dataProvider compositeDataProvider - * @expectedException \Symftony\Xpression\Exception\Expr\UnsupportedExpressionTypeException - * - * @param array $expressions */ - public function testXorX(array $expressions) + public function testXorX(array $expressions): void { + $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "xorX".'); $this->expressionBuilderAdapter->xorX($expressions); } + + public static function compositeDataProvider(): iterable + { + if (!class_exists('Doctrine\Common\Collections\ExpressionBuilder')) { + return []; + } + + yield [ + [ + new Comparison('fieldA', '=', 1), + new Comparison('fieldB', '>', 2), + ], + ]; + } } diff --git a/tests/Bridge/Doctrine/Common/ParserTest.php b/tests/Bridge/Doctrine/Common/ParserTest.php index 493a5c5..fb3e6d9 100644 --- a/tests/Bridge/Doctrine/Common/ParserTest.php +++ b/tests/Bridge/Doctrine/Common/ParserTest.php @@ -1,15 +1,25 @@ 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() + /** + * @dataProvider provideParserCases + */ + public function testParser(string $input, Expression $expectedExpression): void + { + self::assertEquals($expectedExpression, $this->parser->parse($input)); + } + + public static function provideParserCases(): iterable { if (!class_exists('Doctrine\Common\Collections\ExpressionBuilder')) { - return array(); + return []; } - return array( - array( + return [ + [ 'fieldA=1', new Comparison('fieldA', '=', 1), - ), - array( + ], + [ 'fieldA="string"', new Comparison('fieldA', '=', 'string'), - ), - array( + ], + [ 'fieldA≥1', new Comparison('fieldA', '>=', 1), - ), - array( + ], + [ 'fieldA>=1', new Comparison('fieldA', '>=', 1), - ), - array( + ], + [ 'fieldA≤1', new Comparison('fieldA', '<=', 1), - ), - array( + ], + [ 'fieldA<=1', new Comparison('fieldA', '<=', 1), - ), - array( + ], + [ 'fieldA≠1', new Comparison('fieldA', '<>', 1), - ), - array( + ], + [ 'fieldA!=1', new Comparison('fieldA', '<>', 1), - ), - array( + ], + [ 'fieldA[1,2]', - new Comparison('fieldA', 'IN', array(1, 2)), - ), - array( + new Comparison('fieldA', 'IN', [1, 2]), + ], + [ 'fieldA![1,2]', - new Comparison('fieldA', 'NIN', array(1, 2)), - ), - array( + new Comparison('fieldA', 'NIN', [1, 2]), + ], + [ 'fieldA=1|fieldB=2|fieldC=3', - new CompositeExpression('OR', array( + new CompositeExpression('OR', [ new Comparison('fieldA', '=', 1), new Comparison('fieldB', '=', 2), new Comparison('fieldC', '=', 3), - )), - ), - array( + ]), + ], + [ 'fieldA=1&fieldB=2&fieldC=3', - new CompositeExpression('AND', array( + new CompositeExpression('AND', [ new Comparison('fieldA', '=', 1), new Comparison('fieldB', '=', 2), new Comparison('fieldC', '=', 3), - )), - ), + ]), + ], // Precedences - array( + [ 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', - new CompositeExpression('OR', array( + new CompositeExpression('OR', [ new Comparison('fieldA', '=', 1), new Comparison('fieldB', '=', 2), - new CompositeExpression('AND', array( + new CompositeExpression('AND', [ new Comparison('fieldC', '=', 3), new Comparison('fieldD', '=', 4), - )), - )), - ), - array( + ]), + ]), + ], + [ 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', - new CompositeExpression('OR', array( - new CompositeExpression('AND', array( + new CompositeExpression('OR', [ + new CompositeExpression('AND', [ new Comparison('fieldA', '=', 1), new Comparison('fieldB', '=', 2), new Comparison('fieldC', '=', 3), - )), + ]), new Comparison('fieldD', '=', 4), - )), - ), - array( + ]), + ], + [ 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', - new CompositeExpression('OR', array( - new CompositeExpression('AND', array( + new CompositeExpression('OR', [ + new CompositeExpression('AND', [ new Comparison('fieldA', '=', 1), new Comparison('fieldB', '=', 2), - )), - new CompositeExpression('AND', array( + ]), + new CompositeExpression('AND', [ new Comparison('fieldC', '=', 3), new Comparison('fieldD', '=', 4), - )), - )), - ), + ]), + ]), + ], - //Parenthesis - array( + // Parenthesis + [ '((fieldA=1))', new Comparison('fieldA', '=', 1), - ), - array( + ], + [ '(fieldA=1|fieldB=2)&fieldC=3', - new CompositeExpression('AND', array( - new CompositeExpression('OR', array( + new CompositeExpression('AND', [ + new CompositeExpression('OR', [ new Comparison('fieldA', '=', 1), new Comparison('fieldB', '=', 2), - )), + ]), new Comparison('fieldC', '=', 3), - )), - ), - array( + ]), + ], + [ 'fieldA=1|(fieldB=2&fieldC=3)', - new CompositeExpression('OR', array( + new CompositeExpression('OR', [ new Comparison('fieldA', '=', 1), - new CompositeExpression('AND', array( + new CompositeExpression('AND', [ new Comparison('fieldB', '=', 2), new Comparison('fieldC', '=', 3), - )), - )), - ), - ); + ]), + ]), + ], + ]; } /** - * @dataProvider parseSuccessDataProvider - * - * @param $input - * @param $expectedExpression + * @dataProvider provideParserThrowUnsupportedExpressionTypeExceptionCases */ - public function testParser($input, $expectedExpression) + public function testParserThrowUnsupportedExpressionTypeException(string $input): void { - $this->assertEquals($expectedExpression, $this->parser->parse($input)); + $this->expectException(InvalidExpressionException::class); + $this->parser->parse($input); } - public function unsupportedExpressionTypeDataProvider() + public static function provideParserThrowUnsupportedExpressionTypeExceptionCases(): iterable { if (!class_exists('Doctrine\Common\Collections\ExpressionBuilder')) { - return array(); + return []; } - return array( - array('fieldA=1|fieldB=2|fieldC=3⊕fieldD=4'), - array('fieldA=1!|fieldB=2!|fieldC=3'), - array('fieldA=1^|fieldB=2'), - array('fieldA=1⊕fieldB=2'), - array('fieldA=1!&fieldB=2!&fieldC=3'), - array('fieldA=1&fieldB=2&fieldC=3!&fieldD=4'), - array('fieldA=1|fieldB=2|fieldC=3!|fieldD=4'), - array('fieldA!{{1}}'), - ); - } - - /** - * @dataProvider unsupportedExpressionTypeDataProvider - * @expectedException \Symftony\Xpression\Exception\Parser\InvalidExpressionException - * - * @param $input - */ - public function testParserThrowUnsupportedExpressionTypeException($input) - { - $this->parser->parse($input); + return [ + ['fieldA=1|fieldB=2|fieldC=3⊕fieldD=4'], + ['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}}'], + ]; } } diff --git a/tests/Bridge/Doctrine/MongoDb/ExprBuilderTest.php b/tests/Bridge/Doctrine/MongoDb/ExprBuilderTest.php deleted file mode 100644 index 4d74928..0000000 --- a/tests/Bridge/Doctrine/MongoDb/ExprBuilderTest.php +++ /dev/null @@ -1,285 +0,0 @@ -markTestSkipped('This test is run when you have "doctrine/mongodb-odm" installed.'); - } - - $this->exprBuilder = new ExprBuilder(); - } - - public function testValueAsString() - { - $this->assertEquals('my_fake_data', $this->exprBuilder->valueAsString('my_fake_data')); - } - - public function testIsNull() - { - $field = 'fake_field'; - $this->assertEquals( - $this->createExpr()->field('fake_field')->equals(null), - $this->exprBuilder->isNull($field) - ); - } - - public function comparisonDataProvider() - { - if (!class_exists('Doctrine\MongoDB\Query\Expr')) { - return array(); - } - - return array( - array('field', 'value'), - ); - } - - /** - * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value - */ - public function testEq($field, $value) - { - $this->assertEquals( - $this->createExpr()->field('field')->equals('value'), - $this->exprBuilder->eq($field, $value) - ); - } - - /** - * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value - */ - public function testNeq($field, $value) - { - $this->assertEquals( - $this->createExpr()->field('field')->notEqual('value'), - $this->exprBuilder->neq($field, $value) - ); - } - - /** - * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value - */ - public function testGt($field, $value) - { - $this->assertEquals( - $this->createExpr()->field('field')->gt('value'), - $this->exprBuilder->gt($field, $value) - ); - } - - /** - * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value - */ - public function testGte($field, $value) - { - $this->assertEquals( - $this->createExpr()->field('field')->gte('value'), - $this->exprBuilder->gte($field, $value) - ); - } - - /** - * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value - */ - public function testLt($field, $value) - { - $this->assertEquals( - $this->createExpr()->field('field')->lt('value'), - $this->exprBuilder->lt($field, $value) - ); - } - - /** - * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value - */ - public function testLte($field, $value) - { - $this->assertEquals( - $this->createExpr()->field('field')->lte('value'), - $this->exprBuilder->lte($field, $value) - ); - } - - /** - * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value - */ - public function testIn($field, $value) - { - $this->assertEquals( - $this->createExpr()->field('field')->in(array('value')), - $this->exprBuilder->in($field, array($value)) - ); - } - - /** - * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value - */ - public function testNotIn($field, $value) - { - $this->assertEquals( - $this->createExpr()->field('field')->notIn(array('value')), - $this->exprBuilder->notIn($field, array($value)) - ); - } - - /** - * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value - */ - public function testContains($field, $value) - { - if (!class_exists('\MongoRegex')) { - $this->markTestSkipped('This test need "\MongoRegex" installed.'); - } - - $this->assertEquals( - $this->createExpr()->field('field')->equals(new \MongoRegex('/.*value.*/')), - $this->exprBuilder->contains($field, $value) - ); - } - - /** - * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value - */ - public function testNotContains($field, $value) - { - if (!class_exists('\MongoRegex')) { - $this->markTestSkipped('This test need "\MongoRegex" installed.'); - } - - $this->assertEquals( - $this->createExpr()->field('field')->equals(new \MongoRegex('/^((?!value).)*$/')), - $this->exprBuilder->notContains($field, $value) - ); - } - - public function compositeDataProvider() - { - if (!class_exists('Doctrine\MongoDB\Query\Expr')) { - return array(); - } - - return array( - array( - array( - array('fieldA' => 'value'), - array('fieldB' => array('$gt' => 2)), - ), - ), - ); - } - - /** - * @dataProvider compositeDataProvider - * - * @param array $expressions - */ - public function testAndX(array $expressions) - { - $this->assertEquals( - $this->createExpr() - ->addAnd($this->createExpr()->field('fieldA')->equals('value')) - ->addAnd($this->createExpr()->field('fieldB')->gt(2)), - $this->exprBuilder->andX($expressions) - ); - } - - /** - * @dataProvider compositeDataProvider - * @expectedException \Symftony\Xpression\Exception\Expr\UnsupportedExpressionTypeException - * - * @param array $expressions - */ - public function testNandX(array $expressions) - { - $this->exprBuilder->nandX($expressions); - } - - /** - * @dataProvider compositeDataProvider - * - * @param array $expressions - */ - public function testOrX(array $expressions) - { - $this->assertEquals( - $this->createExpr() - ->addOr($this->createExpr()->field('fieldA')->equals('value')) - ->addOr($this->createExpr()->field('fieldB')->gt(2)), - $this->exprBuilder->orX($expressions) - ); - } - - /** - * @dataProvider compositeDataProvider - * - * @param array $expressions - */ - public function testNorX(array $expressions) - { - $this->assertEquals( - $this->createExpr() - ->addNor($this->createExpr()->field('fieldA')->equals('value')) - ->addNor($this->createExpr()->field('fieldB')->gt(2)), - $this->exprBuilder->norX($expressions) - ); - } - - /** - * @dataProvider compositeDataProvider - * @expectedException \Symftony\Xpression\Exception\Expr\UnsupportedExpressionTypeException - * - * @param array $expressions - */ - public function testXorX(array $expressions) - { - $this->exprBuilder->xorX($expressions); - } - - private function createExpr() - { - return new Expr(); - } -} diff --git a/tests/Bridge/Doctrine/MongoDb/ParserTest.php b/tests/Bridge/Doctrine/MongoDb/ParserTest.php deleted file mode 100644 index cf473ef..0000000 --- a/tests/Bridge/Doctrine/MongoDb/ParserTest.php +++ /dev/null @@ -1,212 +0,0 @@ -markTestSkipped('This test is run when you have "doctrine/mongodb-odm" installed.'); - } - - $this->exprBuilder = new ExprBuilder(); - $this->parser = new Parser($this->exprBuilder); - } - - public function parseSuccessDataProvider() - { - if (!class_exists('Doctrine\MongoDB\Query\Expr')) { - return array(); - } - - return array( - array( - 'fieldA=1', - array('fieldA' => 1), - ), - array( - 'fieldA="string"', - array('fieldA' => 'string'), - ), - array( - 'fieldA≥1', - array('fieldA' => array('$gte' => 1)), - ), - array( - 'fieldA>=1', - array('fieldA' => array('$gte' => 1)), - ), - array( - 'fieldA>1', - array('fieldA' => array('$gt' => 1)), - ), - array( - 'fieldA≤1', - array('fieldA' => array('$lte' => 1)), - ), - array( - 'fieldA<=1', - array('fieldA' => array('$lte' => 1)), - ), - array( - 'fieldA<1', - array('fieldA' => array('$lt' => 1)), - ), - array( - 'fieldA≠1', - array('fieldA' => array('$ne' => 1)), - ), - array( - 'fieldA!=1', - array('fieldA' => array('$ne' => 1)), - ), - array( - 'fieldA[1,2]', - array('fieldA' => array('$in' => array(1, 2))), - ), - array( - 'fieldA![1,2]', - array('fieldA' => array('$nin' => array(1, 2))), - ), - array( - 'fieldA{{1}}', - array('fieldA' => new \MongoRegex('/.*1.*/')), - ), - array( - 'fieldA!{{1}}', - array('fieldA' => new \MongoRegex('/^((?!1).)*$/')), - ), - array( - 'fieldA=1|fieldB=2|fieldC=3', - array('$or' => array( - array('fieldA' => 1), - array('fieldB' => 2), - array('fieldC' => 3), - )), - ), - array( - 'fieldA=1&fieldB=2&fieldC=3', - array('$and' => array( - array('fieldA' => 1), - array('fieldB' => 2), - array('fieldC' => 3), - )), - ), - - // Precedences - array( - 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', - array('$or' => array( - array('fieldA' => 1), - array('fieldB' => 2), - array('$and' => array( - array('fieldC' => 3), - array('fieldD' => 4) - )), - )), - ), - array( - 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', - array('$or' => array( - array('$and' => array( - array('fieldA' => 1), - array('fieldB' => 2), - array('fieldC' => 3), - )), - array('fieldD' => 4) - )), - ), - array( - 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', - array('$or' => array( - array('$and' => array( - array('fieldA' => 1), - array('fieldB' => 2), - )), - array('$and' => array( - array('fieldC' => 3), - array('fieldD' => 4) - )) - )), - ), - - //Parenthesis - array( - '((fieldA=1))', - array('fieldA' => 1), - ), - array( - '(fieldA=1|fieldB=2)&fieldC=3', - array('$and' => array( - array('$or' => array( - array('fieldA' => 1), - array('fieldB' => 2), - )), - array('fieldC' => 3) - )), - ), - array( - 'fieldA=1|(fieldB=2&fieldC=3)', - array('$or' => array( - array('fieldA' => 1), - array('$and' => array( - array('fieldB' => 2), - array('fieldC' => 3) - )) - )), - ), - ); - } - - /** - * @dataProvider parseSuccessDataProvider - * - * @param $input - * @param $expectedExpression - */ - public function testParser($input, $expectedExpression) - { - $this->assertEquals($expectedExpression, $this->parser->parse($input)->getQuery()); - } - - public function unsupportedExpressionTypeDataProvider() - { - if (!class_exists('Doctrine\MongoDB\Query\Expr')) { - return array(); - } - - return array( - array('fieldA=1|fieldB=2|fieldC=3⊕fieldD=4'), - array('fieldA=1^|fieldB=2'), - array('fieldA=1⊕fieldB=2'), - array('fieldA=1!&fieldB=2!&fieldC=3'), - array('fieldA=1&fieldB=2&fieldC=3!&fieldD=4'), - ); - } - - /** - * @dataProvider unsupportedExpressionTypeDataProvider - * @expectedException \Symftony\Xpression\Exception\Parser\InvalidExpressionException - * - * @param $input - */ - public function testParserThrowUnsupportedExpressionTypeException($input) - { - $this->parser->parse($input); - } -} diff --git a/tests/Bridge/Doctrine/ORM/ExprAdapterTest.php b/tests/Bridge/Doctrine/ORM/ExprAdapterTest.php index 765b8dd..40afe0a 100644 --- a/tests/Bridge/Doctrine/ORM/ExprAdapterTest.php +++ b/tests/Bridge/Doctrine/ORM/ExprAdapterTest.php @@ -1,58 +1,45 @@ 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()); } - public function testValueAsString() - { - $this->assertEquals((new Expr())->literal('my_fake_data'), $this->exprAdapter->valueAsString('my_fake_data')); - } - - public function testIsNull() + public function testIsNull(): void { $field = 'fake_field'; - $this->assertEquals('fake_field IS NULL', $this->exprAdapter->isNull($field)); - } - - public function comparisonDataProvider() - { - if (!class_exists('Doctrine\ORM\Query\Expr')) { - return array(); - } - - return array( - array('field', 'value'), - ); + self::assertSame('fake_field IS NULL', $this->exprAdapter->isNull($field)); } /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testEq($field, $value) + 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) ); @@ -60,13 +47,10 @@ public function testEq($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testNeq($field, $value) + 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) ); @@ -74,13 +58,10 @@ public function testNeq($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testGt($field, $value) + 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) ); @@ -88,13 +69,10 @@ public function testGt($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testGte($field, $value) + 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) ); @@ -102,13 +80,10 @@ public function testGte($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testLt($field, $value) + 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) ); @@ -116,13 +91,10 @@ public function testLt($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testLte($field, $value) + 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) ); @@ -130,41 +102,32 @@ public function testLte($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testIn($field, $value) + public function testIn(string $field, string $value): void { - $this->assertEquals( - new Expr\Func('field IN', array("'value'")), - $this->exprAdapter->in($field, array($value)) + self::assertEquals( + new Expr\Func('field IN', ["'value'"]), + $this->exprAdapter->in($field, [$value]) ); } /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testNotIn($field, $value) + public function testNotIn(string $field, string $value): void { - $this->assertEquals( - new Expr\Func('field NOT IN', array("'value'")), - $this->exprAdapter->notIn($field, array($value)) + self::assertEquals( + new Expr\Func('field NOT IN', ["'value'"]), + $this->exprAdapter->notIn($field, [$value]) ); } /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testContains($field, $value) + public function testContains(string $field, string $value): void { - $this->assertEquals( + self::assertEquals( new Expr\Comparison('field', 'LIKE', $value), $this->exprAdapter->contains($field, $value) ); @@ -172,56 +135,32 @@ public function testContains($field, $value) /** * @dataProvider comparisonDataProvider - * - * @param $field - * @param $value */ - public function testNotContains($field, $value) + 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() + public static function comparisonDataProvider(): iterable { if (!class_exists('Doctrine\ORM\Query\Expr')) { - return array(); + return []; } - return array( - array(array( - new Expr\Comparison('field', Expr\Comparison::EQ, 'value'), - )), - array(array( - new Expr\Func('field', array('value')) - )), - array(array( - new Expr\Andx( - array( - new Expr\Comparison('field', Expr\Comparison::EQ, 'value'), - ) - ) - )), - array(array( - new Expr\Orx( - array( - new Expr\Comparison('field', Expr\Comparison::EQ, 'value'), - ) - ) - )), - ); + yield ['field', 'value']; } /** * @dataProvider compositeDataProvider * - * @param $expressions + * @param Expression[] $expressions */ - public function testAndX($expressions) + public function testAndX(array $expressions): void { - $this->assertEquals( + self::assertEquals( new Expr\Andx($expressions), $this->exprAdapter->andX($expressions) ); @@ -229,23 +168,24 @@ public function testAndX($expressions) /** * @dataProvider compositeDataProvider - * @expectedException \Symftony\Xpression\Exception\Expr\UnsupportedExpressionTypeException * - * @param $expressions + * @param Expression[] $expressions */ - public function testNandX($expressions) + public function testNandX(array $expressions): void { + $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "nandX".'); $this->exprAdapter->nandX($expressions); } /** * @dataProvider compositeDataProvider * - * @param $expressions + * @param Expression[] $expressions */ - public function testOrX($expressions) + public function testOrX(array $expressions): void { - $this->assertEquals( + self::assertEquals( new Expr\Orx($expressions), $this->exprAdapter->orX($expressions) ); @@ -253,23 +193,56 @@ public function testOrX($expressions) /** * @dataProvider compositeDataProvider - * @expectedException \Symftony\Xpression\Exception\Expr\UnsupportedExpressionTypeException * - * @param $expressions + * @param Expression[] $expressions */ - public function testNorX($expressions) + public function testNorX(array $expressions): void { + $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "norX".'); $this->exprAdapter->norX($expressions); } /** * @dataProvider compositeDataProvider - * @expectedException \Symftony\Xpression\Exception\Expr\UnsupportedExpressionTypeException * - * @param $expressions + * @param Expression[] $expressions */ - public function testXorX($expressions) + public function testXorX(array $expressions): void { + $this->expectException(UnsupportedExpressionTypeException::class); + $this->expectExceptionMessage('Unsupported expression type "xorX".'); $this->exprAdapter->xorX($expressions); } + + public static function compositeDataProvider(): iterable + { + if (!class_exists('Doctrine\ORM\Query\Expr')) { + return []; + } + + 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'), + ] + ), + ]]; + } } diff --git a/tests/Bridge/Doctrine/ORM/ParserTest.php b/tests/Bridge/Doctrine/ORM/ParserTest.php index 4b39c72..fbbf11e 100644 --- a/tests/Bridge/Doctrine/ORM/ParserTest.php +++ b/tests/Bridge/Doctrine/ORM/ParserTest.php @@ -1,207 +1,226 @@ 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() + /** + * @dataProvider provideParserCases + * + * @param mixed $expectedExpression + */ + public function testParser(string $input, $expectedExpression): void + { + self::assertEquals($expectedExpression, $this->parser->parse($input)); + } + + public static function provideParserCases(): iterable { if (!class_exists('Doctrine\ORM\Query\Expr')) { - return array(); + return []; } - return array( - array( - '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), + 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), - ), - array( - 'fieldA="string"', - new Expr\Comparison('fieldA', '=', (new Expr())->literal('string')), - ), - array( - 'fieldA≥1', - new Expr\Comparison('fieldA', '>=', 1), - ), - array( - 'fieldA>=1', - new Expr\Comparison('fieldA', '>=', 1), - ), - array( - 'fieldA≤1', - new Expr\Comparison('fieldA', '<=', 1), - ), - array( - 'fieldA<=1', - new Expr\Comparison('fieldA', '<=', 1), - ), - array( - 'fieldA≠1', - new Expr\Comparison('fieldA', '<>', 1), - ), - array( - 'fieldA!=1', - new Expr\Comparison('fieldA', '<>', 1), - ), - array( - 'fieldA[1,2]', - new Expr\Func('fieldA IN', array(1, 2)), - ), - array( - 'fieldA![1,2]', - new Expr\Func('fieldA NOT IN', array(1, 2)), - ), - array( - 'fieldA{{1}}', - new Expr\Comparison('fieldA', 'LIKE', 1), - ), - array( - 'fieldA!{{1}}', - new Expr\Comparison('fieldA', 'NOT LIKE', 1), - ), - array( - 'fieldA=1|fieldB=2|fieldC=3', - new Expr\Orx(array( + new Expr\Comparison('fieldB', '=', 2), + new Expr\Andx([ + new Expr\Comparison('fieldC', '=', 3), + new Expr\Comparison('fieldD', '=', 4), + ]), + ]), + ]; + + 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), - )), - ), - array( - 'fieldA=1&fieldB=2&fieldC=3', - new Expr\Andx(array( + ]), + new Expr\Comparison('fieldD', '=', 4), + ]), + ]; + + 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), + ]), + ]), + ]; + + // Parenthesis + yield [ + '((fieldA=1))', + new Expr\Comparison('fieldA', '=', 1), + ]; - // Precedences - array( - 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', - new Expr\Orx(array( + yield [ + '(fieldA=1|fieldB=2)&fieldC=3', + new Expr\Andx([ + new Expr\Orx([ new Expr\Comparison('fieldA', '=', 1), new Expr\Comparison('fieldB', '=', 2), - new Expr\Andx(array( - new Expr\Comparison('fieldC', '=', 3), - new Expr\Comparison('fieldD', '=', 4), - )), - )), - ), - array( - 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', - new Expr\Orx(array( - new Expr\Andx(array( - new Expr\Comparison('fieldA', '=', 1), - new Expr\Comparison('fieldB', '=', 2), - new Expr\Comparison('fieldC', '=', 3), - )), - new Expr\Comparison('fieldD', '=', 4), - )), - ), - array( - 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', - new Expr\Orx(array( - new Expr\Andx(array( - new Expr\Comparison('fieldA', '=', 1), - new Expr\Comparison('fieldB', '=', 2), - )), - new Expr\Andx(array( - new Expr\Comparison('fieldC', '=', 3), - new Expr\Comparison('fieldD', '=', 4), - )), - )), - ), - - //Parenthesis - array( - '((fieldA=1))', + ]), + new Expr\Comparison('fieldC', '=', 3), + ]), + ]; + + yield [ + 'fieldA=1|(fieldB=2&fieldC=3)', + new Expr\Orx([ new Expr\Comparison('fieldA', '=', 1), - ), - array( - '(fieldA=1|fieldB=2)&fieldC=3', - new Expr\Andx(array( - new Expr\Orx(array( - new Expr\Comparison('fieldA', '=', 1), - new Expr\Comparison('fieldB', '=', 2), - )), + new Expr\Andx([ + new Expr\Comparison('fieldB', '=', 2), new Expr\Comparison('fieldC', '=', 3), - )), - ), - array( - 'fieldA=1|(fieldB=2&fieldC=3)', - new Expr\Orx(array( - new Expr\Comparison('fieldA', '=', 1), - new Expr\Andx(array( - new Expr\Comparison('fieldB', '=', 2), - new Expr\Comparison('fieldC', '=', 3), - )), - )), - ), - ); + ]), + ]), + ]; } /** - * @dataProvider parseSuccessDataProvider - * - * @param $input - * @param $expectedExpression + * @dataProvider provideParserThrowUnsupportedExpressionTypeExceptionCases */ - public function testParser($input, $expectedExpression) + public function testParserThrowUnsupportedExpressionTypeException(string $input): void { - $this->assertEquals($expectedExpression, $this->parser->parse($input)); + $this->expectException(InvalidExpressionException::class); + $this->expectExceptionMessage('Invalid expression.'); + $this->parser->parse($input); } - public function unsupportedExpressionTypeDataProvider() + public static function provideParserThrowUnsupportedExpressionTypeExceptionCases(): iterable { if (!class_exists('Doctrine\ORM\Query\Expr')) { - return array(); + return []; } - return array( - array('fieldA=1!|fieldB=2!|fieldC=3'), - array('fieldA=1^|fieldB=2'), - array('fieldA=1⊕fieldB=2'), - array('fieldA=1!&fieldB=2!&fieldC=3'), - array('fieldA=1&fieldB=2&fieldC=3!&fieldD=4'), - array('fieldA=1|fieldB=2|fieldC=3!|fieldD=4'), - array('fieldA=1|fieldB=2|fieldC=3⊕fieldD=4'), - ); - } + yield ['fieldA=1!|fieldB=2!|fieldC=3']; - /** - * @dataProvider unsupportedExpressionTypeDataProvider - * @expectedException \Symftony\Xpression\Exception\Parser\InvalidExpressionException - * - * @param $input - */ - public function testParserThrowUnsupportedExpressionTypeException($input) - { - $this->parser->parse($input); + 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']; } } diff --git a/tests/Bridge/MongoDB/ExprBuilderTest.php b/tests/Bridge/MongoDB/ExprBuilderTest.php new file mode 100644 index 0000000..cd9d8e8 --- /dev/null +++ b/tests/Bridge/MongoDB/ExprBuilderTest.php @@ -0,0 +1,139 @@ +exprAdapter = new ExprBuilder(); + } + + public function testGetSupportedTokenType(): void + { + self::assertSame(Lexer::T_ALL - Lexer::T_XOR, $this->exprAdapter->getSupportedTokenType()); + } + + public function testIsNull(): void + { + $field = 'fake_field'; + self::assertSame(['fake_field' => null], $this->exprAdapter->isNull($field)); + } + + public function testEq(): void + { + self::assertSame( + ['fieldA' => ['$eq' => 1]], + $this->exprAdapter->eq('fieldA', 1) + ); + } + + public function testNeq(): void + { + self::assertSame( + ['fieldA' => ['$ne' => 1]], + $this->exprAdapter->neq('fieldA', 1) + ); + } + + public function testGt(): void + { + self::assertSame( + ['fieldA' => ['$gt' => 1]], + $this->exprAdapter->gt('fieldA', 1) + ); + } + + public function testGte(): void + { + self::assertSame( + ['fieldA' => ['$gte' => 1]], + $this->exprAdapter->gte('fieldA', 1) + ); + } + + public function testLt(): void + { + self::assertSame( + ['fieldA' => ['$lt' => 1]], + $this->exprAdapter->lt('fieldA', 1) + ); + } + + public function testLte(): void + { + self::assertSame( + ['fieldA' => ['$lte' => 1]], + $this->exprAdapter->lte('fieldA', 1) + ); + } + + public function testIn(): void + { + self::assertSame( + ['fieldA' => ['$in' => [1, 2]]], + $this->exprAdapter->in('fieldA', [1, 2]) + ); + } + + public function testNin(): void + { + self::assertSame( + ['fieldA' => ['$nin' => [1, 2]]], + $this->exprAdapter->notIn('fieldA', [1, 2]) + ); + } + + public function testAnd(): void + { + self::assertSame( + ['$and' => [['expression1'], ['expression2']]], + $this->exprAdapter->andX([['expression1'], ['expression2']]) + ); + } + + public function testNand(): void + { + self::assertSame( + ['$or' => [['$not' => ['expression1']], ['$not' => ['expression2']]]], + $this->exprAdapter->nandX([['expression1'], ['expression2']]) + ); + } + + public function testOr(): void + { + self::assertSame( + ['$or' => [['expression1'], ['expression2']]], + $this->exprAdapter->orX([['expression1'], ['expression2']]) + ); + } + + public function testNor(): void + { + self::assertSame( + ['$nor' => [['expression1'], ['expression2']]], + $this->exprAdapter->norX([['expression1'], ['expression2']]) + ); + } + + 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 new file mode 100644 index 0000000..85550b9 --- /dev/null +++ b/tests/Bridge/MongoDB/ParserTest.php @@ -0,0 +1,230 @@ +exprBuilder = new ExprBuilder(); + $this->parser = new Parser($this->exprBuilder); + } + + /** + * @dataProvider provideParserCases + */ + public function testParser(string $input, array $expectedExpression): void + { + self::assertSame($expectedExpression, $this->parser->parse($input)); + } + + public static function provideParserCases(): iterable + { + 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', + [ + '$or' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], + ['fieldC' => ['$eq' => 3]], + ], + ], + ]; + + yield [ + 'fieldA=1&fieldB=2&fieldC=3', + [ + '$and' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], + ['fieldC' => ['$eq' => 3]], + ], + ], + ]; + + // Precedences + yield [ + 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', + [ + '$or' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], + [ + '$and' => [ + ['fieldC' => ['$eq' => 3]], + ['fieldD' => ['$eq' => 4]], + ], + ], + ], + ], + ]; + + yield [ + 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', + [ + '$or' => [ + [ + '$and' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], + ['fieldC' => ['$eq' => 3]], + ], + ], + ['fieldD' => ['$eq' => 4]], + ], + ], + ]; + + yield [ + 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', + [ + '$or' => [ + [ + '$and' => [ + ['fieldA' => ['$eq' => 1]], + ['fieldB' => ['$eq' => 2]], + ], + ], + [ + '$and' => [ + ['fieldC' => ['$eq' => 3]], + ['fieldD' => ['$eq' => 4]], + ], + ], + ], + ], + ]; + + // 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]], + ], + ], + ]; + + yield [ + 'fieldA=1|(fieldB=2&fieldC=3)', + [ + '$or' => [ + ['fieldA' => ['$eq' => 1]], + [ + '$and' => [ + ['fieldB' => ['$eq' => 2]], + ['fieldC' => ['$eq' => 3]], + ], + ], + ], + ], + ]; + } + + /** + * @dataProvider provideParserThrowUnsupportedExpressionTypeExceptionCases + */ + public function testParserThrowUnsupportedExpressionTypeException(string $input): void + { + $this->expectException(InvalidExpressionException::class); + $this->expectExceptionMessage('Invalid expression.'); + $this->parser->parse($input); + } + + public static function provideParserThrowUnsupportedExpressionTypeExceptionCases(): iterable + { + yield ['fieldA=1^|fieldB=2']; + + yield ['fieldA=1⊕fieldB=2']; + + yield ['fieldA=1|fieldB=2|fieldC=3⊕fieldD=4']; + } +} diff --git a/tests/Expr/ClosureExpressionBuilderTest.php b/tests/Expr/ClosureExpressionBuilderTest.php index 8567730..b4289d4 100644 --- a/tests/Expr/ClosureExpressionBuilderTest.php +++ b/tests/Expr/ClosureExpressionBuilderTest.php @@ -1,304 +1,266 @@ exampleData = array( + $this->exampleData = [ 'field_null' => null, 'field_number_5' => 5, 'field_number_10' => 10, 'field_string' => 'my_fake_string', - ); + ]; $this->closureExpressionBuilder = new ClosureExpressionBuilder(); } - public function getObjectFieldValueDataProvider() - { - $object = new \stdClass(); - $object->property = 'fake_property'; - $object->_property4 = 'fake_property'; - - return array( - array( - array('fake_key' => 'fake_value'), - 'fake_key', - 'fake_value' - ), - array( - new FakeClass(), - 'property1', - 'fake_is_property' - ), - array( - new FakeClass(), - 'property2', - 'fake_get_property' - ), - array( - new FakeClass(), - 'callProperty', - 'getcallProperty' - ), - array( - new FakeArrayAccess(), - 'callProperty', - 'callProperty' - ), - array( - $object, - 'property', - 'fake_property' - ), - array( - new FakeClass2(), - '_property4', - 'property4' - ), - array( - $object, - '_property4', - 'fake_property' - ), - ); - } - /** - * @dataProvider getObjectFieldValueDataProvider - * - * @param $object - * @param $value - * @param $expectedResult + * @dataProvider provideGetObjectFieldValueCases */ - public function testGetObjectFieldValue($object, $value, $expectedResult) + 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() + public static function provideGetObjectFieldValueCases(): iterable { - $this->assertEquals(Lexer::T_ALL, $this->closureExpressionBuilder->getSupportedTokenType()); + 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'; + + yield [ + $object, + 'property', + 'fake_property', + ]; + + yield [ + $object, + '_property4', + 'fake_property', + ]; } - public function testParameter() + public function testGetSupportedTokenType(): 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(Lexer::T_ALL, $this->closureExpressionBuilder->getSupportedTokenType()); } - public function testString() + public function testParameter(): void { - $this->assertEquals('my_fake_data', $this->closureExpressionBuilder->string('my_fake_data')); + 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 isNullDataProvider() + public function testString(): void { - return array( - array('field_null', true), - array('field_number_5', false), - ); + self::assertSame('my_fake_data', $this->closureExpressionBuilder->string('my_fake_data')); } /** - * @dataProvider isNullDataProvider - * - * @param $field - * @param $expectedResult + * @dataProvider provideIsNullCases */ - public function testIsNull($field, $expectedResult) + public function testIsNull(mixed $field, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->isNull($field); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); } - public function eqDataProvider() + public static function provideIsNullCases(): iterable { - return array( - array('field_number_5', 1, false), - array('field_number_5', 5, true), - array('field_number_5', 10, false), - ); + yield ['field_null', true]; + + yield ['field_number_5', false]; } /** - * @dataProvider eqDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideEqCases */ - public function testEq($field, $value, $expectedResult) + 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() + public static function provideEqCases(): iterable { - return array( - array('field_number_5', 1, true), - array('field_number_5', 5, false), - array('field_number_5', 10, true), - ); + yield ['field_number_5', 1, false]; + + yield ['field_number_5', 5, true]; + + yield ['field_number_5', 10, false]; } /** - * @dataProvider neqDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideNeqCases */ - public function testNeq($field, $value, $expectedResult) + 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() + public static function provideNeqCases(): iterable { - return array( - array('field_number_5', 1, true), - array('field_number_5', 5, false), - array('field_number_5', 10, false), - ); + yield ['field_number_5', 1, true]; + + yield ['field_number_5', 5, false]; + + yield ['field_number_5', 10, true]; } /** - * @dataProvider gtDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideGtCases */ - public function testGt($field, $value, $expectedResult) + 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() + public static function provideGtCases(): iterable { - return array( - array('field_number_5', 1, true), - array('field_number_5', 5, true), - array('field_number_5', 10, false), - ); + yield ['field_number_5', 1, true]; + + yield ['field_number_5', 5, false]; + + yield ['field_number_5', 10, false]; } /** - * @dataProvider gteDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideGteCases */ - public function testGte($field, $value, $expectedResult) + 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() + public static function provideGteCases(): iterable { - return array( - array('field_number_5', 1, false), - array('field_number_5', 5, false), - array('field_number_5', 10, true), - ); + yield ['field_number_5', 1, true]; + + yield ['field_number_5', 5, true]; + + yield ['field_number_5', 10, false]; } /** - * @dataProvider ltDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideLtCases */ - public function testLt($field, $value, $expectedResult) + 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() + public static function provideLtCases(): iterable { - return array( - array('field_number_5', 1, false), - array('field_number_5', 5, true), - array('field_number_5', 10, true), - ); + yield ['field_number_5', 1, false]; + + yield ['field_number_5', 5, false]; + + yield ['field_number_5', 10, true]; } /** - * @dataProvider lteDataProvider - * - * @param $field - * @param $value - * @param $expectedResult + * @dataProvider provideLteCases */ - public function testLte($field, $value, $expectedResult) + 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() + public static function provideLteCases(): iterable { - return array( - array('field_number_5', array(1), false), - array('field_number_5', array(1, 2, 3, 4, 5), true), - ); + yield ['field_number_5', 1, false]; + + yield ['field_number_5', 5, true]; + + yield ['field_number_5', 10, true]; } /** * @dataProvider inDataProvider - * - * @param $field - * @param $value - * @param $expectedResult */ - public function testIn($field, $value, $expectedResult) + public function testIn(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->in($field, $value); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); @@ -306,39 +268,30 @@ public function testIn($field, $value, $expectedResult) /** * @dataProvider inDataProvider - * - * @param $field - * @param $value - * @param $expectedResult */ - public function testNotIn($field, $value, $expectedResult) + 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() + public static function inDataProvider(): iterable { - return array( - array('field_string', 'toto', false), - array('field_string', 'fake', true), - ); + yield ['field_number_5', [1], false]; + + yield ['field_number_5', [1, 2, 3, 4, 5], true]; } /** * @dataProvider containsDataProvider - * - * @param $field - * @param $value - * @param $expectedResult */ - public function testContains($field, $value, $expectedResult) + public function testContains(mixed $field, mixed $value, mixed $expectedResult): void { $expression = $this->closureExpressionBuilder->contains($field, $value); - $this->assertEquals( + self::assertSame( $expectedResult, $expression($this->exampleData) ); @@ -346,45 +299,31 @@ public function testContains($field, $value, $expectedResult) /** * @dataProvider containsDataProvider - * - * @param $field - * @param $value - * @param $expectedResult */ - public function testNotContains($field, $value, $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() + public static function containsDataProvider(): iterable { - return array( - array(array(false, false), false), - array(array(false, true), false), - array(array(true, false), false), - array(array(true, true), true), - ); + yield ['field_string', 'toto', false]; + + yield ['field_string', 'fake', true]; } /** * @dataProvider andXDataProvider - * - * @param array $expressions - * @param $expectedResult */ - public function testAndX(array $expressions, $expectedResult) + 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') ); @@ -392,49 +331,36 @@ public function testAndX(array $expressions, $expectedResult) /** * @dataProvider andXDataProvider - * - * @param array $expressions - * @param $expectedResult */ - public function testNandX(array $expressions, $expectedResult) + 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() + public static function andXDataProvider(): iterable { - return array( - array(array(false, false), false), - array(array(false, true), true), - array(array(true, false), true), - array(array(true, true), true), - ); + yield [[false, false], false]; + + yield [[false, true], false]; + + yield [[true, false], false]; + + yield [[true, true], true]; } /** * @dataProvider orXDataProvider - * - * @param array $expressions - * @param $expectedResult */ - public function testOrX(array $expressions, $expectedResult) + 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') ); @@ -442,66 +368,76 @@ public function testOrX(array $expressions, $expectedResult) /** * @dataProvider orXDataProvider - * - * @param array $expressions - * @param $expectedResult */ - public function testNorX(array $expressions, $expectedResult) + 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() + public static function orXDataProvider(): iterable { - return array( - array(array(false, false), false), - array(array(false, true), true), - array(array(true, false), true), - array(array(true, true), false), + yield [[false, false], false]; - array(array(false, false, false), false), - array(array(false, false, true), true), - array(array(false, true, false), true), - array(array(false, true, true), false), - array(array(true, false, false), true), - array(array(true, false, true), false), - array(array(true, true, false), false), - array(array(true, true, true), true), - ); + yield [[false, true], true]; + + yield [[true, false], true]; + + yield [[true, true], true]; } /** - * @dataProvider xorXDataProvider - * - * @param array $expressions - * @param $expectedResult + * @dataProvider provideXorXCases */ - public function testXorX(array $expressions, $expectedResult) + 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') ); } + + public static function provideXorXCases(): iterable + { + yield [[false, false], false]; + + 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]; + } } class FakeClass { + public function __call($name, $arguments) + { + return $name.implode(', ', $arguments); + } + public function isProperty1() { return 'fake_is_property'; @@ -521,11 +457,6 @@ public function getPROPERTY4() { return 'property4'; } - - public function __call($name, $arguments) - { - return $name . implode(', ', $arguments); - } } class FakeClass2 @@ -543,20 +474,17 @@ public function getPROPERTY4() class FakeArrayAccess implements \ArrayAccess { - public function offsetExists($offset) + public function offsetExists($offset): bool { + return false; } - public function offsetGet($offset) + public function offsetGet($offset): mixed { return $offset; } - public function offsetSet($offset, $value) - { - } + public function offsetSet($offset, $value): void {} - public function offsetUnset($offset) - { - } + public function offsetUnset($offset): void {} } diff --git a/tests/Expr/HtmlExpressionBuilderTest.php b/tests/Expr/HtmlExpressionBuilderTest.php index 66ee6d7..fb555c8 100644 --- a/tests/Expr/HtmlExpressionBuilderTest.php +++ b/tests/Expr/HtmlExpressionBuilderTest.php @@ -1,424 +1,381 @@ closureExpressionBuilder = new HtmlExpressionBuilder(); - } + private HtmlExpressionBuilder $htmlExpressionBuilder; - public function testGetSupportedTokenType() + protected function setUp(): void { - $this->assertEquals(Lexer::T_ALL, $this->closureExpressionBuilder->getSupportedTokenType()); + $this->htmlExpressionBuilder = new HtmlExpressionBuilder(); } - public function testParameter() + public function testGetSupportedTokenType(): 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(Lexer::T_ALL, $this->htmlExpressionBuilder->getSupportedTokenType()); } - public function testString() + public function testParameter(): void { - $this->assertEquals('"my_fake_data"', $this->closureExpressionBuilder->string('my_fake_data')); + 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 isNullDataProvider() + public function testString(): void { - return array( - array('field_null', '
field_null is null
'), - array('field_number_5', '
field_number_5 is null
'), - ); + self::assertSame('"my_fake_data"', $this->htmlExpressionBuilder->string('my_fake_data')); } /** - * @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 provideIsNullCases(): iterable { - return array( - array('field_number_5', 1, '
field_number_5 = 1
'), - array('field_number_5', 5, '
field_number_5 = 5
'), - array('field_number_5', 10, '
field_number_5 = 10
'), - ); + yield ['field_null', '
field_null is null
']; + + yield ['field_number_5', '
field_number_5 is null
']; } /** - * @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 provideEqCases(): iterable { - return array( - array('field_number_5', 1, '
field_number_5 ≠ 1
'), - array('field_number_5', 5, '
field_number_5 ≠ 5
'), - array('field_number_5', 10, '
field_number_5 ≠ 10
'), - ); + 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
'], + ]; } /** - * @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 provideNeqCases(): iterable { - return array( - array('field_number_5', 1, '
field_number_5 > 1
'), - array('field_number_5', 5, '
field_number_5 > 5
'), - array('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 provideGtCases(): iterable { - return array( - array('field_number_5', 1, '
field_number_5 ≥ 1
'), - array('field_number_5', 5, '
field_number_5 ≥ 5
'), - array('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 provideGteCases(): iterable { - return array( - array('field_number_5', 1, '
field_number_5 < 1
'), - array('field_number_5', 5, '
field_number_5 < 5
'), - array('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 provideLtCases(): iterable { - return array( - array('field_number_5', 1, '
field_number_5 ≤ 1
'), - array('field_number_5', 5, '
field_number_5 ≤ 5
'), - array('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 provideLteCases(): iterable { - return array( - array('field_number_5', array(1), '
field_number_5 value in 1
'), - array('field_number_5', array(1, 2, 3, 4, 5), '
field_number_5 value in 1, 2, 3, 4, 5
'), - ); + 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 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 provideInCases(): iterable { - return array( - array('field_number_5', array(1), '
field_number_5 value not in 1
'), - array('field_number_5', array(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 in 1
']; + + yield ['field_number_5', [1, 2, 3, 4, 5], '
field_number_5 value 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 provideNotInCases(): iterable { - return array( - array('field_string', 'toto', '
field_string contains toto
'), - array('field_string', 'fake', '
field_string contains fake
'), - ); + 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 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 provideContainsCases(): iterable { - return array( - array('field_string', 'toto', '
field_string notContains toto
'), - array('field_string', 'fake', '
field_string notContains fake
'), - ); + yield ['field_string', 'toto', '
field_string contains toto
']; + + yield ['field_string', 'fake', '
field_string contains fake
']; } /** - * @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 provideNotContainsCases(): iterable { - return array( - array(array('false', 'false'), '
andfalsefalse
'), - array(array('false', 'true'), '
andfalsetrue
'), - array(array('true', 'false'), '
andtruefalse
'), - array(array('true', 'true'), '
andtruetrue
'), - ); + return [ + ['field_string', 'toto', '
field_string notContains toto
'], + ['field_string', 'fake', '
field_string notContains fake
'], + ]; } /** - * @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 provideAndXCases(): iterable { - return array( - array(array('false', 'false'), '
not-andfalsefalse
'), - array(array('false', 'true'), '
not-andfalsetrue
'), - array(array('true', 'false'), '
not-andtruefalse
'), - array(array('true', 'true'), '
not-andtruetrue
'), - ); + yield [['false', 'false'], '
andfalsefalse
']; + + yield [['false', 'true'], '
andfalsetrue
']; + + yield [['true', 'false'], '
andtruefalse
']; + + yield [['true', 'true'], '
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 provideNandXCases(): iterable { - return array( - array(array('false', 'false'), '
orfalsefalse
'), - array(array('false', 'true'), '
orfalsetrue
'), - array(array('true', 'false'), '
ortruefalse
'), - array(array('true', 'true'), '
ortruetrue
'), - ); + yield [['false', 'false'], '
not-andfalsefalse
']; + + yield [['false', 'true'], '
not-andfalsetrue
']; + + yield [['true', 'false'], '
not-andtruefalse
']; + + yield [['true', 'true'], '
not-andtruetrue
']; } /** - * @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 provideOrXCases(): iterable { - return array( - array(array('false', 'false'), '
not-orfalsefalse
'), - array(array('false', 'true'), '
not-orfalsetrue
'), - array(array('true', 'false'), '
not-ortruefalse
'), - array(array('true', 'true'), '
not-ortruetrue
'), - ); + yield [['false', 'false'], '
orfalsefalse
']; + + yield [['false', 'true'], '
orfalsetrue
']; + + yield [['true', 'false'], '
ortruefalse
']; + + yield [['true', 'true'], '
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 provideNorXCases(): iterable { - return array( - array(array('false', 'false'), '
exclusive-orfalsefalse
'), - array(array('false', 'true'), '
exclusive-orfalsetrue
'), - array(array('true', 'false'), '
exclusive-ortruefalse
'), - array(array('true', 'true'), '
exclusive-ortruetrue
'), + yield [['false', 'false'], '
not-orfalsefalse
']; - array(array('false', 'false', 'false'), '
exclusive-orfalsefalsefalse
'), - array(array('false', 'false', 'true'), '
exclusive-orfalsefalsetrue
'), - array(array('false', 'true', 'false'), '
exclusive-orfalsetruefalse
'), - array(array('false', 'true', 'true'), '
exclusive-orfalsetruetrue
'), - array(array('true', 'false', 'false'), '
exclusive-ortruefalsefalse
'), - array(array('true', 'false', 'true'), '
exclusive-ortruefalsetrue
'), - array(array('true', 'true', 'false'), '
exclusive-ortruetruefalse
'), - array(array('true', 'true', 'true'), '
exclusive-ortruetruetrue
'), - ); + yield [['false', 'true'], '
not-orfalsetrue
']; + + yield [['true', 'false'], '
not-ortruefalse
']; + + yield [['true', 'true'], '
not-ortruetrue
']; } /** - * @dataProvider xorXDataProvider - * - * @param array $expressions - * @param $expectedResult + * @dataProvider provideXorXCases */ - public function testXorX(array $expressions, $expectedResult) + public function testXorX(array $expressions, string $expectedResult): void { - $this->assertEquals( + self::assertSame( $expectedResult, - $this->closureExpressionBuilder->xorX($expressions) + $this->htmlExpressionBuilder->xorX($expressions) ); } + + public static function provideXorXCases(): iterable + { + 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
']; + } } diff --git a/tests/Expr/MapperExpressionBuilderTest.php b/tests/Expr/MapperExpressionBuilderTest.php index a290a1d..85ca977 100644 --- a/tests/Expr/MapperExpressionBuilderTest.php +++ b/tests/Expr/MapperExpressionBuilderTest.php @@ -1,347 +1,315 @@ expressionBuilderMock = $this->prophesize(ExpressionBuilderInterface::class); + private Prophet $prophet; + protected function setUp(): void + { + $this->prophet = new Prophet(); + $this->expressionBuilderMock = $this->prophet->prophesize(ExpressionBuilderInterface::class); $this->mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal()); } - public function testGetSupportedTokenType() + protected function tearDown(): void { + $this->prophet->checkPredictions(); + } + + public function testGetSupportedTokenType(): void + { + $this->expectNotToPerformAssertions(); $this->expressionBuilderMock->getSupportedTokenType()->shouldBeCalled(); $this->mapperExpressionBuilder->getSupportedTokenType(); } - public function testParameter() + 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') ); } - public function testParameterAsValue() + 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) ); } - public function testString() + 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 - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testIsNull($fieldMapping, $field, $expectedMappedField) + public function testIsNull(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->isNull($expectedMappedField)->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->isNull($field) ); } /** - * @dataProvider fieldMappingProvider - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testEq($fieldMapping, $field, $expectedMappedField) + public function testEq(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->eq($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->eq($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testNeq($fieldMapping, $field, $expectedMappedField) + public function testNeq(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->neq($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->neq($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testGt($fieldMapping, $field, $expectedMappedField) + public function testGt(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->gt($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->gt($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testGte($fieldMapping, $field, $expectedMappedField) + public function testGte(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->gte($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->gte($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testLt($fieldMapping, $field, $expectedMappedField) + public function testLt(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->lt($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->lt($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testLte($fieldMapping, $field, $expectedMappedField) + public function testLte(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->lte($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->lte($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testIn($fieldMapping, $field, $expectedMappedField) + public function testIn(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->in($expectedMappedField, ['fake_value'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->in($field, ['fake_value']) ); } /** - * @dataProvider fieldMappingProvider - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testNotIn($fieldMapping, $field, $expectedMappedField) + public function testNotIn(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->notIn($expectedMappedField, ['fake_value'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->notIn($field, ['fake_value']) ); } /** - * @dataProvider fieldMappingProvider - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testContains($fieldMapping, $field, $expectedMappedField) + public function testContains(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->contains($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->contains($field, 'fake_value') ); } /** - * @dataProvider fieldMappingProvider - * - * @param $fieldMapping - * @param $field - * @param $expectedMappedField + * @dataProvider fieldMappingDataProvider */ - public function testNotContains($fieldMapping, $field, $expectedMappedField) + public function testNotContains(array $fieldMapping, mixed $field, mixed $expectedMappedField): void { $mapperExpressionBuilder = new MapperExpressionBuilder($this->expressionBuilderMock->reveal(), $fieldMapping); $this->expressionBuilderMock->notContains($expectedMappedField, 'fake_value')->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $mapperExpressionBuilder->notContains($field, 'fake_value') ); } - public function fieldMappingProvider() + public static function fieldMappingDataProvider(): iterable { - return array( - array( - array(), - 'fake_field', - 'fake_field', - ), - array( - array('*' => 'fake_%s_mapping'), - 'fake_field', - 'fake_fake_field_mapping', - ), - array( - array('other' => 'fake_%s_mapping'), - 'fake_field', - 'fake_field', - ), - array( - array('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', + ]; } - public function testAndX() + public function testAndX(): void { $this->expressionBuilderMock->andX(['fake_expression'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->andX(['fake_expression']) ); } - public function testNandX() + public function testNandX(): void { $this->expressionBuilderMock->nandX(['fake_expression'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->nandX(['fake_expression']) ); } - public function testOrX() + public function testOrX(): void { $this->expressionBuilderMock->orX(['fake_expression'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->orX(['fake_expression']) ); } - public function testNorX() + public function testNorX(): void { $this->expressionBuilderMock->norX(['fake_expression'])->willReturn('fake_return')->shouldBeCalled(); - $this->assertEquals( + self::assertSame( 'fake_return', $this->mapperExpressionBuilder->norX(['fake_expression']) ); } - public function testXorX() + 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 d9ebac3..f8f8413 100644 --- a/tests/LexerTest.php +++ b/tests/LexerTest.php @@ -1,241 +1,343 @@ lexer = new Lexer(); } - public function setInputSuccessDataProvider() - { - return array( - array( - 'a=1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 2), - ), - ), - array( - 'a.b=1', - array( - array('value' => 'a.b', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 3), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4), - ), - ), - array( - 'a-b=1', - array( - array('value' => 'a-b', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 3), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4), - ), - ), - array( - 'a≠1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '≠', 'type' => Lexer::T_NOT_EQUALS, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4), - ), - ), - array( - 'a!=1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '≠', 'type' => Lexer::T_NOT_EQUALS, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3), - ), - ), - array( - 'a="value"', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 1), - array('value' => 'value', 'type' => Lexer::T_STRING, 'position' => 2), - ), - ), - array( - "a='value'", - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 1), - array('value' => 'value', 'type' => Lexer::T_STRING, 'position' => 2), - ), - ), - array( - 'a>1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '>', 'type' => Lexer::T_GREATER_THAN, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 2), - ), - ), - array( - 'a>=1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '≥', 'type' => Lexer::T_GREATER_THAN_EQUALS, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3), - ), - ), - array( - 'a≥1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '≥', 'type' => Lexer::T_GREATER_THAN_EQUALS, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4), - ), - ), - array( - 'a<1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '<', 'type' => Lexer::T_LOWER_THAN, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 2), - ), - ), - array( - 'a<=1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '≤', 'type' => Lexer::T_LOWER_THAN_EQUALS, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3), - ), - ), - array( - 'a≤1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '≤', 'type' => Lexer::T_LOWER_THAN_EQUALS, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4), - ), - ), - array( - 'a|1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '|', 'type' => Lexer::T_OR, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 2), - ), - ), - array( - 'a!|1', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '!|', 'type' => Lexer::T_NOT_OR, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3), - ), - ), - array( - 'a[1,2]', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '[', 'type' => Lexer::T_OPEN_SQUARE_BRACKET, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 2), - array('value' => ',', 'type' => Lexer::T_COMMA, 'position' => 3), - array('value' => '2', 'type' => Lexer::T_INTEGER, 'position' => 4), - array('value' => ']', 'type' => Lexer::T_CLOSE_SQUARE_BRACKET, 'position' => 5), - ), - ), - array( - 'a![1,2]', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '![', 'type' => Lexer::T_NOT_OPEN_SQUARE_BRACKET, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3), - array('value' => ',', 'type' => Lexer::T_COMMA, 'position' => 4), - array('value' => '2', 'type' => Lexer::T_INTEGER, 'position' => 5), - array('value' => ']', 'type' => Lexer::T_CLOSE_SQUARE_BRACKET, 'position' => 6), - ), - ), - array( - 'a{{1}}', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '{{', 'type' => Lexer::T_DOUBLE_OPEN_CURLY_BRACKET, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 3), - array('value' => '}}', 'type' => Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET, 'position' => 4), - ), - ), - array( - 'a!{{1}}', - array( - array('value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0), - array('value' => '!{{', 'type' => Lexer::T_NOT_DOUBLE_OPEN_CURLY_BRACKET, 'position' => 1), - array('value' => '1', 'type' => Lexer::T_INTEGER, 'position' => 4), - array('value' => '}}', 'type' => Lexer::T_DOUBLE_CLOSE_CURLY_BRACKET, 'position' => 5), - ), - ), - ); - } - /** - * @dataProvider setInputSuccessDataProvider - * - * @param $input - * @param $expectedTokens + * @dataProvider provideSetInputSuccessCases */ - public function testSetInputSuccess($input, $expectedTokens) + 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) { - $this->assertEquals($expectedTokens[$i], $currentToken); + self::assertSame($expectedTokens[$i], $currentToken); $this->lexer->moveNext(); - $i++; + ++$i; } } - public function unexpectedValueExceptionProvider() + public static function provideSetInputSuccessCases(): iterable { - return array( - array( - '!', - ), - array( - '§', - ), - array( - '^', - ), - array( - ';', - ), - array( - ':', - ), - array( - '/', - ), - ); + yield [ + '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], + ], + ]; + + yield [ + 'a=1.5', + [ + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 0], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 1], + ['value' => 1.5, 'type' => Lexer::T_FLOAT, 'position' => 2], + ], + ]; + + yield [ + '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], + ], + ]; + + yield [ + '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], + ], + ]; + + yield [ + '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], + ], + ]; + + yield [ + '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], + ], + ]; + + 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], + ], + ]; + + 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], + ], + ]; + + 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], + ], + ]; + + yield [ + '(a=1)', + [ + ['value' => '(', 'type' => Lexer::T_OPEN_PARENTHESIS, 'position' => 0], + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 1], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 2], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 3], + ['value' => ')', 'type' => Lexer::T_CLOSE_PARENTHESIS, 'position' => 4], + ], + ]; + + yield [ + '(a=1)', + [ + ['value' => '(', 'type' => Lexer::T_OPEN_PARENTHESIS, 'position' => 0], + ['value' => 'a', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 1], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 2], + ['value' => 1, 'type' => Lexer::T_INTEGER, 'position' => 3], + ['value' => ')', 'type' => Lexer::T_CLOSE_PARENTHESIS, 'position' => 4], + ], + ]; + + yield [ + 'a=1&b=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], + ['value' => '&', 'type' => Lexer::T_AND, 'position' => 3], + ['value' => 'b', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 4], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 5], + ['value' => 2, 'type' => Lexer::T_INTEGER, 'position' => 6], + ], + ]; + + yield [ + 'a=1!&b=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], + ['value' => '!&', 'type' => Lexer::T_NOT_AND, 'position' => 3], + ['value' => 'b', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 5], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 6], + ['value' => 2, 'type' => Lexer::T_INTEGER, 'position' => 7], + ], + ]; + + yield [ + 'a=1⊕b=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], + ['value' => '⊕', 'type' => Lexer::T_XOR, 'position' => 3], + ['value' => 'b', 'type' => Lexer::T_INPUT_PARAMETER, 'position' => 6], + ['value' => '=', 'type' => Lexer::T_EQUALS, 'position' => 7], + ['value' => 2, 'type' => Lexer::T_INTEGER, 'position' => 8], + ], + ]; } /** - * @dataProvider unexpectedValueExceptionProvider - * - * @expectedException \Symftony\Xpression\Exception\Lexer\UnknownTokenTypeException - * @expectedExceptionMessageRegExp /Unknown token type ".+"\./ - * - * @param $input + * @dataProvider provideUnexpectedValueExceptionCases */ - public function testUnexpectedValueException($input) + public function testUnexpectedValueException(string $input): void { + $this->expectException(UnknownTokenTypeException::class); + $this->expectExceptionMessageMatches('/Unknown token type ".+"\./'); $this->lexer->setInput($input); } + + public static function provideUnexpectedValueExceptionCases(): iterable + { + yield ['!']; + + yield ['§']; + + yield ['^']; + + yield [';']; + + yield [':']; + + yield ['/']; + } + + /** + * @dataProvider getTokenSyntaxDataProvider + */ + public function testGetTokenSyntax(mixed $tokenType, array $expectedTokenSyntax): void + { + $this->assertSame($expectedTokenSyntax, Lexer::getTokenSyntax($tokenType)); + } + + public static function getTokenSyntaxDataProvider(): iterable + { + yield [ + Lexer::T_NONE, + [], + ]; + + yield [ + Lexer::T_ALL, + [',', 'simple float', 'simple integer', '/[a-z_][a-z0-9_]*/', '"value" or \'value\'', '=', '≠ or !=', '>', '≥ or >=', '<', '≤ or <=', '&', '!&', '|', '!|', '⊕ or ^|', '(', ')', '[', '![', ']', '{{', '!{{', '}}'], + ]; + + yield [ + Lexer::T_OPERAND, + ['simple float', 'simple integer', '/[a-z_][a-z0-9_]*/', '"value" or \'value\''], + ]; + } } diff --git a/tests/ParserTest.php b/tests/ParserTest.php index 363e74f..3f81bfa 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -1,440 +1,459 @@ expressionBuilderMock = $this->prophesize('Symftony\Xpression\Expr\ExpressionBuilderInterface'); + $this->expressionBuilderMock = (new Prophet())->prophesize(ExpressionBuilderInterface::class); $this->expressionBuilderMock->getSupportedTokenType()->willReturn(Lexer::T_ALL); $this->parser = new Parser($this->expressionBuilderMock->reveal()); } - public function parseSuccessDataProvider() - { - return array( - array( - 'fieldA=1', - array(array('eq', 'fieldA', 1, 'my_fake_comparison_A')), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA="string"', - array(array('eq', 'fieldA', 'my_fake_string', 'my_fake_comparison_A')), - array(array('valueAsString', 'string', 'my_fake_string')), - 'my_fake_comparison_A', - ), - array( - 'fieldA>1', - array(array('gt', 'fieldA', 1, 'my_fake_comparison_A')), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA≥1', - array( - array('gte', 'fieldA', 1, 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA>=1', - array( - array('gte', 'fieldA', 1, 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA<1', - array( - array('lt', 'fieldA', 1, 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA≤1', - array( - array('lte', 'fieldA', 1, 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA<=1', - array( - array('lte', 'fieldA', 1, 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA≠1', - array( - array('neq', 'fieldA', 1, 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA!=1', - array( - array('neq', 'fieldA', 1, 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA[1,2]', - array( - array('in', 'fieldA', array(1, 2), 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA![1,2]', - array( - array('notIn', 'fieldA', array(1, 2), 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A' - ), - array( - 'fieldA{{1}}', - array( - array('contains', 'fieldA', 1, 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A', - ), - array( - 'fieldA!{{1}}', - array( - array('notContains', 'fieldA', 1, 'my_fake_comparison_A') - ), - array(), - 'my_fake_comparison_A' - ), - - // Composite - array( - 'fieldA=1|fieldB=2|fieldC=3', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - ), - array( - array('orX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_orX_composite'), - ), - 'my_fake_orX_composite' - ), - array( - 'fieldA=1!|fieldB=2!|fieldC=3', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - ), - array( - array('norX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_norX_composite'), - ), - 'my_fake_norX_composite' - ), - array( - 'fieldA=1^|fieldB=2^|fieldC=3', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - ), - array( - array('xorX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_xorX_composite'), - ), - 'my_fake_xorX_composite' - ), - array( - 'fieldA=1⊕fieldB=2⊕fieldC=3', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - ), - array( - array('xorX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_xorX_composite'), - ), - 'my_fake_xorX_composite' - ), - array( - 'fieldA=1&fieldB=2&fieldC=3', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - ), - array( - array('andX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_andX_composite'), - ), - 'my_fake_andX_composite' - ), - array( - 'fieldA{{value}}&fieldB=2', - array( - array('contains', 'fieldA', 'value', 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - ), - array( - array('andX', array('my_fake_comparison_A', 'my_fake_comparison_B'), 'my_fake_andX_composite'), - ), - 'my_fake_andX_composite' - ), - array( - 'fieldA=1!&fieldB=2!&fieldC=3', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - ), - array( - array('nandX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_nandX_composite'), - ), - 'my_fake_nandX_composite' - ), - - // Precedences - array( - 'fieldA=1|fieldB=2|fieldC=3&fieldD=4', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - array('eq', 'fieldD', 4, 'my_fake_comparison_D'), - ), - array( - array('andX', array('my_fake_comparison_C', 'my_fake_comparison_D'), 'my_fake_andX_composite'), - array('orX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_andX_composite'), 'my_fake_orX_composite'), - ), - 'my_fake_orX_composite' - ), - array( - 'fieldA=1&fieldB=2&fieldC=3!&fieldD=4', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - array('eq', 'fieldD', 4, 'my_fake_comparison_D'), - ), - array( - array('andX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_andX_composite'), - array('nandX', array('my_fake_andX_composite', 'my_fake_comparison_D'), 'my_fake_orX_composite'), - ), - 'my_fake_orX_composite' - ), - array( - 'fieldA=1|fieldB=2|fieldC=3!|fieldD=4', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - array('eq', 'fieldD', 4, 'my_fake_comparison_D'), - ), - array( - array('orX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_orX_composite'), - array('norX', array('my_fake_orX_composite', 'my_fake_comparison_D'), 'my_fake_norX_composite'), - ), - 'my_fake_norX_composite' - ), - array( - 'fieldA=1&fieldB=2&fieldC=3|fieldD=4', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - array('eq', 'fieldD', 4, 'my_fake_comparison_D'), - ), - array( - array('andX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_andX_composite'), - array('orX', array('my_fake_andX_composite', 'my_fake_comparison_D'), 'my_fake_orX_composite'), - ), - 'my_fake_orX_composite' - ), - array( - 'fieldA=1&fieldB=2|fieldC=3&fieldD=4', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - array('eq', 'fieldD', 4, 'my_fake_comparison_D'), - ), - array( - array('andX', array('my_fake_comparison_A', 'my_fake_comparison_B'), 'my_fake_andX_composite_1'), - array('andX', array('my_fake_comparison_C', 'my_fake_comparison_D'), 'my_fake_andX_composite_2'), - array('orX', array('my_fake_andX_composite_1', 'my_fake_andX_composite_2'), 'my_fake_orX_composite'), - ), - 'my_fake_orX_composite' - ), - array( - 'fieldA=1|fieldB=2|fieldC=3⊕fieldD=4', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - array('eq', 'fieldD', 4, 'my_fake_comparison_D'), - ), - array( - array('orX', array('my_fake_comparison_A', 'my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_orX_composite'), - array('xorX', array('my_fake_orX_composite', 'my_fake_comparison_D'), 'my_fake_xorX_composite'), - ), - 'my_fake_xorX_composite' - ), - - //Parenthesis - array( - '((fieldA=1))', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - ), - array(), - 'my_fake_comparison_A' - ), - array( - '(fieldA=1|fieldB=2)&fieldC=3', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - ), - array( - array('orX', array('my_fake_comparison_A', 'my_fake_comparison_B'), 'my_fake_orX_composite'), - array('andX', array('my_fake_orX_composite', 'my_fake_comparison_C'), 'my_fake_andX_composite'), - ), - 'my_fake_andX_composite' - ), - array( - 'fieldA=1|(fieldB=2&fieldC=3)', - array( - array('eq', 'fieldA', 1, 'my_fake_comparison_A'), - array('eq', 'fieldB', 2, 'my_fake_comparison_B'), - array('eq', 'fieldC', 3, 'my_fake_comparison_C'), - ), - array( - array('andX', array('my_fake_comparison_B', 'my_fake_comparison_C'), 'my_fake_andX_composite'), - array('orX', array('my_fake_comparison_A', 'my_fake_andX_composite'), 'my_fake_orX_composite'), - ), - 'my_fake_orX_composite' - ), - ); - } - /** - * @dataProvider parseSuccessDataProvider + * @dataProvider provideParseSuccessCases * - * @param $input - * @param $comparisonMethods - * @param $compositeMethods - * @param $expectedResult + * @param mixed $expectedResult */ - public function testParseSuccess($input, $comparisonMethods, $compositeMethods, $expectedResult) + public function testParseSuccess(string $input, callable $configureExpressionBuilderMock, $expectedResult): void { - foreach ($comparisonMethods as $comparisonMethod) { - $this->expressionBuilderMock - ->{$comparisonMethod[0]}($comparisonMethod[1], $comparisonMethod[2]) - ->willReturn($comparisonMethod[3]) - ->shouldBeCalled(); - } - - foreach ($compositeMethods as $compositeMethod) { - $this->expressionBuilderMock - ->{$compositeMethod[0]}($compositeMethod[1]) - ->willReturn($compositeMethod[2]) - ->shouldBeCalled(); - } + $configureExpressionBuilderMock($this->expressionBuilderMock); - $this->assertEquals($expectedResult, $this->parser->parse($input)); + self::assertSame($expectedResult, $this->parser->parse($input)); } - public function forbiddenTokenDataProvider() + public static function provideParseSuccessCases(): iterable { - return array( - array(','), - array('9'), - array('"string"'), - array("'string'"), - array('param'), - array('4.5'), - array('='), - array('≠'), - array('>'), - array('≥'), - array('<'), - array('≤'), - array('&'), - array('!&'), - array('|'), - array('!|'), - array('^|'), - array('⊕'), - array('('), - array(')'), - array('['), - array('!['), - array(']'), - array('{{'), - array('!{{'), - array('}}'), - ); + $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 forbiddenTokenDataProvider - * @expectedException \Symftony\Xpression\Exception\Parser\InvalidExpressionException - * - * @param $input + * @dataProvider provideForbiddenTokenCases */ - public function testForbiddenToken($input) + public function testForbiddenToken(string $input): void { + $this->expectException(InvalidExpressionException::class); + $this->expectExceptionMessage('Invalid expression.'); $this->parser->parse($input, Lexer::T_NONE); } - /** - * @expectedException \Symftony\Xpression\Exception\Parser\InvalidExpressionException - */ - public function testUnexpectedToken() + public static function provideForbiddenTokenCases(): iterable { - $this->expressionBuilderMock->eq('fieldA', 'foo')->willReturn('fake_return'); + yield [',']; + + yield ['9']; + + yield ['"string"']; + + yield ["'string'"]; + + yield ['param']; + + yield ['4.5']; + + yield ['=']; + + yield ['≠']; + + yield ['>']; + + yield ['≥']; + + yield ['<']; + + yield ['≤']; + + yield ['&']; + + yield ['!&']; + + yield ['|']; + + yield ['!|']; + + yield ['^|']; - $this->parser->parse('fieldA=foo=1'); + yield ['⊕']; + + yield ['(']; + + yield [')']; + + yield ['[']; + + yield ['![']; + + yield [']']; + + yield ['{{']; + + yield ['!{{']; + + yield ['}}']; } - /** - * @expectedException \Symftony\Xpression\Exception\Parser\InvalidExpressionException - */ - public function testUnsupportedToken() + 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'); } + + public function testTokenPrecedenceError(): void + { + $this->expectException(InvalidExpressionException::class); + $this->expectExceptionMessage('Invalid expression.'); + $this->expressionBuilderMock->getSupportedTokenType()->willReturn(Lexer::T_ALL)->shouldBeCalled(); + + try { + $this->parser->parse('a&b', Lexer::T_NONE); + } catch (\Throwable $e) { + $this->assertInstanceOf(ForbiddenTokenException::class, $e->getPrevious()); + $this->assertEquals('Forbidden token "a". Allowed was .', $e->getPrevious()->getMessage()); + + throw $e; + } + } } diff --git a/tests/QueryStringParserTest.php b/tests/QueryStringParserTest.php index 6c4beed..b648ea8 100644 --- a/tests/QueryStringParserTest.php +++ b/tests/QueryStringParserTest.php @@ -1,205 +1,226 @@ '', - ) - ), - array( - 'param-A=', - 'param-A=', - array( - 'param-A' => '', - ) - ), - array( - 'param-A=valueA', - 'param-A=valueA', - array( - 'param-A' => 'valueA', - ) - ), - array( - 'param-A[]=valueA', - 'param-A[]=valueA', - array( - 'param-A' => array('valueA'), - ) - ), - array( - 'param-A[subA]=valueA', - 'param-A[subA]=valueA', - array( - 'param-A' => array('subA' => 'valueA'), - ) - ), - array( - 'param-A¶m-B', - 'param-A¶m-B', - array( - 'param-A' => '', - 'param-B' => '', - ) - ), - array( - 'param-A=¶m-B', - 'param-A=¶m-B', - array( - 'param-A' => '', - 'param-B' => '', - ) - ), - array( - 'param-A=valueA¶m-B', - 'param-A=valueA¶m-B', - array( - 'param-A' => 'valueA', - 'param-B' => '', - ) - ), - array( - 'param-A[]=valueA¶m-B', - 'param-A[]=valueA¶m-B', - array( - 'param-A' => array('valueA'), - 'param-B' => '', - ) - ), - array( - 'param-A[subA]=valueA¶m-B', - 'param-A[subA]=valueA¶m-B', - array( - 'param-A' => array('subA' => 'valueA'), - 'param-B' => '', - ) - ), - - // With Xpression - array( - 'query{{valueA}}', - 'query{{valueA}}', - array( - 'query{{valueA}}' => '', - ) - ), - array( - 'query={price{{test}}&price=6}', - 'query=price%7B%7Btest%7D%7D%26price%3D6', - array( - 'query' => 'price{{test}}&price=6', - ) - ), - array( - 'query={name{{test 2}}}', - 'query=name%7B%7Btest+2%7D%7D', - array( - 'query' => 'name{{test 2}}', - ) - ), - array( - 'query={valueA}', - 'query=valueA', - array( - 'query' => 'valueA', - ) - ), - array( - 'query[]={valueA}', - 'query[]=valueA', - array( - 'query' => array('valueA'), - ) - ), - array( - 'query[subA]={valueA}', - 'query[subA]=valueA', - array( - 'query' => array('subA' => 'valueA'), - ) - ), - array( - 'query-A={valueA}&query-B={valueB}', - 'query-A=valueA&query-B=valueB', - array( - 'query-A' => 'valueA', - 'query-B' => 'valueB', - ) - ), - array( - 'query-A[]={valueA1}&query-A[]={valueA2}&query-B={valueB}', - 'query-A[]=valueA1&query-A[]=valueA2&query-B=valueB', - array( - 'query-A' => array('valueA1', 'valueA2'), - 'query-B' => 'valueB', - ) - ), - array( - 'query-A[subA]={valueA}&query-B={valueB}', - 'query-A[subA]=valueA&query-B=valueB', - array( - 'query-A' => array('subA' => 'valueA'), - 'query-B' => 'valueB', - ) - ), - - // Fail - array( - 'query-A=valueA}', - 'query-A=valueA}', - array( - 'query-A' => 'valueA}', - ) - ), - array( - 'query-A={valueA', - 'query-A={valueA', - array( - 'query-A' => '{valueA', - ) - ), - array( - 'query-A={}valueA', - 'query-A={}valueA', - array( - 'query-A' => '{}valueA', - ) - ), - array( - 'query-A={{valueA}}', - 'query-A={{valueA}}', - array( - 'query-A' => '{{valueA}}', - ) - ), - ); - } - /** - * @dataProvider parseDataProvider - * - * @param $queryString - * @param $expectedQueryString - * @param $expectedGET + * @dataProvider provideParseCases */ - public function testParse($queryString, $expectedQueryString, $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); + } + + public static function provideParseCases(): iterable + { + // Default querystring + yield [ + 'param-A', + 'param-A', + [ + 'param-A' => '', + ], + ]; + + yield [ + 'param-A=', + 'param-A=', + [ + 'param-A' => '', + ], + ]; + + yield [ + 'param-A=valueA', + 'param-A=valueA', + [ + 'param-A' => 'valueA', + ], + ]; + + yield [ + 'param-A[]=valueA', + 'param-A[]=valueA', + [ + 'param-A' => ['valueA'], + ], + ]; + + yield [ + 'param-A[subA]=valueA', + 'param-A[subA]=valueA', + [ + 'param-A' => ['subA' => 'valueA'], + ], + ]; + + yield [ + 'param-A¶m-B', + 'param-A¶m-B', + [ + 'param-A' => '', + 'param-B' => '', + ], + ]; + + yield [ + 'param-A=¶m-B', + 'param-A=¶m-B', + [ + 'param-A' => '', + 'param-B' => '', + ], + ]; + + yield [ + 'param-A=valueA¶m-B', + 'param-A=valueA¶m-B', + [ + 'param-A' => 'valueA', + 'param-B' => '', + ], + ]; + + yield [ + 'param-A[]=valueA¶m-B', + 'param-A[]=valueA¶m-B', + [ + 'param-A' => ['valueA'], + 'param-B' => '', + ], + ]; + + yield [ + 'param-A[subA]=valueA¶m-B', + 'param-A[subA]=valueA¶m-B', + [ + 'param-A' => ['subA' => 'valueA'], + 'param-B' => '', + ], + ]; + + // With Xpression + yield [ + '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', + ], + ]; + + yield [ + 'query={name{{test 2}}}', + 'query=name%7B%7Btest+2%7D%7D', + [ + 'query' => 'name{{test 2}}', + ], + ]; + + yield [ + 'query={valueA}', + 'query=valueA', + [ + 'query' => 'valueA', + ], + ]; + + yield [ + 'query[]={valueA}', + 'query[]=valueA', + [ + 'query' => ['valueA'], + ], + ]; + + yield [ + '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', + ], + ]; + + yield [ + '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', + ], + ]; + + yield [ + 'query-A[subA]={valueA}&query-B={valueB}', + 'query-A[subA]=valueA&query-B=valueB', + [ + 'query-A' => ['subA' => 'valueA'], + 'query-B' => 'valueB', + ], + ]; + + // Fail + yield [ + 'query-A=valueA}', + 'query-A=valueA}', + [ + 'query-A' => 'valueA}', + ], + ]; + + yield [ + 'query-A={valueA', + 'query-A={valueA', + [ + 'query-A' => '{valueA', + ], + ]; + + yield [ + 'query-A={}valueA', + 'query-A={}valueA', + [ + 'query-A' => '{}valueA', + ], + ]; + + yield [ + 'query-A={{valueA}}', + 'query-A={{valueA}}', + [ + 'query-A' => '{{valueA}}', + ], + ]; } }