From 7dc3d1fba31caaee646b148d209473d4dd31bb9b Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Sat, 4 Feb 2023 21:16:36 +0100
Subject: [PATCH] [TASK] Update various low-level PHP dependencies
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Various dependencies which have released
major versions (to be compatible to further
upstream packages) are now raised:

* doctrine/annotations (allowing both 1.13 + 2)
* doctrine/event-manager (For DBAL v4 compatibility)
* doctrine/lexer (v2 + v3 instead of v1)
* egulias/email-validator (4.0) using lexer

Core `TYPO3\CMS\Core\Database\Schema\Parser\Parser`
has got proper native types and return types to
match deprecated array access on tokens returned
by raised `doctrine/lexer` - which is used internal.
That solved some phpstan ignore patterns as side-change.

Used commands:
* composer req "doctrine/annotations:^1.13.3 || ^2.0" "doctrine/event-manager:^2.0" "doctrine/lexer:^2.0 || ^3.0" "egulias/email-validator:^4.0" -W
* composer req "doctrine/annotations:^1.13.3 || ^2.0" "doctrine/event-manager:^2.0" "doctrine/lexer:^2.0 || ^3.0" "egulias/email-validator:^4.0" -W -d typo3/sysext/core --no-update
* Build/Scripts/runTests.sh -s phpstanGenerateBaseline

Resolves: #99832
Releases: main
Change-Id: I8acc42933014f5d6711f2a6442499d7070a68a0b
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/77704
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: core-ci <typo3@b13.com>
Tested-by: Jochen <rothjochen@gmail.com>
Reviewed-by: Jochen <rothjochen@gmail.com>
Tested-by: Stefan Bürk <stefan@buerk.tech>
---
 Build/phpstan/phpstan-baseline.neon           |  10 --
 composer.json                                 |   8 +-
 composer.lock                                 |  69 +++++-----
 .../Classes/Database/Schema/Parser/Parser.php | 122 ++++++++----------
 typo3/sysext/core/composer.json               |   8 +-
 5 files changed, 99 insertions(+), 118 deletions(-)

diff --git a/Build/phpstan/phpstan-baseline.neon b/Build/phpstan/phpstan-baseline.neon
index 5e5e1b5f6c5f..ed446dc01a7c 100644
--- a/Build/phpstan/phpstan-baseline.neon
+++ b/Build/phpstan/phpstan-baseline.neon
@@ -545,16 +545,6 @@ parameters:
 			count: 1
 			path: ../../typo3/sysext/core/Classes/Database/Schema/DefaultTcaSchema.php
 
-		-
-			message: "#^Unreachable statement \\- code above always terminates\\.$#"
-			count: 3
-			path: ../../typo3/sysext/core/Classes/Database/Schema/Parser/Parser.php
-
-		-
-			message: "#^While loop condition is always true\\.$#"
-			count: 3
-			path: ../../typo3/sysext/core/Classes/Database/Schema/Parser/Parser.php
-
 		-
 			message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Core\\\\Package\\\\PackageInterface\\:\\:getServiceProvider\\(\\)\\.$#"
 			count: 1
diff --git a/composer.json b/composer.json
index cef1f6c82acc..b574959656ce 100644
--- a/composer.json
+++ b/composer.json
@@ -49,11 +49,11 @@
 		"composer-runtime-api": "^2.1",
 		"bacon/bacon-qr-code": "^2.0.7",
 		"christian-riesen/base32": "^1.6",
-		"doctrine/annotations": "^1.13.3",
+		"doctrine/annotations": "^1.13.3 || ^2.0",
 		"doctrine/dbal": "^3.5.1",
-		"doctrine/event-manager": "^1.1.2",
-		"doctrine/lexer": "^1.2.3",
-		"egulias/email-validator": "^3.2.1",
+		"doctrine/event-manager": "^2.0",
+		"doctrine/lexer": "^2.0 || ^3.0",
+		"egulias/email-validator": "^4.0",
 		"enshrined/svg-sanitize": "^0.15.4",
 		"firebase/php-jwt": "^6.3.1",
 		"guzzlehttp/guzzle": "^7.5.0",
