From 54a5b37c43ff34ac88dbd760408bbe885dd938be Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Mon, 24 Mar 2025 01:29:47 +0000 Subject: [PATCH 1/7] fix: add support for Lexer v1 (allowed by ORM < v2.15) --- .github/workflows/ci.yml | 28 ++++++++++++++-- .../AST/Functions/BaseVariadicFunction.php | 11 ++++--- src/MartinGeorgiev/Utils/DoctrineLexer.php | 33 +++++++++++++++++++ 3 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 src/MartinGeorgiev/Utils/DoctrineLexer.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57994f9c..8ec23d30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,17 +11,25 @@ permissions: jobs: tests: - name: "PHP ${{ matrix.php }} + Doctrine ORM ${{ matrix.doctrine-orm }}" + name: "PHP ${{ matrix.php }} + Doctrine ORM ${{ matrix.doctrine-orm }} + Doctrine Lexer ${{ matrix.doctrine-lexer }}" runs-on: ubuntu-latest strategy: fail-fast: false matrix: php: ['8.1', '8.2', '8.3', '8.4'] - doctrine-orm: ['2.14', '3.0', 'latest'] + doctrine-lexer: ['2.1', '3.0', 'latest'] + doctrine-orm: ['2.14', '2.18', '3.0', 'latest'] include: + - doctrine-orm: '2.14' + doctrine-lexer: '1.2' - php: '8.4' calculate-code-coverage: true + exclude: + - doctrine-orm: '2.14' + doctrine-lexer: '3.0' + - doctrine-orm: '3.0' + doctrine-lexer: '2.1' steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 @@ -46,10 +54,24 @@ jobs: restore-keys: | ${{ runner.os }}-php- - - name: Install composer dependencies + - name: Install Doctrine Lexer dependency + run: | + if [ "${{ matrix.doctrine-lexer }}" == "1.2" ]; then + composer require doctrine/lexer "~1.2" --dev --prefer-dist --no-interaction --no-progress + elif [ "${{ matrix.doctrine-lexer }}" == "2.1" ]; then + composer require doctrine/lexer "~2.1" --dev --prefer-dist --no-interaction --no-progress + elif [ "${{ matrix.doctrine-lexer }}" == "3.0" ]; then + composer require doctrine/lexer "~3.0" --dev --prefer-dist --no-interaction --no-progress + else + composer update --prefer-dist --no-interaction --no-progress + fi + + - name: Install Doctrine ORM dependency run: | if [ "${{ matrix.doctrine-orm }}" == "2.14" ]; then composer require doctrine/orm "~2.14" --prefer-dist --no-interaction --no-progress + elif [ "${{ matrix.doctrine-orm }}" == "2.18" ]; then + composer require doctrine/orm "~2.18" --prefer-dist --no-interaction --no-progress elif [ "${{ matrix.doctrine-orm }}" == "3.0" ]; then composer require doctrine/orm "~3.0" --prefer-dist --no-interaction --no-progress else diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php index df2a08ba..8a08b319 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php @@ -11,6 +11,7 @@ use Doctrine\ORM\Query\TokenType; use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Exception\InvalidArgumentForVariadicFunctionException; use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Exception\ParserException; +use MartinGeorgiev\Utils\DoctrineLexer; use MartinGeorgiev\Utils\DoctrineOrm; /** @@ -30,24 +31,24 @@ protected function feedParserWithNodes(Parser $parser): void try { // @phpstan-ignore-next-line $this->nodes[] = $parser->{$this->commonNodeMapping}(); - if ($lexer->lookahead?->type === null) { + $lookaheadType = DoctrineLexer::getLookaheadType($lexer); + if ($lookaheadType === null) { throw ParserException::missingLookaheadType(); } } catch (\Throwable $throwable) { throw ParserException::withThrowable($throwable); } - $aheadType = $lexer->lookahead->type; $shouldUseLexer = DoctrineOrm::isPre219(); - while (($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS) !== $aheadType) { - if (($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA) === $aheadType) { + while (($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS) !== $lookaheadType) { + if (($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA) === $lookaheadType) { $parser->match($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA); // @phpstan-ignore-next-line $this->nodes[] = $parser->{$this->commonNodeMapping}(); } - $aheadType = $lexer->lookahead->type; + $lookaheadType = DoctrineLexer::getLookaheadType($lexer); } $this->validateArguments($this->nodes); diff --git a/src/MartinGeorgiev/Utils/DoctrineLexer.php b/src/MartinGeorgiev/Utils/DoctrineLexer.php new file mode 100644 index 00000000..d4228bd1 --- /dev/null +++ b/src/MartinGeorgiev/Utils/DoctrineLexer.php @@ -0,0 +1,33 @@ +lookahead); + } + + /** + * @return mixed|null + */ + public static function getLookaheadType(Lexer $lexer) + { + if (self::isPre200($lexer)) { + // @phpstan-ignore-next-line + return $lexer->lookahead['type']; + } + + // @phpstan-ignore-next-line + return $lexer->lookahead?->type; + } +} From 04fbebfaf1c94417096555f71e5e0f8df712da5b Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Mon, 24 Mar 2025 01:34:27 +0000 Subject: [PATCH 2/7] test only old PHP for Lexer 1 --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ec23d30..75342033 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,8 @@ jobs: doctrine-lexer: ['2.1', '3.0', 'latest'] doctrine-orm: ['2.14', '2.18', '3.0', 'latest'] include: - - doctrine-orm: '2.14' + - php: '8.1' + doctrine-orm: '2.14' doctrine-lexer: '1.2' - php: '8.4' calculate-code-coverage: true From e8592ebbad952eeb5e513a623a7748363bf4458e Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Mon, 24 Mar 2025 20:06:23 +0000 Subject: [PATCH 3/7] Allow Composer to modify other package versions as needed to resolve dependency conflicts when installing ORM --- .github/workflows/ci.yml | 6 +++--- src/MartinGeorgiev/Utils/DoctrineLexer.php | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75342033..25318c07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,11 +70,11 @@ jobs: - name: Install Doctrine ORM dependency run: | if [ "${{ matrix.doctrine-orm }}" == "2.14" ]; then - composer require doctrine/orm "~2.14" --prefer-dist --no-interaction --no-progress + composer require doctrine/orm "~2.14" --prefer-dist --no-interaction --no-progress --with-all-dependencies elif [ "${{ matrix.doctrine-orm }}" == "2.18" ]; then - composer require doctrine/orm "~2.18" --prefer-dist --no-interaction --no-progress + composer require doctrine/orm "~2.18" --prefer-dist --no-interaction --no-progress --with-all-dependencies elif [ "${{ matrix.doctrine-orm }}" == "3.0" ]; then - composer require doctrine/orm "~3.0" --prefer-dist --no-interaction --no-progress + composer require doctrine/orm "~3.0" --prefer-dist --no-interaction --no-progress --with-all-dependencies else composer update --prefer-dist --no-interaction --no-progress fi diff --git a/src/MartinGeorgiev/Utils/DoctrineLexer.php b/src/MartinGeorgiev/Utils/DoctrineLexer.php index d4228bd1..e79215f6 100644 --- a/src/MartinGeorgiev/Utils/DoctrineLexer.php +++ b/src/MartinGeorgiev/Utils/DoctrineLexer.php @@ -11,6 +11,12 @@ */ final class DoctrineLexer { + /** + * Checks if the Lexer is prior to version 2.0.0. + * + * In Lexer versions prior to 2.0.0, the lookahead property is an array, + * while in 2.0.0+ it's an object. + */ public static function isPre200(Lexer $lexer): bool { // @phpstan-ignore-next-line From bfa855d2adf6e12bf1d28c6c6b704e9bc07c18b2 Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Mon, 24 Mar 2025 20:11:45 +0000 Subject: [PATCH 4/7] phpstan :) --- .../baselines/{lexer-constants.neon => lexer-variations.neon} | 2 ++ ci/phpstan/config.neon | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) rename ci/phpstan/baselines/{lexer-constants.neon => lexer-variations.neon} (81%) diff --git a/ci/phpstan/baselines/lexer-constants.neon b/ci/phpstan/baselines/lexer-variations.neon similarity index 81% rename from ci/phpstan/baselines/lexer-constants.neon rename to ci/phpstan/baselines/lexer-variations.neon index 943744fd..ec18b245 100644 --- a/ci/phpstan/baselines/lexer-constants.neon +++ b/ci/phpstan/baselines/lexer-variations.neon @@ -11,3 +11,5 @@ parameters: - '#Fetching deprecated class constant T_DISTINCT of class Doctrine\\ORM\\Query\\Lexer#' - '#Parameter \#1 \$token of method Doctrine\\ORM\\Query\\Parser::match\(\) expects Doctrine\\ORM\\Query\\TokenType, mixed given.#' - '#Parameter \#1 \$type of method Doctrine\\Common\\Lexer\\AbstractLexer::isNextToken\(\) expects Doctrine\\ORM\\Query\\TokenType, mixed given.#' + - '#Parameter \#1 \$token of method Doctrine\\ORM\\Query\\Parser::match\(\) expects int\|string, mixed given.#' + - '#Parameter \#1 \$type of method Doctrine\\Common\\Lexer\\AbstractLexer::isNextToken\(\) expects int\|string, mixed given.#' diff --git a/ci/phpstan/config.neon b/ci/phpstan/config.neon index 22d0a60e..1c0d0f1f 100644 --- a/ci/phpstan/config.neon +++ b/ci/phpstan/config.neon @@ -4,7 +4,7 @@ includes: - ../../vendor/phpstan/phpstan-doctrine/extension.neon - ../../vendor/phpstan/phpstan-phpunit/extension.neon - ./baselines/deprecated-methods.neon - - ./baselines/lexer-constants.neon + - ./baselines/lexer-variations.neon - ./baselines/phpstan-identifiers.neon - ./baselines/type-mismatches.neon From 786fe0036685ee18d460f6b0bceccac9354eda43 Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Mon, 24 Mar 2025 20:22:35 +0000 Subject: [PATCH 5/7] baseline exceptions --- ci/phpstan/baselines/lexer-variations.neon | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/phpstan/baselines/lexer-variations.neon b/ci/phpstan/baselines/lexer-variations.neon index ec18b245..de961a18 100644 --- a/ci/phpstan/baselines/lexer-variations.neon +++ b/ci/phpstan/baselines/lexer-variations.neon @@ -13,3 +13,5 @@ parameters: - '#Parameter \#1 \$type of method Doctrine\\Common\\Lexer\\AbstractLexer::isNextToken\(\) expects Doctrine\\ORM\\Query\\TokenType, mixed given.#' - '#Parameter \#1 \$token of method Doctrine\\ORM\\Query\\Parser::match\(\) expects int\|string, mixed given.#' - '#Parameter \#1 \$type of method Doctrine\\Common\\Lexer\\AbstractLexer::isNextToken\(\) expects int\|string, mixed given.#' + - '#Parameter \#1 \$token of method Doctrine\\ORM\\Query\\Parser::match\(\) expects 1\|2\|3\|4\|5\|6\|7\|8\|9\|10\|11\|12\|13\|14\|15\|16\|17\|18\|19\|100\|101\|102\|200\|201\|202\|203\|204\|205\|206\|207\|208\|209\|210\|211\|212\|213\|214\|215\|216\|217\|218\|219\|220\|221\|222\|223\|224\|225\|226\|227\|228\|229\|230\|231\|232\|233\|234\|235\|236\|237\|238\|239\|240\|241\|242\|243\|244\|245\|246\|247\|248\|249\|250\|251\|252\|253\|254\|255\|256, mixed given.#' + - '#Parameter \#1 \$type of method Doctrine\\Common\\Lexer\\AbstractLexer::isNextToken\(\) expects int\|string, mixed given.#' From 46069d23afceccafa53e012ddec168e0364f4eed Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Mon, 24 Mar 2025 21:44:17 +0000 Subject: [PATCH 6/7] Add more Lexer utils for managing versions --- ci/rector/config.php | 4 ++++ .../Doctrine/ORM/Query/AST/Functions/Cast.php | 11 +++-------- src/MartinGeorgiev/Utils/DoctrineLexer.php | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/ci/rector/config.php b/ci/rector/config.php index f29b9a46..bec08cfc 100644 --- a/ci/rector/config.php +++ b/ci/rector/config.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use Rector\CodeQuality\Rector\Identical\FlipTypeControlToUseExclusiveTypeRector; use Rector\Config\RectorConfig; use Rector\Doctrine\Set\DoctrineSetList; use Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector; @@ -40,6 +41,9 @@ $rectorConfig->skip([ RenamePropertyToMatchTypeRector::class, + FlipTypeControlToUseExclusiveTypeRector::class => [ + $basePath.'src/MartinGeorgiev/Utils/DoctrineLexer.php', + ], ]); $rectorConfig->importShortClasses(false); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Cast.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Cast.php index 2b2f7d62..b4dfe409 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Cast.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Cast.php @@ -4,13 +4,13 @@ namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions; -use Doctrine\Common\Lexer\Token; use Doctrine\ORM\Query\AST\Functions\FunctionNode; use Doctrine\ORM\Query\AST\Node; use Doctrine\ORM\Query\Lexer; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\TokenType; +use MartinGeorgiev\Utils\DoctrineLexer; use MartinGeorgiev\Utils\DoctrineOrm; /** @@ -40,16 +40,11 @@ public function parse(Parser $parser): void $parser->match($shouldUseLexer ? Lexer::T_IDENTIFIER : TokenType::T_IDENTIFIER); $lexer = $parser->getLexer(); - $token = $lexer->token; - if (!$token instanceof Token) { + $type = DoctrineLexer::getTokenValue($lexer); + if ($type === null) { return; } - if (!\is_string($token->value)) { - return; - } - - $type = $token->value; if ($lexer->isNextToken($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS)) { $parser->match($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS); $parameter = $parser->Literal(); diff --git a/src/MartinGeorgiev/Utils/DoctrineLexer.php b/src/MartinGeorgiev/Utils/DoctrineLexer.php index e79215f6..1abc5499 100644 --- a/src/MartinGeorgiev/Utils/DoctrineLexer.php +++ b/src/MartinGeorgiev/Utils/DoctrineLexer.php @@ -36,4 +36,23 @@ public static function getLookaheadType(Lexer $lexer) // @phpstan-ignore-next-line return $lexer->lookahead?->type; } + + /** + * @return mixed|null + */ + public static function getTokenValue(Lexer $lexer) + { + if (self::isPre200($lexer)) { + // @phpstan-ignore-next-line + if ($lexer->token === null) { + return null; + } + + // @phpstan-ignore-next-line + return $lexer->token['value']; + } + + // @phpstan-ignore-next-line + return $lexer->token?->value; + } } From 6940e9622c3ed6349cbe2f104766b56a592d9def Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Mon, 24 Mar 2025 21:48:33 +0000 Subject: [PATCH 7/7] static analysis --- src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Cast.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Cast.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Cast.php index b4dfe409..c1fab78a 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Cast.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Cast.php @@ -41,7 +41,7 @@ public function parse(Parser $parser): void $lexer = $parser->getLexer(); $type = DoctrineLexer::getTokenValue($lexer); - if ($type === null) { + if (!\is_string($type)) { return; }