diff --git a/composer.lock b/composer.lock
index d123a23d2af3..f2492b51e7e7 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "6c7c18fa5a7cd4785cb536486338f2fc",
+    "content-hash": "95c552d41acec33b145ec7bea700127a",
     "packages": [
         {
             "name": "bacon/bacon-qr-code",
@@ -491,30 +491,29 @@
         },
         {
             "name": "doctrine/event-manager",
-            "version": "1.2.0",
+            "version": "2.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/event-manager.git",
-                "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520"
+                "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520",
-                "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520",
+                "url": "https://api.github.com/repos/doctrine/event-manager/zipball/750671534e0241a7c50ea5b43f67e23eb5c96f32",
+                "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32",
                 "shasum": ""
             },
             "require": {
-                "doctrine/deprecations": "^0.5.3 || ^1",
-                "php": "^7.1 || ^8.0"
+                "php": "^8.1"
             },
             "conflict": {
                 "doctrine/common": "<2.9"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^9 || ^10",
-                "phpstan/phpstan": "~1.4.10 || ^1.8.8",
-                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
-                "vimeo/psalm": "^4.24"
+                "doctrine/coding-standard": "^10",
+                "phpstan/phpstan": "^1.8.8",
+                "phpunit/phpunit": "^9.5",
+                "vimeo/psalm": "^4.28"
             },
             "type": "library",
             "autoload": {
@@ -563,7 +562,7 @@
             ],
             "support": {
                 "issues": "https://github.com/doctrine/event-manager/issues",
-                "source": "https://github.com/doctrine/event-manager/tree/1.2.0"
+                "source": "https://github.com/doctrine/event-manager/tree/2.0.0"
             },
             "funding": [
                 {
@@ -579,35 +578,37 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-10-12T20:51:15+00:00"
+            "time": "2022-10-12T20:59:15+00:00"
         },
         {
             "name": "doctrine/lexer",
-            "version": "1.2.3",
+            "version": "2.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/lexer.git",
-                "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229"
+                "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229",
-                "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229",
+                "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124",
+                "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124",
                 "shasum": ""
             },
             "require": {
+                "doctrine/deprecations": "^1.0",
                 "php": "^7.1 || ^8.0"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^9.0",
+                "doctrine/coding-standard": "^9 || ^10",
                 "phpstan/phpstan": "^1.3",
                 "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
-                "vimeo/psalm": "^4.11"
+                "psalm/plugin-phpunit": "^0.18.3",
+                "vimeo/psalm": "^4.11 || ^5.0"
             },
             "type": "library",
             "autoload": {
                 "psr-4": {
-                    "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
+                    "Doctrine\\Common\\Lexer\\": "src"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -639,7 +640,7 @@
             ],
             "support": {
                 "issues": "https://github.com/doctrine/lexer/issues",
-                "source": "https://github.com/doctrine/lexer/tree/1.2.3"
+                "source": "https://github.com/doctrine/lexer/tree/2.1.0"
             },
             "funding": [
                 {
@@ -655,30 +656,30 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-02-28T11:07:21+00:00"
+            "time": "2022-12-14T08:49:07+00:00"
         },
         {
             "name": "egulias/email-validator",
-            "version": "3.2.5",
+            "version": "4.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/egulias/EmailValidator.git",
-                "reference": "b531a2311709443320c786feb4519cfaf94af796"
+                "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b531a2311709443320c786feb4519cfaf94af796",
-                "reference": "b531a2311709443320c786feb4519cfaf94af796",
+                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff",
+                "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff",
                 "shasum": ""
             },
             "require": {
-                "doctrine/lexer": "^1.2|^2",
-                "php": ">=7.2",
-                "symfony/polyfill-intl-idn": "^1.15"
+                "doctrine/lexer": "^2.0 || ^3.0",
+                "php": ">=8.1",
+                "symfony/polyfill-intl-idn": "^1.26"
             },
             "require-dev": {
-                "phpunit/phpunit": "^8.5.8|^9.3.3",
-                "vimeo/psalm": "^4"
+                "phpunit/phpunit": "^9.5.27",
+                "vimeo/psalm": "^4.30"
             },
             "suggest": {
                 "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
@@ -686,7 +687,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.0.x-dev"
+                    "dev-master": "4.0.x-dev"
                 }
             },
             "autoload": {
@@ -714,7 +715,7 @@
             ],
             "support": {
                 "issues": "https://github.com/egulias/EmailValidator/issues",
-                "source": "https://github.com/egulias/EmailValidator/tree/3.2.5"
+                "source": "https://github.com/egulias/EmailValidator/tree/4.0.1"
             },
             "funding": [
                 {
@@ -722,7 +723,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2023-01-02T17:26:14+00:00"
+            "time": "2023-01-14T14:17:03+00:00"
         },
         {
             "name": "enshrined/svg-sanitize",
diff --git a/typo3/sysext/core/Classes/Database/Schema/Parser/Parser.php b/typo3/sysext/core/Classes/Database/Schema/Parser/Parser.php
index bb141e9e213d..2ea2174b3008 100644
--- a/typo3/sysext/core/Classes/Database/Schema/Parser/Parser.php
+++ b/typo3/sysext/core/Classes/Database/Schema/Parser/Parser.php
@@ -17,6 +17,7 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Core\Database\Schema\Parser;
 
+use Doctrine\Common\Lexer\Token;
 use Doctrine\DBAL\Schema\Table;
 use TYPO3\CMS\Core\Database\Schema\Exception\StatementException;
 use TYPO3\CMS\Core\Database\Schema\Parser\AST\AbstractCreateDefinitionItem;
@@ -66,22 +67,12 @@ use TYPO3\CMS\Core\Database\Schema\Parser\AST\ReferenceDefinition;
 /**
  * An LL(*) recursive-descent parser for MySQL CREATE TABLE statements.
  * Parses a CREATE TABLE statement, reports any errors in it, and generates an AST.
+ * @todo mark as internal/final
  */
 class Parser
 {
-    /**
-     * The lexer.
-     *
-     * @var Lexer
-     */
-    protected $lexer;
-
-    /**
-     * The statement to parse.
-     *
-     * @var string
-     */
-    protected $statement;
+    protected Lexer $lexer;
+    protected string $statement = '';
 
     /**
      * Creates a new statement parser object.
@@ -96,6 +87,7 @@ class Parser
 
     /**
      * Gets the lexer used by the parser.
+     * @todo unused. drop after recheck.
      */
     public function getLexer(): Lexer
     {
@@ -121,12 +113,11 @@ class Parser
      *
      * @param int $token The token type.
      *
-     *
      * @throws StatementException If the tokens don't match.
      */
-    public function match($token)
+    public function match(int $token)
     {
-        $lookaheadType = $this->lexer->lookahead['type'];
+        $lookaheadType = $this->lexer->lookahead->type;
 
         // Short-circuit on first condition, usually types match
         if ($lookaheadType !== $token) {
@@ -163,7 +154,7 @@ class Parser
      * @param bool $deep Whether to clean peek and reset errors.
      * @param int $position Position to reset.
      */
-    public function free($deep = false, $position = 0)
+    public function free(bool $deep = false, int $position = 0): void
     {
         // WARNING! Use this method with care. It resets the scanner!
         $this->lexer->resetPosition($position);
@@ -204,22 +195,21 @@ class Parser
      * Generates a new syntax error.
      *
      * @param string $expected Expected string.
-     * @param array|null $token Got token.
+     * @param Token|null $token Got token.
      *
      *
      * @throws \TYPO3\CMS\Core\Database\Schema\Exception\StatementException
      */
-    public function syntaxError($expected = '', $token = null)
+    public function syntaxError(string $expected = '', ?Token $token = null): void
     {
         if ($token === null) {
             $token = $this->lexer->lookahead;
         }
-
-        $tokenPos = $token['position'] ?? '-1';
+        $tokenPos = $token->position;
 
         $message = "line 0, col {$tokenPos}: Error: ";
         $message .= ($expected !== '') ? "Expected {$expected}, got " : 'Unexpected ';
-        $message .= ($this->lexer->lookahead === null) ? 'end of string.' : "'{$token['value']}'";
+        $message .= ($this->lexer->lookahead === null) ? 'end of string.' : "'{$token->value}'";
 
         throw StatementException::syntaxError($message, StatementException::sqlError($this->statement));
     }
@@ -228,16 +218,17 @@ class Parser
      * Generates a new semantical error.
      *
      * @param string $message Optional message.
-     * @param array|null $token Optional token.
+     * @param Token|null $token Optional token.
      *
      *
      * @throws \TYPO3\CMS\Core\Database\Schema\Exception\StatementException
      */
-    public function semanticalError($message = '', $token = null)
+    public function semanticalError(string $message = '', ?Token $token = null): void
     {
         if ($token === null) {
             $token = $this->lexer->lookahead ?? [];
         }
+        $tokenPos = $token->position;
 
         // Minimum exposed chars ahead of token
         $distance = 12;
@@ -245,12 +236,11 @@ class Parser
         // Find a position of a final word to display in error string
         $createTableStatement = $this->statement;
         $length = strlen($createTableStatement);
-        $pos = $token['position'] + $distance;
+        $pos = $tokenPos + $distance;
         $pos = strpos($createTableStatement, ' ', ($length > $pos) ? $pos : $length);
-        $length = ($pos !== false) ? $pos - $token['position'] : $distance;
+        $length = ($pos !== false) ? $pos - $tokenPos : $distance;
 
-        $tokenPos = array_key_exists('position', $token) && $token['position'] > 0 ? $token['position'] : '-1';
-        $tokenStr = substr($createTableStatement, $token['position'], $length);
+        $tokenStr = substr($createTableStatement, $tokenPos, $length);
 
         // Building informative message
         $message = 'line 0, col ' . $tokenPos . " near '" . $tokenStr . "': Error: " . $message;
@@ -263,15 +253,15 @@ class Parser
      *
      * @param bool $resetPeek Reset peek after finding the closing parenthesis.
      *
-     * @return array
+     * @return Token
      */
-    protected function peekBeyondClosingParenthesis($resetPeek = true)
+    protected function peekBeyondClosingParenthesis(bool $resetPeek = true): Token
     {
         $token = $this->lexer->peek();
         $numUnmatched = 1;
 
         while ($numUnmatched > 0 && $token !== null) {
-            switch ($token['type']) {
+            switch ($token->type) {
                 case Lexer::T_OPEN_PARENTHESIS:
                     ++$numUnmatched;
                     break;
@@ -301,7 +291,7 @@ class Parser
     {
         $this->lexer->moveNext();
 
-        if ($this->lexer->lookahead['type'] !== Lexer::T_CREATE) {
+        if (($this->lexer->lookahead?->type ?? null) !== Lexer::T_CREATE) {
             $this->syntaxError('CREATE');
         }
 
@@ -326,7 +316,7 @@ class Parser
         $statement = null;
         $this->match(Lexer::T_CREATE);
 
-        switch ($this->lexer->lookahead['type']) {
+        switch ($this->lexer->lookahead->type) {
             case Lexer::T_TEMPORARY:
                 // Intentional fall-through
             case Lexer::T_TABLE:
@@ -439,7 +429,7 @@ class Parser
     {
         $definitionItem = null;
 
-        switch ($this->lexer->lookahead['type']) {
+        switch ($this->lexer->lookahead->type) {
             case Lexer::T_FULLTEXT:
                 // Intentional fall-through
             case Lexer::T_SPATIAL:
@@ -483,7 +473,7 @@ class Parser
         $isUnique = false;
         $indexDefinition = new CreateIndexDefinitionItem();
 
-        switch ($this->lexer->lookahead['type']) {
+        switch ($this->lexer->lookahead->type) {
             case Lexer::T_PRIMARY:
                 $this->match(Lexer::T_PRIMARY);
                 // KEY is a required keyword for PRIMARY index
@@ -623,7 +613,7 @@ class Parser
 
         $this->match(Lexer::T_USING);
 
-        switch ($this->lexer->lookahead['type']) {
+        switch ($this->lexer->lookahead->type) {
             case Lexer::T_BTREE:
                 $this->match(Lexer::T_BTREE);
                 $indexType = 'BTREE';
@@ -652,14 +642,14 @@ class Parser
         $options = [];
 
         while ($this->lexer->lookahead && !$this->lexer->isNextTokenAny([Lexer::T_COMMA, Lexer::T_CLOSE_PARENTHESIS])) {
-            switch ($this->lexer->lookahead['type']) {
+            switch ($this->lexer->lookahead->type) {
                 case Lexer::T_KEY_BLOCK_SIZE:
                     $this->match(Lexer::T_KEY_BLOCK_SIZE);
                     if ($this->lexer->isNextToken(Lexer::T_EQUALS)) {
                         $this->match(Lexer::T_EQUALS);
                     }
                     $this->lexer->moveNext();
-                    $options['key_block_size'] = (int)$this->lexer->token['value'];
+                    $options['key_block_size'] = (int)$this->lexer->token->value;
                     break;
                 case Lexer::T_USING:
                     $options['index_type'] = $this->indexType();
@@ -672,7 +662,7 @@ class Parser
                 case Lexer::T_COMMENT:
                     $this->match(Lexer::T_COMMENT);
                     $this->match(Lexer::T_STRING);
-                    $options['comment'] = $this->lexer->token['value'];
+                    $options['comment'] = $this->lexer->token->value;
                     break;
                 default:
                     $this->syntaxError('KEY_BLOCK_SIZE, USING, WITH PARSER or COMMENT');
@@ -703,7 +693,7 @@ class Parser
         $columnDefinitionItem = new CreateColumnDefinitionItem($columnName, $dataType);
 
         while ($this->lexer->lookahead && !$this->lexer->isNextTokenAny([Lexer::T_COMMA, Lexer::T_CLOSE_PARENTHESIS])) {
-            switch ($this->lexer->lookahead['type']) {
+            switch ($this->lexer->lookahead->type) {
                 case Lexer::T_NOT:
                     $columnDefinitionItem->allowNull = false;
                     $this->match(Lexer::T_NOT);
@@ -742,7 +732,7 @@ class Parser
                 case Lexer::T_COMMENT:
                     $this->match(Lexer::T_COMMENT);
                     if ($this->lexer->isNextToken(Lexer::T_STRING)) {
-                        $columnDefinitionItem->comment = $this->lexer->lookahead['value'];
+                        $columnDefinitionItem->comment = $this->lexer->lookahead->value;
                         $this->match(Lexer::T_STRING);
                     }
                     break;
@@ -824,7 +814,7 @@ class Parser
     {
         $dataType = null;
 
-        switch ($this->lexer->lookahead['type']) {
+        switch ($this->lexer->lookahead->type) {
             case Lexer::T_BIT:
                 $this->match(Lexer::T_BIT);
                 $dataType = new BitDataType(
@@ -1015,20 +1005,20 @@ class Parser
      * @return mixed
      * @throws \TYPO3\CMS\Core\Database\Schema\Exception\StatementException
      */
-    protected function columnDefaultValue()
+    protected function columnDefaultValue(): mixed
     {
         $this->match(Lexer::T_DEFAULT);
         $value = null;
 
-        switch ($this->lexer->lookahead['type']) {
+        switch ($this->lexer->lookahead->type) {
             case Lexer::T_INTEGER:
-                $value = (int)$this->lexer->lookahead['value'];
+                $value = (int)$this->lexer->lookahead->value;
                 break;
             case Lexer::T_FLOAT:
-                $value = (float)$this->lexer->lookahead['value'];
+                $value = (float)$this->lexer->lookahead->value;
                 break;
             case Lexer::T_STRING:
-                $value = (string)$this->lexer->lookahead['value'];
+                $value = (string)$this->lexer->lookahead->value;
                 break;
             case Lexer::T_CURRENT_TIMESTAMP:
                 $value = 'CURRENT_TIMESTAMP';
@@ -1061,7 +1051,7 @@ class Parser
         }
 
         $this->match(Lexer::T_OPEN_PARENTHESIS);
-        $length = (int)$this->lexer->lookahead['value'];
+        $length = (int)$this->lexer->lookahead->value;
         $this->match(Lexer::T_INTEGER);
         $this->match(Lexer::T_CLOSE_PARENTHESIS);
 
@@ -1081,12 +1071,12 @@ class Parser
         }
 
         $this->match(Lexer::T_OPEN_PARENTHESIS);
-        $options['length'] = (int)$this->lexer->lookahead['value'];
+        $options['length'] = (int)$this->lexer->lookahead->value;
         $this->match(Lexer::T_INTEGER);
 
         if ($this->lexer->isNextToken(Lexer::T_COMMA)) {
             $this->match(Lexer::T_COMMA);
-            $options['decimals'] = (int)$this->lexer->lookahead['value'];
+            $options['decimals'] = (int)$this->lexer->lookahead->value;
             $this->match(Lexer::T_INTEGER);
         }
 
@@ -1109,7 +1099,7 @@ class Parser
         }
 
         while ($this->lexer->isNextTokenAny([Lexer::T_UNSIGNED, Lexer::T_ZEROFILL])) {
-            switch ($this->lexer->lookahead['type']) {
+            switch ($this->lexer->lookahead->type) {
                 case Lexer::T_UNSIGNED:
                     $this->match(Lexer::T_UNSIGNED);
                     $options['unsigned'] = true;
@@ -1158,7 +1148,7 @@ class Parser
         }
 
         while ($this->lexer->isNextTokenAny([Lexer::T_CHARACTER, Lexer::T_COLLATE, Lexer::T_BINARY])) {
-            switch ($this->lexer->lookahead['type']) {
+            switch ($this->lexer->lookahead->type) {
                 case Lexer::T_BINARY:
                     $this->match(Lexer::T_BINARY);
                     $options['binary'] = true;
@@ -1167,12 +1157,12 @@ class Parser
                     $this->match(Lexer::T_CHARACTER);
                     $this->match(Lexer::T_SET);
                     $this->match(Lexer::T_STRING);
-                    $options['charset'] = $this->lexer->token['value'];
+                    $options['charset'] = $this->lexer->token->value;
                     break;
                 case Lexer::T_COLLATE:
                     $this->match(Lexer::T_COLLATE);
                     $this->match(Lexer::T_STRING);
-                    $options['collation'] = $this->lexer->token['value'];
+                    $options['collation'] = $this->lexer->token->value;
                     break;
                 default:
                     $this->syntaxError('BINARY, CHARACTER SET or COLLATE');
@@ -1196,17 +1186,17 @@ class Parser
         }
 
         while ($this->lexer->isNextTokenAny([Lexer::T_CHARACTER, Lexer::T_COLLATE])) {
-            switch ($this->lexer->lookahead['type']) {
+            switch ($this->lexer->lookahead->type) {
                 case Lexer::T_CHARACTER:
                     $this->match(Lexer::T_CHARACTER);
                     $this->match(Lexer::T_SET);
                     $this->match(Lexer::T_STRING);
-                    $options['charset'] = $this->lexer->token['value'];
+                    $options['charset'] = $this->lexer->token->value;
                     break;
                 case Lexer::T_COLLATE:
                     $this->match(Lexer::T_COLLATE);
                     $this->match(Lexer::T_STRING);
-                    $options['collation'] = $this->lexer->token['value'];
+                    $options['collation'] = $this->lexer->token->value;
                     break;
                 default:
                     $this->syntaxError('CHARACTER SET or COLLATE');
@@ -1247,7 +1237,7 @@ class Parser
     {
         $this->match(Lexer::T_STRING);
 
-        return (string)$this->lexer->token['value'];
+        return (string)$this->lexer->token->value;
     }
 
     /**
@@ -1277,10 +1267,10 @@ class Parser
         $referenceDefinition = new ReferenceDefinition($tableName, $referenceColumns);
 
         while (!$this->lexer->isNextTokenAny([Lexer::T_COMMA, Lexer::T_CLOSE_PARENTHESIS])) {
-            switch ($this->lexer->lookahead['type']) {
+            switch ($this->lexer->lookahead->type) {
                 case Lexer::T_MATCH:
                     $this->match(Lexer::T_MATCH);
-                    $referenceDefinition->match = $this->lexer->lookahead['value'];
+                    $referenceDefinition->match = $this->lexer->lookahead->value;
                     $this->lexer->moveNext();
                     break;
                 case Lexer::T_ON:
@@ -1332,7 +1322,7 @@ class Parser
     {
         $action = null;
 
-        switch ($this->lexer->lookahead['type']) {
+        switch ($this->lexer->lookahead->type) {
             case Lexer::T_RESTRICT:
                 $this->match(Lexer::T_RESTRICT);
                 $action = 'RESTRICT';
@@ -1394,7 +1384,7 @@ class Parser
         $options = [];
 
         while ($this->lexer->lookahead && !$this->lexer->isNextToken(Lexer::T_SEMICOLON)) {
-            switch ($this->lexer->lookahead['type']) {
+            switch ($this->lexer->lookahead->type) {
                 case Lexer::T_DEFAULT:
                     // DEFAULT prefix is optional for COLLATE/CHARACTER SET, do nothing
                     $this->match(Lexer::T_DEFAULT);
@@ -1546,7 +1536,7 @@ class Parser
      * @return mixed
      * @throws \TYPO3\CMS\Core\Database\Schema\Exception\StatementException
      */
-    protected function tableOptionValue()
+    protected function tableOptionValue(): mixed
     {
         // Skip the optional equals sign
         if ($this->lexer->isNextToken(Lexer::T_EQUALS)) {
@@ -1554,7 +1544,7 @@ class Parser
         }
         $this->lexer->moveNext();
 
-        return $this->lexer->token['value'];
+        return $this->lexer->token->value;
     }
 
     /**
@@ -1564,9 +1554,9 @@ class Parser
      * @return \TYPO3\CMS\Core\Database\Schema\Parser\AST\Identifier
      * @throws \TYPO3\CMS\Core\Database\Schema\Exception\StatementException
      */
-    protected function schemaObjectName()
+    protected function schemaObjectName(): Identifier
     {
-        $schemaObjectName = $this->lexer->lookahead['value'];
+        $schemaObjectName = $this->lexer->lookahead->value;
         $this->lexer->moveNext();
 
         return new Identifier((string)$schemaObjectName);
diff --git a/typo3/sysext/core/composer.json b/typo3/sysext/core/composer.json
index a384bd7a8747..00dad5132778 100644
--- a/typo3/sysext/core/composer.json
+++ b/typo3/sysext/core/composer.json
@@ -32,11 +32,11 @@
 		"composer-runtime-api": "^2.1",
 		"bacon/bacon-qr-code": "^2.0.7",
 		"christian-riesen/base32": "^1.6",
-		"doctrine/annotations": "^1.13.3",
+		"doctrine/annotations": "^1.13.3 || ^2.0",
 		"doctrine/dbal": "^3.5.1",
-		"doctrine/event-manager": "^1.1.2",
-		"doctrine/lexer": "^1.2.3",
-		"egulias/email-validator": "^3.2.1",
+		"doctrine/event-manager": "^2.0",
+		"doctrine/lexer": "^2.0 || ^3.0",
+		"egulias/email-validator": "^4.0",
 		"enshrined/svg-sanitize": "^0.15.4",
 		"firebase/php-jwt": "^6.3.1",
 		"guzzlehttp/guzzle": "^7.5.0",
-- 
GitLab