diff --git a/composer.json b/composer.json
index 8a17fafda2..380f2f8e10 100644
--- a/composer.json
+++ b/composer.json
@@ -21,10 +21,10 @@
         "composer/composer": "^1.9.1",
         "drupal/coder": "^8.3.7",
         "mikey179/vfsstream": "^1.6.8",
-        "phpunit/phpunit": "^8.4.1",
+        "phpunit/phpunit": "^8.4.1 || ^9",
         "phpspec/prophecy": "^1.7",
         "symfony/css-selector": "^4.4",
-        "symfony/phpunit-bridge": "^4.4",
+        "symfony/phpunit-bridge": "^4.4.8",
         "symfony/error-handler": "^4.4",
         "justinrainbow/json-schema": "^5.2",
         "symfony/filesystem": "^4.4",
@@ -86,7 +86,7 @@
         "pre-update-cmd": "Drupal\\Composer\\Composer::ensureComposerVersion",
         "pre-autoload-dump": "Drupal\\Core\\Composer\\Composer::preAutoloadDump",
         "drupal-phpunit-upgrade-check": "Drupal\\Core\\Composer\\Composer::upgradePHPUnit",
-        "drupal-phpunit-upgrade": "@composer update phpunit/phpunit symfony/phpunit-bridge phpspec/prophecy symfony/yaml --with-dependencies --no-progress",
+        "drupal-phpunit-upgrade": "@composer update phpunit/phpunit symfony/phpunit-bridge phpspec/prophecy symfony/yaml --with-dependencies --no-progress --ignore-platform-reqs",
         "post-update-cmd": [
             "Drupal\\Composer\\Composer::generateMetapackages"
         ],
diff --git a/composer.lock b/composer.lock
index 37c95a8604..084166fb5c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -485,7 +485,7 @@
             "dist": {
                 "type": "path",
                 "url": "core",
-                "reference": "5bd6798a64831fa08a343a14a0ee47127c4cb99f"
+                "reference": "8b05c278f5cc7a2a150e8f3999e185110d56d334"
             },
             "require": {
                 "asm89/stack-cors": "^1.1",
@@ -708,6 +708,9 @@
                     "lib/Drupal/Core/DrupalKernelInterface.php",
                     "lib/Drupal/Core/Installer/InstallerRedirectTrait.php",
                     "lib/Drupal/Core/Site/Settings.php"
+                ],
+                "files": [
+                    "includes/class_aliases.php"
                 ]
             },
             "scripts": {
@@ -1984,7 +1987,7 @@
         },
         {
             "name": "symfony/console",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
@@ -2075,7 +2078,7 @@
         },
         {
             "name": "symfony/debug",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/debug.git",
@@ -2146,16 +2149,16 @@
         },
         {
             "name": "symfony/dependency-injection",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dependency-injection.git",
-                "reference": "6a2cecd7011aec38b5fb2270abf0de120e7679b1"
+                "reference": "a37cc0a90fec178768aa5048fea9251efde591c5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6a2cecd7011aec38b5fb2270abf0de120e7679b1",
-                "reference": "6a2cecd7011aec38b5fb2270abf0de120e7679b1",
+                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a37cc0a90fec178768aa5048fea9251efde591c5",
+                "reference": "a37cc0a90fec178768aa5048fea9251efde591c5",
                 "shasum": ""
             },
             "require": {
@@ -2229,11 +2232,11 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-30T20:06:45+00:00"
+            "time": "2020-06-12T07:37:04+00:00"
         },
         {
             "name": "symfony/error-handler",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/error-handler.git",
@@ -2304,7 +2307,7 @@
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
@@ -2446,7 +2449,7 @@
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
@@ -2515,16 +2518,16 @@
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "54526b598d7fc86a67850488b194a88a79ab8467"
+                "reference": "81d42148474e1852a333ed7a732f2a014af75430"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/54526b598d7fc86a67850488b194a88a79ab8467",
-                "reference": "54526b598d7fc86a67850488b194a88a79ab8467",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/81d42148474e1852a333ed7a732f2a014af75430",
+                "reference": "81d42148474e1852a333ed7a732f2a014af75430",
                 "shasum": ""
             },
             "require": {
@@ -2616,20 +2619,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-31T05:25:51+00:00"
+            "time": "2020-06-12T11:15:37+00:00"
         },
         {
             "name": "symfony/mime",
-            "version": "v5.1.0",
+            "version": "v5.1.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/mime.git",
-                "reference": "56261f89385f9d13cf843a5101ac72131190bc91"
+                "reference": "c0c418f05e727606e85b482a8591519c4712cf45"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/mime/zipball/56261f89385f9d13cf843a5101ac72131190bc91",
-                "reference": "56261f89385f9d13cf843a5101ac72131190bc91",
+                "url": "https://api.github.com/repos/symfony/mime/zipball/c0c418f05e727606e85b482a8591519c4712cf45",
+                "reference": "c0c418f05e727606e85b482a8591519c4712cf45",
                 "shasum": ""
             },
             "require": {
@@ -2693,20 +2696,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-25T12:33:44+00:00"
+            "time": "2020-06-09T15:07:35+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
-            "version": "v1.17.0",
+            "version": "v1.17.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-ctype.git",
-                "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9"
+                "reference": "2edd75b8b35d62fd3eeabba73b26b8f1f60ce13d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
-                "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/2edd75b8b35d62fd3eeabba73b26b8f1f60ce13d",
+                "reference": "2edd75b8b35d62fd3eeabba73b26b8f1f60ce13d",
                 "shasum": ""
             },
             "require": {
@@ -2719,6 +2722,10 @@
             "extra": {
                 "branch-alias": {
                     "dev-master": "1.17-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
@@ -2765,20 +2772,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-12T16:14:59+00:00"
+            "time": "2020-06-06T08:46:27+00:00"
         },
         {
             "name": "symfony/polyfill-iconv",
-            "version": "v1.17.0",
+            "version": "v1.17.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-iconv.git",
-                "reference": "c4de7601eefbf25f9d47190abe07f79fe0a27424"
+                "reference": "ba6c9c18db36235b859cc29b8372d1c01298c035"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/c4de7601eefbf25f9d47190abe07f79fe0a27424",
-                "reference": "c4de7601eefbf25f9d47190abe07f79fe0a27424",
+                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/ba6c9c18db36235b859cc29b8372d1c01298c035",
+                "reference": "ba6c9c18db36235b859cc29b8372d1c01298c035",
                 "shasum": ""
             },
             "require": {
@@ -2791,6 +2798,10 @@
             "extra": {
                 "branch-alias": {
                     "dev-master": "1.17-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
@@ -2838,7 +2849,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-12T16:47:27+00:00"
+            "time": "2020-06-06T08:46:27+00:00"
         },
         {
             "name": "symfony/polyfill-intl-idn",
@@ -2918,16 +2929,16 @@
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.17.0",
+            "version": "v1.17.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "fa79b11539418b02fc5e1897267673ba2c19419c"
+                "reference": "7110338d81ce1cbc3e273136e4574663627037a7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c",
-                "reference": "fa79b11539418b02fc5e1897267673ba2c19419c",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7110338d81ce1cbc3e273136e4574663627037a7",
+                "reference": "7110338d81ce1cbc3e273136e4574663627037a7",
                 "shasum": ""
             },
             "require": {
@@ -2940,6 +2951,10 @@
             "extra": {
                 "branch-alias": {
                     "dev-master": "1.17-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
@@ -2987,7 +3002,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-12T16:47:27+00:00"
+            "time": "2020-06-06T08:46:27+00:00"
         },
         {
             "name": "symfony/polyfill-php72",
@@ -3060,16 +3075,16 @@
         },
         {
             "name": "symfony/polyfill-php73",
-            "version": "v1.17.0",
+            "version": "v1.17.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php73.git",
-                "reference": "a760d8964ff79ab9bf057613a5808284ec852ccc"
+                "reference": "fa0837fe02d617d31fbb25f990655861bb27bd1a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a760d8964ff79ab9bf057613a5808284ec852ccc",
-                "reference": "a760d8964ff79ab9bf057613a5808284ec852ccc",
+                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fa0837fe02d617d31fbb25f990655861bb27bd1a",
+                "reference": "fa0837fe02d617d31fbb25f990655861bb27bd1a",
                 "shasum": ""
             },
             "require": {
@@ -3079,6 +3094,10 @@
             "extra": {
                 "branch-alias": {
                     "dev-master": "1.17-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
@@ -3128,20 +3147,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-12T16:47:27+00:00"
+            "time": "2020-06-06T08:46:27+00:00"
         },
         {
             "name": "symfony/polyfill-php80",
-            "version": "v1.17.0",
+            "version": "v1.17.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php80.git",
-                "reference": "5e30b2799bc1ad68f7feb62b60a73743589438dd"
+                "reference": "4a5b6bba3259902e386eb80dd1956181ee90b5b2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/5e30b2799bc1ad68f7feb62b60a73743589438dd",
-                "reference": "5e30b2799bc1ad68f7feb62b60a73743589438dd",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4a5b6bba3259902e386eb80dd1956181ee90b5b2",
+                "reference": "4a5b6bba3259902e386eb80dd1956181ee90b5b2",
                 "shasum": ""
             },
             "require": {
@@ -3151,6 +3170,10 @@
             "extra": {
                 "branch-alias": {
                     "dev-master": "1.17-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
@@ -3204,11 +3227,11 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-12T16:47:27+00:00"
+            "time": "2020-06-06T08:46:27+00:00"
         },
         {
             "name": "symfony/process",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
@@ -3271,20 +3294,20 @@
         },
         {
             "name": "symfony/psr-http-message-bridge",
-            "version": "v2.0.0",
+            "version": "v2.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/psr-http-message-bridge.git",
-                "reference": "ce709cd9c90872c08c2427b45739d5f3c781ab4f"
+                "reference": "e44f249afab496b4e8c0f7461fb8140eaa4b24d2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/ce709cd9c90872c08c2427b45739d5f3c781ab4f",
-                "reference": "ce709cd9c90872c08c2427b45739d5f3c781ab4f",
+                "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/e44f249afab496b4e8c0f7461fb8140eaa4b24d2",
+                "reference": "e44f249afab496b4e8c0f7461fb8140eaa4b24d2",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1",
+                "php": ">=7.1",
                 "psr/http-message": "^1.0",
                 "symfony/http-foundation": "^4.4 || ^5.0"
             },
@@ -3331,11 +3354,25 @@
                 "psr-17",
                 "psr-7"
             ],
-            "time": "2020-01-02T08:07:11+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-06-25T08:21:47+00:00"
         },
         {
             "name": "symfony/routing",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
@@ -3425,16 +3462,16 @@
         },
         {
             "name": "symfony/serializer",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/serializer.git",
-                "reference": "f2d82706d488b87e67050b03a9ae54194b129024"
+                "reference": "a91ceee34fc690a824770085192ffdeaa4476a8c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/serializer/zipball/f2d82706d488b87e67050b03a9ae54194b129024",
-                "reference": "f2d82706d488b87e67050b03a9ae54194b129024",
+                "url": "https://api.github.com/repos/symfony/serializer/zipball/a91ceee34fc690a824770085192ffdeaa4476a8c",
+                "reference": "a91ceee34fc690a824770085192ffdeaa4476a8c",
                 "shasum": ""
             },
             "require": {
@@ -3517,7 +3554,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-30T20:06:45+00:00"
+            "time": "2020-06-01T17:29:37+00:00"
         },
         {
             "name": "symfony/service-contracts",
@@ -3593,7 +3630,7 @@
         },
         {
             "name": "symfony/translation",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
@@ -3754,7 +3791,7 @@
         },
         {
             "name": "symfony/validator",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/validator.git",
@@ -3861,7 +3898,7 @@
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v5.1.0",
+            "version": "v5.1.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
@@ -3951,7 +3988,7 @@
         },
         {
             "name": "symfony/yaml",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
@@ -4024,31 +4061,31 @@
         },
         {
             "name": "twig/twig",
-            "version": "v2.12.5",
+            "version": "2.x-dev",
             "source": {
                 "type": "git",
                 "url": "https://github.com/twigphp/Twig.git",
-                "reference": "18772e0190734944277ee97a02a9a6c6555fcd94"
+                "reference": "f32950c872a995a93807909bab69387f47afaa25"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/twigphp/Twig/zipball/18772e0190734944277ee97a02a9a6c6555fcd94",
-                "reference": "18772e0190734944277ee97a02a9a6c6555fcd94",
+                "url": "https://api.github.com/repos/twigphp/Twig/zipball/f32950c872a995a93807909bab69387f47afaa25",
+                "reference": "f32950c872a995a93807909bab69387f47afaa25",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.0",
+                "php": ">=7.1.3",
                 "symfony/polyfill-ctype": "^1.8",
                 "symfony/polyfill-mbstring": "^1.3"
             },
             "require-dev": {
                 "psr/container": "^1.0",
-                "symfony/phpunit-bridge": "^4.4|^5.0"
+                "symfony/phpunit-bridge": "^4.4.9|^5.0.9"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.12-dev"
+                    "dev-master": "2.13-dev"
                 }
             },
             "autoload": {
@@ -4085,7 +4122,29 @@
             "keywords": [
                 "templating"
             ],
-            "time": "2020-02-11T15:31:23+00:00"
+            "funding": [
+                {
+                    "url": "https://certification.symfony.com/",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://live.symfony.com/",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://symfony.com/cloud/",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/twig/twig",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-06-22T15:25:21+00:00"
         },
         {
             "name": "typo3/phar-stream-wrapper",
@@ -4481,9 +4540,6 @@
                 "ext-zip": "Enabling the zip extension allows you to unzip archives",
                 "ext-zlib": "Allow gzip compression of HTTP requests"
             },
-            "bin": [
-                "bin/composer"
-            ],
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -6513,7 +6569,7 @@
         },
         {
             "name": "symfony/browser-kit",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/browser-kit.git",
@@ -6586,7 +6642,7 @@
         },
         {
             "name": "symfony/css-selector",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
@@ -6653,7 +6709,7 @@
         },
         {
             "name": "symfony/dom-crawler",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dom-crawler.git",
@@ -6728,7 +6784,7 @@
         },
         {
             "name": "symfony/filesystem",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
@@ -6792,7 +6848,7 @@
         },
         {
             "name": "symfony/finder",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
@@ -6855,16 +6911,16 @@
         },
         {
             "name": "symfony/lock",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/lock.git",
-                "reference": "55b1ae7bcb7f1b126c5d2b492d6f52342bec1951"
+                "reference": "b2c6895cf282cfc11fae859e8b418c95dbb5d6f8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/lock/zipball/55b1ae7bcb7f1b126c5d2b492d6f52342bec1951",
-                "reference": "55b1ae7bcb7f1b126c5d2b492d6f52342bec1951",
+                "url": "https://api.github.com/repos/symfony/lock/zipball/b2c6895cf282cfc11fae859e8b418c95dbb5d6f8",
+                "reference": "b2c6895cf282cfc11fae859e8b418c95dbb5d6f8",
                 "shasum": ""
             },
             "require": {
@@ -6930,20 +6986,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-28T08:27:51+00:00"
+            "time": "2020-06-09T14:02:17+00:00"
         },
         {
             "name": "symfony/phpunit-bridge",
-            "version": "v4.4.9",
+            "version": "v4.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/phpunit-bridge.git",
-                "reference": "004bf7b78114a8891c9264e4aabe7b281e4131f7"
+                "reference": "84cb4467ca3ea127f636806e2f6f27c5c1de4eb9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/004bf7b78114a8891c9264e4aabe7b281e4131f7",
-                "reference": "004bf7b78114a8891c9264e4aabe7b281e4131f7",
+                "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/84cb4467ca3ea127f636806e2f6f27c5c1de4eb9",
+                "reference": "84cb4467ca3ea127f636806e2f6f27c5c1de4eb9",
                 "shasum": ""
             },
             "require": {
@@ -7009,7 +7065,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-21T18:35:07+00:00"
+            "time": "2020-06-07T15:34:22+00:00"
         },
         {
             "name": "theseer/tokenizer",
diff --git a/composer/Metapackage/CoreRecommended/composer.json b/composer/Metapackage/CoreRecommended/composer.json
index 8006c950ed..6019759cf6 100644
--- a/composer/Metapackage/CoreRecommended/composer.json
+++ b/composer/Metapackage/CoreRecommended/composer.json
@@ -34,33 +34,33 @@
         "ralouphie/getallheaders": "3.0.3",
         "stack/builder": "v1.0.6",
         "symfony-cmf/routing": "2.3.2",
-        "symfony/console": "v4.4.9",
-        "symfony/debug": "v4.4.9",
-        "symfony/dependency-injection": "v4.4.9",
-        "symfony/error-handler": "v4.4.9",
-        "symfony/event-dispatcher": "v4.4.9",
+        "symfony/console": "v4.4.10",
+        "symfony/debug": "v4.4.10",
+        "symfony/dependency-injection": "v4.4.10",
+        "symfony/error-handler": "v4.4.10",
+        "symfony/event-dispatcher": "v4.4.10",
         "symfony/event-dispatcher-contracts": "v1.1.7",
-        "symfony/http-foundation": "v4.4.9",
-        "symfony/http-kernel": "v4.4.9",
-        "symfony/mime": "v5.1.0",
-        "symfony/polyfill-ctype": "v1.17.0",
-        "symfony/polyfill-iconv": "v1.17.0",
+        "symfony/http-foundation": "v4.4.10",
+        "symfony/http-kernel": "v4.4.10",
+        "symfony/mime": "v5.1.2",
+        "symfony/polyfill-ctype": "v1.17.1",
+        "symfony/polyfill-iconv": "v1.17.1",
         "symfony/polyfill-intl-idn": "v1.17.0",
-        "symfony/polyfill-mbstring": "v1.17.0",
+        "symfony/polyfill-mbstring": "v1.17.1",
         "symfony/polyfill-php72": "v1.17.0",
-        "symfony/polyfill-php73": "v1.17.0",
-        "symfony/polyfill-php80": "v1.17.0",
-        "symfony/process": "v4.4.9",
-        "symfony/psr-http-message-bridge": "v2.0.0",
-        "symfony/routing": "v4.4.9",
-        "symfony/serializer": "v4.4.9",
+        "symfony/polyfill-php73": "v1.17.1",
+        "symfony/polyfill-php80": "v1.17.1",
+        "symfony/process": "v4.4.10",
+        "symfony/psr-http-message-bridge": "v2.0.1",
+        "symfony/routing": "v4.4.10",
+        "symfony/serializer": "v4.4.10",
         "symfony/service-contracts": "v2.1.2",
-        "symfony/translation": "v4.4.9",
+        "symfony/translation": "v4.4.10",
         "symfony/translation-contracts": "v2.1.2",
-        "symfony/validator": "v4.4.9",
-        "symfony/var-dumper": "v5.1.0",
-        "symfony/yaml": "v4.4.9",
-        "twig/twig": "v2.12.5",
+        "symfony/validator": "v4.4.10",
+        "symfony/var-dumper": "v5.1.2",
+        "symfony/yaml": "v4.4.10",
+        "twig/twig": "2.x-dev",
         "typo3/phar-stream-wrapper": "v3.1.4"
     }
 }
diff --git a/composer/Metapackage/DevDependencies/composer.json b/composer/Metapackage/DevDependencies/composer.json
index ceb0cffc4c..5af2af7092 100644
--- a/composer/Metapackage/DevDependencies/composer.json
+++ b/composer/Metapackage/DevDependencies/composer.json
@@ -16,7 +16,7 @@
         "justinrainbow/json-schema": "^5.2",
         "mikey179/vfsstream": "^1.6.8",
         "phpspec/prophecy": "^1.7",
-        "phpunit/phpunit": "^8.4.1",
+        "phpunit/phpunit": "^8.4.1 || ^9",
         "symfony/browser-kit": "^4.4",
         "symfony/css-selector": "^4.4",
         "symfony/dom-crawler": "^4.4 !=4.4.5",
@@ -24,6 +24,6 @@
         "symfony/filesystem": "^4.4",
         "symfony/finder": "^4.4",
         "symfony/lock": "^4.4",
-        "symfony/phpunit-bridge": "^4.4"
+        "symfony/phpunit-bridge": "^4.4.8"
     }
 }
diff --git a/composer/Metapackage/PinnedDevDependencies/composer.json b/composer/Metapackage/PinnedDevDependencies/composer.json
index 9851eae6f3..64889b7014 100644
--- a/composer/Metapackage/PinnedDevDependencies/composer.json
+++ b/composer/Metapackage/PinnedDevDependencies/composer.json
@@ -51,13 +51,13 @@
         "seld/jsonlint": "1.8.0",
         "seld/phar-utils": "1.1.0",
         "squizlabs/php_codesniffer": "3.5.5",
-        "symfony/browser-kit": "v4.4.9",
-        "symfony/css-selector": "v4.4.9",
-        "symfony/dom-crawler": "v4.4.9",
-        "symfony/filesystem": "v4.4.9",
-        "symfony/finder": "v4.4.9",
-        "symfony/lock": "v4.4.9",
-        "symfony/phpunit-bridge": "v4.4.9",
+        "symfony/browser-kit": "v4.4.10",
+        "symfony/css-selector": "v4.4.10",
+        "symfony/dom-crawler": "v4.4.10",
+        "symfony/filesystem": "v4.4.10",
+        "symfony/finder": "v4.4.10",
+        "symfony/lock": "v4.4.10",
+        "symfony/phpunit-bridge": "v4.4.10",
         "theseer/tokenizer": "1.1.3",
         "webmozart/assert": "1.8.0"
     }
diff --git a/core/.cspell.json b/core/.cspell.json
index 0a4a3fd76b..732e822de8 100644
--- a/core/.cspell.json
+++ b/core/.cspell.json
@@ -8,6 +8,7 @@
       "lib/Drupal/Component/Diff/**",
       "lib/Drupal/Component/Transliteration/data/**",
       "lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php",
+      "lib/Drupal/Core/Php8/**",
       "modules/**/Migrate*Test.php",
       "modules/color/preview.html",
       "modules/color/tests/modules/color_test/themes/color_test_theme/color/preview.html",
diff --git a/core/composer.json b/core/composer.json
index cf64c7800f..525335fdc9 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -31,7 +31,7 @@
         "symfony/polyfill-iconv": "^1.0",
         "symfony/yaml": "^4.4",
         "typo3/phar-stream-wrapper": "^3.1.3",
-        "twig/twig": "^2.12.0",
+        "twig/twig": "2.x-dev",
         "doctrine/reflection": "^1.1",
         "doctrine/annotations": "^1.4",
         "guzzlehttp/guzzle": "^6.5.2",
@@ -196,7 +196,8 @@
             "lib/Drupal/Core/DrupalKernelInterface.php",
             "lib/Drupal/Core/Installer/InstallerRedirectTrait.php",
             "lib/Drupal/Core/Site/Settings.php"
-        ]
+        ],
+        "files": [ "includes/class_aliases.php" ]
     },
     "config": {
         "preferred-install": "dist"
diff --git a/core/includes/class_aliases.php b/core/includes/class_aliases.php
new file mode 100644
index 0000000000..196eb4968e
--- /dev/null
+++ b/core/includes/class_aliases.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Class aliases for different versions of PHP.
+ *
+ * @see core/composer.json
+ */
+
+if (PHP_VERSION_ID >= 80000) {
+  // Class aliases necessary for PHP8.
+  // @todo remove.
+  class_alias('\Drupal\Core\Php8\Doctrine\Reflection\StaticReflectionClass', '\Doctrine\Common\Reflection\StaticReflectionClass');
+}
+// This new code should work regardless of PHP version.
+class_alias('\Drupal\Core\Php8\Phpspec\Prophecy\ClassMirror', '\Prophecy\Doubler\Generator\ClassMirror');
+class_alias('\Drupal\Core\Php8\Phpdocumentor\ReflectionDocBlock\StandardTagFactory', '\phpDocumentor\Reflection\DocBlock\StandardTagFactory');
diff --git a/core/lib/Drupal/Component/Datetime/DateTimePlus.php b/core/lib/Drupal/Component/Datetime/DateTimePlus.php
index ef200d19f1..594ff59d82 100644
--- a/core/lib/Drupal/Component/Datetime/DateTimePlus.php
+++ b/core/lib/Drupal/Component/Datetime/DateTimePlus.php
@@ -626,7 +626,7 @@ public static function checkArray($array) {
     // Check for a valid date using checkdate(). Only values that
     // meet that test are valid.
     if (array_key_exists('year', $array) && array_key_exists('month', $array) && array_key_exists('day', $array)) {
-      if (@checkdate($array['month'], $array['day'], $array['year'])) {
+      if (@checkdate((int) $array['month'], (int) $array['day'], (int) $array['year'])) {
         $valid_date = TRUE;
       }
     }
diff --git a/core/lib/Drupal/Component/ProxyBuilder/ProxyBuilder.php b/core/lib/Drupal/Component/ProxyBuilder/ProxyBuilder.php
index 8d1b21818c..e92e369312 100644
--- a/core/lib/Drupal/Component/ProxyBuilder/ProxyBuilder.php
+++ b/core/lib/Drupal/Component/ProxyBuilder/ProxyBuilder.php
@@ -259,17 +259,13 @@ protected function buildMethod(\ReflectionMethod $reflection_method) {
   protected function buildParameter(\ReflectionParameter $parameter) {
     $parameter_string = '';
 
-    if ($parameter->isArray()) {
-      $parameter_string .= 'array ';
-    }
-    elseif ($parameter->isCallable()) {
-      $parameter_string .= 'callable ';
-    }
-    elseif ($class = $parameter->getClass()) {
-      $parameter_string .= '\\' . $class->getName() . ' ';
-    }
-    elseif ($parameter->hasType()) {
-      $parameter_string .= $parameter->getType()->getName() . ' ';
+    if ($parameter->hasType()) {
+      $type = $parameter->getType();
+      if (!$type->isBuiltin()) {
+        // The parameter is a class or interface.
+        $parameter_string .= '\\';
+      }
+      $parameter_string .= $type->getName() . ' ';
     }
 
     if ($parameter->isPassedByReference()) {
diff --git a/core/lib/Drupal/Component/Utility/ArgumentsResolver.php b/core/lib/Drupal/Component/Utility/ArgumentsResolver.php
index 0bd53b214e..7596eef252 100644
--- a/core/lib/Drupal/Component/Utility/ArgumentsResolver.php
+++ b/core/lib/Drupal/Component/Utility/ArgumentsResolver.php
@@ -69,7 +69,11 @@ public function getArguments(callable $callable) {
    *   Thrown when there is a missing parameter.
    */
   protected function getArgument(\ReflectionParameter $parameter) {
-    $parameter_type_hint = $parameter->getClass();
+    $parameter_type_hint = NULL;
+    $type = $parameter->getType();
+    if ($type && !$type->isBuiltin()) {
+      $parameter_type_hint = new \ReflectionClass($type->getName());
+    }
     $parameter_name = $parameter->getName();
 
     // If the argument exists and is NULL, return it, regardless of
diff --git a/core/lib/Drupal/Component/Utility/Bytes.php b/core/lib/Drupal/Component/Utility/Bytes.php
index d625b12364..13943b719a 100644
--- a/core/lib/Drupal/Component/Utility/Bytes.php
+++ b/core/lib/Drupal/Component/Utility/Bytes.php
@@ -29,6 +29,8 @@ public static function toInt($size) {
     $unit = preg_replace('/[^bkmgtpezy]/i', '', $size);
     // Remove the non-numeric characters from the size.
     $size = preg_replace('/[^0-9\.]/', '', $size);
+    // Ensure size is a proper number type.
+    $size = is_float($size) ? (float) $size : (int) $size;
     if ($unit) {
       // Find the position of the unit in the ordered string which is the power
       // of magnitude to multiply a kilobyte by.
diff --git a/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php b/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php
index 061460219c..d190317474 100644
--- a/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php
+++ b/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php
@@ -86,8 +86,14 @@ public function validate($token, $value = '') {
     if (empty($seed)) {
       return FALSE;
     }
+    $value = $this->computeToken($seed, $value);
+    // PHP 8.0 strictly typehints for hash_equals. Maintain BC until we can
+    // enforce scalar typehints on this method.
+    if (!is_string($token)) {
+      return FALSE;
+    }
 
-    return hash_equals($this->computeToken($seed, $value), $token);
+    return hash_equals($value, $token);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Composer/Composer.php b/core/lib/Drupal/Core/Composer/Composer.php
index 6b7d5899d6..2f60ab9d0f 100644
--- a/core/lib/Drupal/Core/Composer/Composer.php
+++ b/core/lib/Drupal/Core/Composer/Composer.php
@@ -310,7 +310,7 @@ public static function upgradePHPUnit(Event $event) {
       return;
     }
 
-    // If the PHP version is 7.3 or above and PHPUnit is less than version 7
+    // If the PHP version is 7.4 or above and PHPUnit is less than version 9
     // call the drupal-phpunit-upgrade script to upgrade PHPUnit.
     if (!static::upgradePHPUnitCheck($phpunit_package->getVersion())) {
       $event->getComposer()
@@ -332,7 +332,7 @@ public static function upgradePHPUnit(Event $event) {
    *   TRUE if the PHPUnit needs to be upgraded, FALSE if not.
    */
   public static function upgradePHPUnitCheck($phpunit_version) {
-    return !(version_compare(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, '7.3') >= 0 && version_compare($phpunit_version, '7.0') < 0);
+    return !(version_compare(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, '7.4') >= 0 && version_compare($phpunit_version, '9.0') < 0);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Database/StatementEmpty.php b/core/lib/Drupal/Core/Database/StatementEmpty.php
index a87bca920a..0d89ea3f4f 100644
--- a/core/lib/Drupal/Core/Database/StatementEmpty.php
+++ b/core/lib/Drupal/Core/Database/StatementEmpty.php
@@ -54,7 +54,7 @@ public function setFetchMode($mode, $a1 = NULL, $a2 = []) {}
   /**
    * {@inheritdoc}
    */
-  public function fetch($mode = NULL, $cursor_orientation = NULL, $cursor_offset = NULL) {
+  public function fetch(int $mode = \PDO::FETCH_BOTH, int $cursor_orientation = \PDO::FETCH_ORI_NEXT, int $cursor_offset = 0) {
     return NULL;
   }
 
diff --git a/core/lib/Drupal/Core/Database/StatementInterface.php b/core/lib/Drupal/Core/Database/StatementInterface.php
index 4f4248df04..f61e25e915 100644
--- a/core/lib/Drupal/Core/Database/StatementInterface.php
+++ b/core/lib/Drupal/Core/Database/StatementInterface.php
@@ -109,7 +109,7 @@ public function setFetchMode($mode, $a1 = NULL, $a2 = []);
    * @return
    *   A result, formatted according to $mode.
    */
-  public function fetch($mode = NULL, $cursor_orientation = NULL, $cursor_offset = NULL);
+  public function fetch(int $mode = \PDO::FETCH_BOTH, int $cursor_orientation = \PDO::FETCH_ORI_NEXT, int $cursor_offset = 0);
 
   /**
    * Returns a single field from the next record of a result set.
diff --git a/core/lib/Drupal/Core/Database/StatementPrefetch.php b/core/lib/Drupal/Core/Database/StatementPrefetch.php
index 3f6efdf364..aae3e1bf3b 100644
--- a/core/lib/Drupal/Core/Database/StatementPrefetch.php
+++ b/core/lib/Drupal/Core/Database/StatementPrefetch.php
@@ -375,7 +375,7 @@ public function rowCount() {
   /**
    * {@inheritdoc}
    */
-  public function fetch($fetch_style = NULL, $cursor_orientation = \PDO::FETCH_ORI_NEXT, $cursor_offset = NULL) {
+  public function fetch(int $mode = \PDO::FETCH_BOTH, int $cursor_orientation = \PDO::FETCH_ORI_NEXT, int $cursor_offset = 0) {
     if (isset($this->currentRow)) {
       // Set the fetch parameter.
       $this->fetchStyle = isset($fetch_style) ? $fetch_style : $this->defaultFetchStyle;
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/TaggedHandlersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/TaggedHandlersPass.php
index 35b5beaaa3..38c85ec63e 100644
--- a/core/lib/Drupal/Core/DependencyInjection/Compiler/TaggedHandlersPass.php
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/TaggedHandlersPass.php
@@ -130,8 +130,11 @@ protected function processServiceCollectorPass(array $pass, $consumer_id, Contai
     $priority_pos = NULL;
     $extra_params = [];
     foreach ($params as $pos => $param) {
-      if ($param->getClass()) {
-        $interface = $param->getClass();
+      /** @var \ReflectionType $type */
+      $type = $param->getType();
+      $class = NULL !== $type && !$type->isBuiltin() ? $type->getName() : NULL;
+      if (NULL !== $class) {
+        $interface = $class;
       }
       elseif ($param->getName() === 'id') {
         $id_pos = $pos;
@@ -152,7 +155,6 @@ protected function processServiceCollectorPass(array $pass, $consumer_id, Contai
         $method_name,
       ]));
     }
-    $interface = $interface->getName();
 
     // Find all tagged handlers.
     $handlers = [];
diff --git a/core/lib/Drupal/Core/Entity/EntityResolverManager.php b/core/lib/Drupal/Core/Entity/EntityResolverManager.php
index c10d72f551..a696645f4e 100644
--- a/core/lib/Drupal/Core/Entity/EntityResolverManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityResolverManager.php
@@ -74,6 +74,10 @@ protected function getControllerClass(array $defaults) {
       }
     }
 
+    if ($controller === NULL) {
+      return NULL;
+    }
+
     if (strpos($controller, ':') === FALSE) {
       if (method_exists($controller, '__invoke')) {
         return [$controller, '__invoke'];
@@ -135,7 +139,9 @@ protected function setParametersFromReflection($controller, Route $route) {
       if (isset($entity_types[$parameter_name])) {
         $entity_type = $entity_types[$parameter_name];
         $entity_class = $entity_type->getClass();
-        if (($reflection_class = $parameter->getClass()) && (is_subclass_of($entity_class, $reflection_class->name) || $entity_class == $reflection_class->name)) {
+        $type = $parameter->getType();
+        $reflection_class = NULL !== $type && !$type->isBuiltin() ? $type->getName() : NULL;
+        if ($reflection_class && (is_subclass_of($entity_class, $reflection_class) || $entity_class == $reflection_class)) {
           $parameter_definitions += [$parameter_name => []];
           $parameter_definitions[$parameter_name] += [
             'type' => 'entity:' . $parameter_name,
diff --git a/core/lib/Drupal/Core/Php8/Doctrine/Reflection/StaticReflectionClass.php b/core/lib/Drupal/Core/Php8/Doctrine/Reflection/StaticReflectionClass.php
new file mode 100644
index 0000000000..e24798e47b
--- /dev/null
+++ b/core/lib/Drupal/Core/Php8/Doctrine/Reflection/StaticReflectionClass.php
@@ -0,0 +1,415 @@
+<?php
+// @codingStandardsIgnoreFile
+
+namespace Drupal\Core\Php8\Doctrine\Reflection;
+
+use Doctrine\Common\Reflection\StaticReflectionParser;
+use ReflectionClass;
+use ReflectionException;
+
+class StaticReflectionClass extends ReflectionClass
+{
+    /**
+     * The static reflection parser object.
+     *
+     * @var StaticReflectionParser
+     */
+    private $staticReflectionParser;
+
+    public function __construct(StaticReflectionParser $staticReflectionParser)
+    {
+        $this->staticReflectionParser = $staticReflectionParser;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getName()
+    {
+        return $this->staticReflectionParser->getClassName();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getDocComment()
+    {
+        return $this->staticReflectionParser->getDocComment();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getNamespaceName()
+    {
+        return $this->staticReflectionParser->getNamespaceName();
+    }
+
+    /**
+     * @return string[]
+     */
+    public function getUseStatements()
+    {
+        return $this->staticReflectionParser->getUseStatements();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethod($name)
+    {
+        return $this->staticReflectionParser->getReflectionMethod($name);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getProperty($name)
+    {
+        return $this->staticReflectionParser->getReflectionProperty($name);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public static function export($argument, $return = false)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getConstant($name)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getConstants(int $filter = \ReflectionClassConstant::IS_PUBLIC | \ReflectionClassConstant::IS_PROTECTED | \ReflectionClassConstant::IS_PRIVATE)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getConstructor()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getDefaultProperties()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getEndLine()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getExtension()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getExtensionName()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getFileName()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getInterfaceNames()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getInterfaces()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethods($filter = null)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getModifiers()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getParentClass()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getProperties($filter = null)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getShortName()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getStartLine()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getStaticProperties()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getStaticPropertyValue($name, $default = '')
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getTraitAliases()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getTraitNames()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getTraits()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function hasConstant($name)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function hasMethod($name)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function hasProperty($name)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function implementsInterface($interface)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function inNamespace()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isAbstract()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isCloneable()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isFinal()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isInstance($object)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isInstantiable()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isInterface()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isInternal()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isIterateable()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isSubclassOf($class)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isTrait()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function isUserDefined()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function newInstance(...$args)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function newInstanceArgs(array $args = [])
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function newInstanceWithoutConstructor()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setStaticPropertyValue($name, $value)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function __toString()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+}
diff --git a/core/lib/Drupal/Core/Php8/Phpdocumentor/ReflectionDocBlock/StandardTagFactory.php b/core/lib/Drupal/Core/Php8/Phpdocumentor/ReflectionDocBlock/StandardTagFactory.php
new file mode 100644
index 0000000000..f9f4311071
--- /dev/null
+++ b/core/lib/Drupal/Core/Php8/Phpdocumentor/ReflectionDocBlock/StandardTagFactory.php
@@ -0,0 +1,341 @@
+<?php
+
+declare(strict_types=1);
+
+// @codingStandardsIgnoreFile
+
+namespace Drupal\Core\Php8\Phpdocumentor\ReflectionDocBlock;
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @link http://phpdoc.org
+ */
+
+use InvalidArgumentException;
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\TagFactory;
+use phpDocumentor\Reflection\DocBlock\Tags\Author;
+use phpDocumentor\Reflection\DocBlock\Tags\Covers;
+use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
+use phpDocumentor\Reflection\DocBlock\Tags\Generic;
+use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
+use phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag;
+use phpDocumentor\Reflection\DocBlock\Tags\Method;
+use phpDocumentor\Reflection\DocBlock\Tags\Param;
+use phpDocumentor\Reflection\DocBlock\Tags\Property;
+use phpDocumentor\Reflection\DocBlock\Tags\PropertyRead;
+use phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite;
+use phpDocumentor\Reflection\DocBlock\Tags\Return_;
+use phpDocumentor\Reflection\DocBlock\Tags\See as SeeTag;
+use phpDocumentor\Reflection\DocBlock\Tags\Since;
+use phpDocumentor\Reflection\DocBlock\Tags\Source;
+use phpDocumentor\Reflection\DocBlock\Tags\Throws;
+use phpDocumentor\Reflection\DocBlock\Tags\Uses;
+use phpDocumentor\Reflection\DocBlock\Tags\Var_;
+use phpDocumentor\Reflection\DocBlock\Tags\Version;
+use phpDocumentor\Reflection\FqsenResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use ReflectionMethod;
+use ReflectionParameter;
+use Webmozart\Assert\Assert;
+use function array_merge;
+use function array_slice;
+use function call_user_func_array;
+use function count;
+use function get_class;
+use function preg_match;
+use function strpos;
+use function trim;
+
+/**
+ * Creates a Tag object given the contents of a tag.
+ *
+ * This Factory is capable of determining the appropriate class for a tag and instantiate it using its `create`
+ * factory method. The `create` factory method of a Tag can have a variable number of arguments; this way you can
+ * pass the dependencies that you need to construct a tag object.
+ *
+ * > Important: each parameter in addition to the body variable for the `create` method must default to null, otherwise
+ * > it violates the constraint with the interface; it is recommended to use the {@see Assert::notNull()} method to
+ * > verify that a dependency is actually passed.
+ *
+ * This Factory also features a Service Locator component that is used to pass the right dependencies to the
+ * `create` method of a tag; each dependency should be registered as a service or as a parameter.
+ *
+ * When you want to use a Tag of your own with custom handling you need to call the `registerTagHandler` method, pass
+ * the name of the tag and a Fully Qualified Class Name pointing to a class that implements the Tag interface.
+ */
+final class StandardTagFactory implements TagFactory
+{
+    /** PCRE regular expression matching a tag name. */
+    public const REGEX_TAGNAME = '[\w\-\_\\\\:]+';
+
+    /**
+     * @var array<class-string<Tag>> An array with a tag as a key, and an
+     *                               FQCN to a class that handles it as an array value.
+     */
+    private $tagHandlerMappings = [
+        'author' => Author::class,
+        'covers' => Covers::class,
+        'deprecated' => Deprecated::class,
+        // 'example'        => '\phpDocumentor\Reflection\DocBlock\Tags\Example',
+        'link' => LinkTag::class,
+        'method' => Method::class,
+        'param' => Param::class,
+        'property-read' => PropertyRead::class,
+        'property' => Property::class,
+        'property-write' => PropertyWrite::class,
+        'return' => Return_::class,
+        'see' => SeeTag::class,
+        'since' => Since::class,
+        'source' => Source::class,
+        'throw' => Throws::class,
+        'throws' => Throws::class,
+        'uses' => Uses::class,
+        'var' => Var_::class,
+        'version' => Version::class,
+    ];
+
+    /**
+     * @var array<class-string<Tag>> An array with a anotation s a key, and an
+     *      FQCN to a class that handles it as an array value.
+     */
+    private $annotationMappings = [];
+
+    /**
+     * @var ReflectionParameter[][] a lazy-loading cache containing parameters
+     *      for each tagHandler that has been used.
+     */
+    private $tagHandlerParameterCache = [];
+
+    /** @var FqsenResolver */
+    private $fqsenResolver;
+
+    /**
+     * @var mixed[] an array representing a simple Service Locator where we can store parameters and
+     *     services that can be inserted into the Factory Methods of Tag Handlers.
+     */
+    private $serviceLocator = [];
+
+    /**
+     * Initialize this tag factory with the means to resolve an FQSEN and optionally a list of tag handlers.
+     *
+     * If no tag handlers are provided than the default list in the {@see self::$tagHandlerMappings} property
+     * is used.
+     *
+     * @see self::registerTagHandler() to add a new tag handler to the existing default list.
+     *
+     * @param array<class-string<Tag>> $tagHandlers
+     */
+    public function __construct(FqsenResolver $fqsenResolver, ?array $tagHandlers = null)
+    {
+        $this->fqsenResolver = $fqsenResolver;
+        if ($tagHandlers !== null) {
+            $this->tagHandlerMappings = $tagHandlers;
+        }
+
+        $this->addService($fqsenResolver, FqsenResolver::class);
+    }
+
+    public function create(string $tagLine, ?TypeContext $context = null) : Tag
+    {
+        if (!$context) {
+            $context = new TypeContext('');
+        }
+
+        [$tagName, $tagBody] = $this->extractTagParts($tagLine);
+
+        return $this->createTag(trim($tagBody), $tagName, $context);
+    }
+
+    /**
+     * @param mixed $value
+     */
+    public function addParameter(string $name, $value) : void
+    {
+        $this->serviceLocator[$name] = $value;
+    }
+
+    public function addService(object $service, ?string $alias = null) : void
+    {
+        $this->serviceLocator[$alias ?: get_class($service)] = $service;
+    }
+
+    public function registerTagHandler(string $tagName, string $handler) : void
+    {
+        Assert::stringNotEmpty($tagName);
+        Assert::classExists($handler);
+        Assert::implementsInterface($handler, StaticMethod::class);
+
+        if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
+            throw new InvalidArgumentException(
+                'A namespaced tag must have a leading backslash as it must be fully qualified'
+            );
+        }
+
+        $this->tagHandlerMappings[$tagName] = $handler;
+    }
+
+    /**
+     * Extracts all components for a tag.
+     *
+     * @return string[]
+     */
+    private function extractTagParts(string $tagLine) : array
+    {
+        $matches = [];
+        if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')((?:[\s\(\{])\s*([^\s].*)|$)/us', $tagLine, $matches)) {
+            throw new InvalidArgumentException(
+                'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'
+            );
+        }
+
+        if (count($matches) < 3) {
+            $matches[] = '';
+        }
+
+        return array_slice($matches, 1);
+    }
+
+    /**
+     * Creates a new tag object with the given name and body or returns null if the tag name was recognized but the
+     * body was invalid.
+     */
+    private function createTag(string $body, string $name, TypeContext $context) : Tag
+    {
+        $handlerClassName = $this->findHandlerClassName($name, $context);
+        $arguments        = $this->getArgumentsForParametersFromWiring(
+            $this->fetchParametersForHandlerFactoryMethod($handlerClassName),
+            $this->getServiceLocatorWithDynamicParameters($context, $name, $body)
+        );
+
+        try {
+            $callable = [$handlerClassName, 'create'];
+            Assert::isCallable($callable);
+            /** @phpstan-var callable(string): ?Tag $callable */
+            $tag = call_user_func_array($callable, $arguments);
+
+            return $tag ?? InvalidTag::create($body, $name);
+        } catch (InvalidArgumentException $e) {
+            return InvalidTag::create($body, $name)->withError($e);
+        }
+    }
+
+    /**
+     * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`).
+     *
+     * @return class-string<Tag>
+     */
+    private function findHandlerClassName(string $tagName, TypeContext $context) : string
+    {
+        $handlerClassName = Generic::class;
+        if (isset($this->tagHandlerMappings[$tagName])) {
+            $handlerClassName = $this->tagHandlerMappings[$tagName];
+        } elseif ($this->isAnnotation($tagName)) {
+            // TODO: Annotation support is planned for a later stage and as such is disabled for now
+            $tagName = (string) $this->fqsenResolver->resolve($tagName, $context);
+            if (isset($this->annotationMappings[$tagName])) {
+                $handlerClassName = $this->annotationMappings[$tagName];
+            }
+        }
+
+        return $handlerClassName;
+    }
+
+    /**
+     * Retrieves the arguments that need to be passed to the Factory Method with the given Parameters.
+     *
+     * @param ReflectionParameter[] $parameters
+     * @param mixed[]               $locator
+     *
+     * @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters
+     *     is provided with this method.
+     */
+    private function getArgumentsForParametersFromWiring(array $parameters, array $locator) : array
+    {
+        $arguments = [];
+        foreach ($parameters as $parameter) {
+            $typeHint = null;
+            if ($parameter->hasType() && !$parameter->getType()->isBuiltin()) {
+                $typeHint = $parameter->getType()->getName();
+            }
+
+            if (isset($locator[$typeHint])) {
+                $arguments[] = $locator[$typeHint];
+                continue;
+            }
+
+            $parameterName = $parameter->getName();
+            if (isset($locator[$parameterName])) {
+                $arguments[] = $locator[$parameterName];
+                continue;
+            }
+
+            $arguments[] = null;
+        }
+
+        return $arguments;
+    }
+
+    /**
+     * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given
+     * tag handler class name.
+     *
+     * @return ReflectionParameter[]
+     */
+    private function fetchParametersForHandlerFactoryMethod(string $handlerClassName) : array
+    {
+        if (!isset($this->tagHandlerParameterCache[$handlerClassName])) {
+            $methodReflection                                  = new ReflectionMethod($handlerClassName, 'create');
+            $this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters();
+        }
+
+        return $this->tagHandlerParameterCache[$handlerClassName];
+    }
+
+    /**
+     * Returns a copy of this class' Service Locator with added dynamic parameters,
+     * such as the tag's name, body and Context.
+     *
+     * @param TypeContext $context The Context (namespace and aliasses) that may be
+     *  passed and is used to resolve FQSENs.
+     * @param string      $tagName The name of the tag that may be
+     *  passed onto the factory method of the Tag class.
+     * @param string      $tagBody The body of the tag that may be
+     *  passed onto the factory method of the Tag class.
+     *
+     * @return mixed[]
+     */
+    private function getServiceLocatorWithDynamicParameters(
+        TypeContext $context,
+        string $tagName,
+        string $tagBody
+    ) : array {
+        return array_merge(
+            $this->serviceLocator,
+            [
+                'name' => $tagName,
+                'body' => $tagBody,
+                TypeContext::class => $context,
+            ]
+        );
+    }
+
+    /**
+     * Returns whether the given tag belongs to an annotation.
+     *
+     * @todo this method should be populated once we implement Annotation notation support.
+     */
+    private function isAnnotation(string $tagContent) : bool
+    {
+        // 1. Contains a namespace separator
+        // 2. Contains parenthesis
+        // 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part
+        //    of the annotation class name matches the found tag name
+
+        return false;
+    }
+}
diff --git a/core/lib/Drupal/Core/Php8/Phpspec/Prophecy/ClassMirror.php b/core/lib/Drupal/Core/Php8/Phpspec/Prophecy/ClassMirror.php
new file mode 100644
index 0000000000..299543fdca
--- /dev/null
+++ b/core/lib/Drupal/Core/Php8/Phpspec/Prophecy/ClassMirror.php
@@ -0,0 +1,243 @@
+<?php
+// @codingStandardsIgnoreFile
+
+namespace Drupal\Core\Php8\Phpspec\Prophecy;
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *     Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Prophecy\Doubler\Generator\Node;
+use Prophecy\Exception\InvalidArgumentException;
+use Prophecy\Exception\Doubler\ClassMirrorException;
+use ReflectionClass;
+use ReflectionMethod;
+use ReflectionParameter;
+
+/**
+ * Class mirror.
+ * Core doubler class. Mirrors specific class and/or interfaces into class node tree.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ClassMirror
+{
+    private static $reflectableMethods = array(
+        '__construct',
+        '__destruct',
+        '__sleep',
+        '__wakeup',
+        '__toString',
+        '__call',
+        '__invoke'
+    );
+
+    /**
+     * Reflects provided arguments into class node.
+     *
+     * @param ReflectionClass   $class
+     * @param ReflectionClass[] $interfaces
+     *
+     * @return Node\ClassNode
+     *
+     * @throws \Prophecy\Exception\InvalidArgumentException
+     */
+    public function reflect(ReflectionClass $class = null, array $interfaces)
+    {
+        $node = new Node\ClassNode;
+
+        if (null !== $class) {
+            if (true === $class->isInterface()) {
+                throw new InvalidArgumentException(sprintf(
+                    "Could not reflect %s as a class, because it\n".
+                    "is interface - use the second argument instead.",
+                    $class->getName()
+                ));
+            }
+
+            $this->reflectClassToNode($class, $node);
+        }
+
+        foreach ($interfaces as $interface) {
+            if (!$interface instanceof ReflectionClass) {
+                throw new InvalidArgumentException(sprintf(
+                    "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n".
+                    "a second argument to `ClassMirror::reflect(...)`, but got %s.",
+                    is_object($interface) ? get_class($interface).' class' : gettype($interface)
+                ));
+            }
+            if (false === $interface->isInterface()) {
+                throw new InvalidArgumentException(sprintf(
+                    "Could not reflect %s as an interface, because it\n".
+                    "is class - use the first argument instead.",
+                    $interface->getName()
+                ));
+            }
+
+            $this->reflectInterfaceToNode($interface, $node);
+        }
+
+        $node->addInterface('Prophecy\Doubler\Generator\ReflectionInterface');
+
+        return $node;
+    }
+
+    private function reflectClassToNode(ReflectionClass $class, Node\ClassNode $node)
+    {
+        if (true === $class->isFinal()) {
+            throw new ClassMirrorException(sprintf(
+                'Could not reflect class %s as it is marked final.', $class->getName()
+            ), $class);
+        }
+
+        $node->setParentClass($class->getName());
+
+        foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) {
+            if (false === $method->isProtected()) {
+                continue;
+            }
+
+            $this->reflectMethodToNode($method, $node);
+        }
+
+        foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
+            if (0 === strpos($method->getName(), '_')
+                && !in_array($method->getName(), self::$reflectableMethods)) {
+                continue;
+            }
+
+            if (true === $method->isFinal()) {
+                $node->addUnextendableMethod($method->getName());
+                continue;
+            }
+
+            $this->reflectMethodToNode($method, $node);
+        }
+    }
+
+    private function reflectInterfaceToNode(ReflectionClass $interface, Node\ClassNode $node)
+    {
+        $node->addInterface($interface->getName());
+
+        foreach ($interface->getMethods() as $method) {
+            $this->reflectMethodToNode($method, $node);
+        }
+    }
+
+    private function reflectMethodToNode(ReflectionMethod $method, Node\ClassNode $classNode)
+    {
+        $node = new Node\MethodNode($method->getName());
+
+        if (true === $method->isProtected()) {
+            $node->setVisibility('protected');
+        }
+
+        if (true === $method->isStatic()) {
+            $node->setStatic();
+        }
+
+        if (true === $method->returnsReference()) {
+            $node->setReturnsReference();
+        }
+
+        if (version_compare(PHP_VERSION, '7.0', '>=') && $method->hasReturnType()) {
+            $returnType = PHP_VERSION_ID >= 70100 ? $method->getReturnType()->getName() : (string) $method->getReturnType();
+            $returnTypeLower = strtolower($returnType);
+
+            if ('self' === $returnTypeLower) {
+                $returnType = $method->getDeclaringClass()->getName();
+            }
+            if ('parent' === $returnTypeLower) {
+                $returnType = $method->getDeclaringClass()->getParentClass()->getName();
+            }
+
+            $node->setReturnType($returnType);
+
+            if (version_compare(PHP_VERSION, '7.1', '>=') && $method->getReturnType()->allowsNull()) {
+                $node->setNullableReturnType(true);
+            }
+        }
+
+        if (is_array($params = $method->getParameters()) && count($params)) {
+            foreach ($params as $param) {
+                $this->reflectArgumentToNode($param, $node);
+            }
+        }
+
+        $classNode->addMethod($node);
+    }
+
+    private function reflectArgumentToNode(ReflectionParameter $parameter, Node\MethodNode $methodNode)
+    {
+        $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName();
+        $node = new Node\ArgumentNode($name);
+
+        $node->setTypeHint($this->getTypeHint($parameter));
+
+        if ($this->isVariadic($parameter)) {
+            $node->setAsVariadic();
+        }
+
+        if ($this->hasDefaultValue($parameter)) {
+            $node->setDefault($this->getDefaultValue($parameter));
+        }
+
+        if ($parameter->isPassedByReference()) {
+            $node->setAsPassedByReference();
+        }
+
+        $node->setAsNullable($this->isNullable($parameter));
+
+        $methodNode->addArgument($node);
+    }
+
+    private function hasDefaultValue(ReflectionParameter $parameter)
+    {
+        if ($this->isVariadic($parameter)) {
+            return false;
+        }
+
+        if ($parameter->isDefaultValueAvailable()) {
+            return true;
+        }
+
+        return $parameter->isOptional() || $this->isNullable($parameter);
+    }
+
+    private function getDefaultValue(ReflectionParameter $parameter)
+    {
+        if (!$parameter->isDefaultValueAvailable()) {
+            return null;
+        }
+
+        return $parameter->getDefaultValue();
+    }
+
+    private function getTypeHint(ReflectionParameter $parameter)
+    {
+        if (version_compare(PHP_VERSION, '7.0', '>=') && true === $parameter->hasType()) {
+            $typehint = $parameter->getType()->getName();
+            if ($typehint === 'self') {
+              $typehint = $parameter->getDeclaringClass()->getName();
+            }
+            return $typehint;
+        }
+
+        return null;
+    }
+
+    private function isVariadic(ReflectionParameter $parameter)
+    {
+        return PHP_VERSION_ID >= 50600 && $parameter->isVariadic();
+    }
+
+    private function isNullable(ReflectionParameter $parameter)
+    {
+        return $parameter->allowsNull() && null !== $this->getTypeHint($parameter);
+    }
+
+}
diff --git a/core/lib/Drupal/Core/Render/Element/StatusReport.php b/core/lib/Drupal/Core/Render/Element/StatusReport.php
index 85b12e25ff..3d9313af11 100644
--- a/core/lib/Drupal/Core/Render/Element/StatusReport.php
+++ b/core/lib/Drupal/Core/Render/Element/StatusReport.php
@@ -52,7 +52,7 @@ public static function preRenderGroupRequirements($element) {
     // Order the grouped requirements by a set order.
     $order = array_flip($element['#priorities']);
     uksort($grouped_requirements, function ($a, $b) use ($order) {
-      return $order[$a] > $order[$b];
+      return $order[$a] <=> $order[$b];
     });
 
     $element['#grouped_requirements'] = $grouped_requirements;
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index 09b988f413..05c53c3e8b 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -297,7 +297,7 @@ public static function fromUri($uri, $options = []) {
     // Extract query parameters and fragment and merge them into $uri_options,
     // but preserve the original $options for the fallback case.
     $uri_options = $options;
-    if (isset($uri_parts['fragment'])) {
+    if (isset($uri_parts['fragment']) && $uri_parts['fragment'] !== '') {
       $uri_options += ['fragment' => $uri_parts['fragment']];
       unset($uri_parts['fragment']);
     }
diff --git a/core/modules/hal/tests/src/Kernel/EntityTranslationNormalizeTest.php b/core/modules/hal/tests/src/Kernel/EntityTranslationNormalizeTest.php
index bd31f529cc..af1f10a22c 100644
--- a/core/modules/hal/tests/src/Kernel/EntityTranslationNormalizeTest.php
+++ b/core/modules/hal/tests/src/Kernel/EntityTranslationNormalizeTest.php
@@ -70,8 +70,8 @@ public function testNodeTranslation() {
 
     $normalized = $this->serializer->normalize($node, $this->format);
 
-    $this->assertContains(['lang' => 'en', 'value' => $node->getTitle()], $normalized['title'], 'Original language title has been normalized.');
-    $this->assertContains(['lang' => 'de', 'value' => $translation->getTitle()], $normalized['title'], 'Translation language title has been normalized.');
+    $this->assertContainsEquals(['lang' => 'en', 'value' => $node->getTitle()], $normalized['title'], 'Original language title has been normalized.');
+    $this->assertContainsEquals(['lang' => 'de', 'value' => $translation->getTitle()], $normalized['title'], 'Translation language title has been normalized.');
 
     /** @var \Drupal\node\NodeInterface $denormalized_node */
     $denormalized_node = $this->serializer->denormalize($normalized, 'Drupal\node\Entity\Node', $this->format);
diff --git a/core/modules/migrate/tests/src/Unit/MigrateStubTest.php b/core/modules/migrate/tests/src/Unit/MigrateStubTest.php
index fcf171b17c..dbdc5e8e19 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateStubTest.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateStubTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\migrate\Unit;
 
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use Drupal\migrate\MigrateStub;
 use Drupal\migrate\Plugin\MigrateDestinationInterface;
 use Drupal\migrate\Plugin\MigrateIdMapInterface;
@@ -22,6 +23,8 @@
  */
 class MigrateStubTest extends TestCase {
 
+  use PhpUnitWarningsCompatibilityTrait;
+
   /**
    * The plugin manager prophecy.
    *
diff --git a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
index 2d5e1678ac..b27f16bcae 100644
--- a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
+++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
@@ -131,8 +131,10 @@ public static function create(ContainerInterface $container, array $configuratio
    *   An instance of the current toolkit object.
    */
   public function setResource($resource) {
-    if (!is_resource($resource) || get_resource_type($resource) != 'gd') {
-      throw new \InvalidArgumentException('Invalid resource argument');
+    if (!($resource instanceof \GdImage)) {
+      if (!is_resource($resource) || get_resource_type($resource) != 'gd') {
+        throw new \InvalidArgumentException('Invalid resource argument');
+      }
     }
     $this->preLoadInfo = NULL;
     $this->resource = $resource;
@@ -146,7 +148,7 @@ public function setResource($resource) {
    *   The GD image resource, or NULL if not available.
    */
   public function getResource() {
-    if (!is_resource($this->resource)) {
+    if (!(is_resource($this->resource) || $this->resource instanceof \GdImage)) {
       $this->load();
     }
     return $this->resource;
diff --git a/core/modules/system/tests/src/Kernel/Entity/EntityReferenceSelectionReferenceableTest.php b/core/modules/system/tests/src/Kernel/Entity/EntityReferenceSelectionReferenceableTest.php
index 16b9810535..a4bea10bdf 100644
--- a/core/modules/system/tests/src/Kernel/Entity/EntityReferenceSelectionReferenceableTest.php
+++ b/core/modules/system/tests/src/Kernel/Entity/EntityReferenceSelectionReferenceableTest.php
@@ -126,7 +126,7 @@ public function testReferenceablesWithNoLabelKey($match, $match_operator, $limit
       // entity labels.
       // @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface::getReferenceableEntities()
       $item = is_string($item) ? Html::escape($item) : $item;
-      $this->assertContains($item, $referenceables[$this->bundle]);
+      $this->assertContainsEquals($item, $referenceables[$this->bundle]);
     }
 
     // Test ::countReferenceableEntities().
diff --git a/core/modules/user/src/PermissionHandler.php b/core/modules/user/src/PermissionHandler.php
index 056c435dff..dd04ab0118 100644
--- a/core/modules/user/src/PermissionHandler.php
+++ b/core/modules/user/src/PermissionHandler.php
@@ -205,10 +205,10 @@ protected function sortPermissions(array $all_permissions = []) {
 
     uasort($all_permissions, function (array $permission_a, array $permission_b) use ($modules) {
       if ($modules[$permission_a['provider']] == $modules[$permission_b['provider']]) {
-        return $permission_a['title'] > $permission_b['title'];
+        return $permission_a['title'] <=> $permission_b['title'];
       }
       else {
-        return $modules[$permission_a['provider']] > $modules[$permission_b['provider']];
+        return $modules[$permission_a['provider']] <=> $modules[$permission_b['provider']];
       }
     });
     return $all_permissions;
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 955eb5bf55..91b4778261 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -149,7 +149,7 @@
 }
 
 if (!Composer::upgradePHPUnitCheck(Version::id())) {
-  simpletest_script_print_error("PHPUnit testing framework version 7 or greater is required when running on PHP 7.3 or greater. Run the command 'composer run-script drupal-phpunit-upgrade' in order to fix this.");
+  simpletest_script_print_error("PHPUnit testing framework version 9 or greater is required when running on PHP 7.4 or greater. Run the command 'composer run-script drupal-phpunit-upgrade' in order to fix this.");
   exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
 }
 
diff --git a/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php b/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php
index 88e38fddf6..5182871ac5 100644
--- a/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php
+++ b/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php
@@ -7,7 +7,7 @@
 use Behat\Mink\Mink;
 use Behat\Mink\Session;
 use Drupal\Component\FileSystem\FileSystem as DrupalFilesystem;
-use Drupal\Tests\Traits\PHPUnit8Warnings;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
 use Symfony\Component\Finder\Finder;
@@ -52,7 +52,7 @@
 abstract class BuildTestBase extends TestCase {
 
   use ExternalCommandRequirementsTrait;
-  use PHPUnit8Warnings;
+  use PhpUnitWarningsCompatibilityTrait;
 
   /**
    * The working directory where this test will manipulate files.
diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php
index d05429864d..0235a4216e 100644
--- a/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php
@@ -617,7 +617,7 @@ public function testUnmetDependency() {
         'Configuration <em class="placeholder">unknown.config</em> depends on the <em class="placeholder">unknown</em> extension that will not be installed after import.',
       ];
       foreach ($expected as $expected_message) {
-        $this->assertContains($expected_message, $error_log, $expected_message);
+        $this->assertContainsEquals($expected_message, $error_log, $expected_message);
       }
     }
 
@@ -663,7 +663,7 @@ public function testUnmetDependency() {
         'Configuration <em class="placeholder">config_test.dynamic.dotted.theme</em> depends on themes (<em class="placeholder">unknown, Seven</em>) that will not be installed after import.',
       ];
       foreach ($expected as $expected_message) {
-        $this->assertContains($expected_message, $error_log, $expected_message);
+        $this->assertContainsEquals($expected_message, $error_log, $expected_message);
       }
     }
   }
diff --git a/core/tests/Drupal/KernelTests/Core/Database/QueryTest.php b/core/tests/Drupal/KernelTests/Core/Database/QueryTest.php
index bff32113f2..55a57f9931 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/QueryTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/QueryTest.php
@@ -67,14 +67,14 @@ public function testArrayArgumentsSQLInjection() {
   public function testConditionOperatorArgumentsSQLInjection() {
     $injection = "IS NOT NULL) ;INSERT INTO {test} (name) VALUES ('test12345678'); -- ";
 
-    $previous_error_handler = set_error_handler(function ($severity, $message, $filename, $lineno, $context) use (&$previous_error_handler) {
+    $previous_error_handler = set_error_handler(function ($severity, $message, $filename, $lineno) use (&$previous_error_handler) {
       // Normalize the filename to use UNIX directory separators.
       if (preg_match('@core/lib/Drupal/Core/Database/Query/Condition.php$@', str_replace(DIRECTORY_SEPARATOR, '/', $filename))) {
         // Convert errors to exceptions for testing purposes below.
         throw new \ErrorException($message, 0, $severity, $filename, $lineno);
       }
       if ($previous_error_handler) {
-        return $previous_error_handler($severity, $message, $filename, $lineno, $context);
+        return $previous_error_handler($severity, $message, $filename, $lineno);
       }
     });
     try {
diff --git a/core/tests/Drupal/KernelTests/Core/File/NameMungingTest.php b/core/tests/Drupal/KernelTests/Core/File/NameMungingTest.php
index 1e97b7a805..991f9263c9 100644
--- a/core/tests/Drupal/KernelTests/Core/File/NameMungingTest.php
+++ b/core/tests/Drupal/KernelTests/Core/File/NameMungingTest.php
@@ -42,7 +42,7 @@ public function testMunging() {
     $munged_name = file_munge_filename($this->name, '', TRUE);
     $messages = \Drupal::messenger()->all();
     \Drupal::messenger()->deleteAll();
-    $this->assertContains(strtr('For security reasons, your upload has been renamed to <em class="placeholder">%filename</em>.', ['%filename' => $munged_name]), $messages['status'], 'Alert properly set when a file is renamed.');
+    $this->assertContainsEquals(strtr('For security reasons, your upload has been renamed to <em class="placeholder">%filename</em>.', ['%filename' => $munged_name]), $messages['status'], 'Alert properly set when a file is renamed.');
     $this->assertNotEqual($munged_name, $this->name, new FormattableMarkup('The new filename (%munged) has been modified from the original (%original)', ['%munged' => $munged_name, '%original' => $this->name]));
   }
 
diff --git a/core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php b/core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php
index ac305022ff..e244e05e04 100644
--- a/core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php
@@ -49,7 +49,7 @@ public function testRemoveSingleMessage() {
 
     // Check we only have the second one.
     $this->assertCount(1, $this->messenger->messagesByType(MessengerInterface::TYPE_STATUS));
-    $this->assertContains('Second message with <em>markup!</em> (not removed).', $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS));
+    $this->assertContainsEquals('Second message with <em>markup!</em> (not removed).', $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS));
 
   }
 
diff --git a/core/tests/Drupal/KernelTests/Core/Test/Comparator/MarkupInterfaceComparatorTest.php b/core/tests/Drupal/KernelTests/Core/Test/Comparator/MarkupInterfaceComparatorTest.php
index 4a72a3dc42..bc8685db01 100644
--- a/core/tests/Drupal/KernelTests/Core/Test/Comparator/MarkupInterfaceComparatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Test/Comparator/MarkupInterfaceComparatorTest.php
@@ -7,6 +7,7 @@
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\TestTools\Comparator\MarkupInterfaceComparator;
 use PHPUnit\Framework\Error\Notice;
+use PHPUnit\Framework\Error\Warning;
 use SebastianBergmann\Comparator\Factory;
 use SebastianBergmann\Comparator\ComparisonFailure;
 
@@ -108,7 +109,7 @@ public function dataSetProvider() {
         new FormattableMarkup('goldfinger', []),
         ['goldfinger'],
         FALSE,
-        Notice::class,
+        PHP_VERSION_ID >= 80000 ? Warning::class : Notice::class,
       ],
       'stdClass vs TranslatableMarkup' => [
         (object) ['goldfinger'],
diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php
index 5d13f51683..f34a6e4ce6 100644
--- a/core/tests/Drupal/KernelTests/KernelTestBase.php
+++ b/core/tests/Drupal/KernelTests/KernelTestBase.php
@@ -20,7 +20,7 @@
 use Drupal\Tests\ConfigTestTrait;
 use Drupal\Tests\RandomGeneratorTrait;
 use Drupal\Tests\TestRequirementsTrait;
-use Drupal\Tests\Traits\PHPUnit8Warnings;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use Drupal\TestTools\Comparator\MarkupInterfaceComparator;
 use PHPUnit\Framework\Exception;
 use PHPUnit\Framework\TestCase;
@@ -80,7 +80,7 @@ abstract class KernelTestBase extends TestCase implements ServiceProviderInterfa
   use RandomGeneratorTrait;
   use ConfigTestTrait;
   use TestRequirementsTrait;
-  use PHPUnit8Warnings;
+  use PhpUnitWarningsCompatibilityTrait;
 
   /**
    * {@inheritdoc}
diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php
index afea7671e1..239d182f6f 100644
--- a/core/tests/Drupal/Tests/BrowserTestBase.php
+++ b/core/tests/Drupal/Tests/BrowserTestBase.php
@@ -16,7 +16,7 @@
 use Drupal\Tests\block\Traits\BlockCreationTrait;
 use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
 use Drupal\Tests\node\Traits\NodeCreationTrait;
-use Drupal\Tests\Traits\PHPUnit8Warnings;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use Drupal\Tests\user\Traits\UserCreationTrait;
 use Drupal\TestTools\Comparator\MarkupInterfaceComparator;
 use GuzzleHttp\Cookie\CookieJar;
@@ -64,7 +64,7 @@ abstract class BrowserTestBase extends TestCase {
     createUser as drupalCreateUser;
   }
   use XdebugRequestTrait;
-  use PHPUnit8Warnings;
+  use PhpUnitWarningsCompatibilityTrait;
 
   /**
    * The database prefix of this test run.
diff --git a/core/tests/Drupal/Tests/Component/Annotation/Plugin/Discovery/AnnotationBridgeDecoratorTest.php b/core/tests/Drupal/Tests/Component/Annotation/Plugin/Discovery/AnnotationBridgeDecoratorTest.php
index 12732e110a..5cbfa08e16 100644
--- a/core/tests/Drupal/Tests/Component/Annotation/Plugin/Discovery/AnnotationBridgeDecoratorTest.php
+++ b/core/tests/Drupal/Tests/Component/Annotation/Plugin/Discovery/AnnotationBridgeDecoratorTest.php
@@ -6,6 +6,7 @@
 use Drupal\Component\Annotation\Plugin\Discovery\AnnotationBridgeDecorator;
 use Drupal\Component\Plugin\Definition\PluginDefinition;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -14,6 +15,8 @@
  */
 class AnnotationBridgeDecoratorTest extends TestCase {
 
+  use PhpUnitWarningsCompatibilityTrait;
+
   /**
    * @covers ::getDefinitions
    */
diff --git a/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php b/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php
index 2172f20180..302bf3f08d 100644
--- a/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php
+++ b/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\Component\DependencyInjection;
 
 use Drupal\Component\Utility\Crypt;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
@@ -24,6 +25,8 @@
  */
 class ContainerTest extends TestCase {
 
+  use PhpUnitWarningsCompatibilityTrait;
+
   /**
    * The tested container.
    *
diff --git a/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php b/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php
index 92ee5490ac..44927ffb31 100644
--- a/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php
+++ b/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\Component\DependencyInjection\Dumper {
 
   use Drupal\Component\Utility\Crypt;
+  use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
   use PHPUnit\Framework\TestCase;
   use Symfony\Component\DependencyInjection\Definition;
   use Symfony\Component\DependencyInjection\Reference;
@@ -24,6 +25,8 @@
    */
   class OptimizedPhpArrayDumperTest extends TestCase {
 
+    use PhpUnitWarningsCompatibilityTrait;
+
     /**
      * The container builder instance.
      *
diff --git a/core/tests/Drupal/Tests/Component/Diff/Engine/DiffOpTest.php b/core/tests/Drupal/Tests/Component/Diff/Engine/DiffOpTest.php
index be2054811e..76f0064e7d 100644
--- a/core/tests/Drupal/Tests/Component/Diff/Engine/DiffOpTest.php
+++ b/core/tests/Drupal/Tests/Component/Diff/Engine/DiffOpTest.php
@@ -4,7 +4,6 @@
 
 use Drupal\Component\Diff\Engine\DiffOp;
 use PHPUnit\Framework\TestCase;
-use PHPUnit\Framework\Error\Error;
 
 /**
  * Test DiffOp base class.
@@ -25,7 +24,7 @@ class DiffOpTest extends TestCase {
    * @covers ::reverse
    */
   public function testReverse() {
-    $this->expectException(Error::class);
+    $this->expectError();
     $op = new DiffOp();
     $result = $op->reverse();
   }
diff --git a/core/tests/Drupal/Tests/Component/Gettext/PoStreamWriterTest.php b/core/tests/Drupal/Tests/Component/Gettext/PoStreamWriterTest.php
index 39cf95d18a..b9e007ff10 100644
--- a/core/tests/Drupal/Tests/Component/Gettext/PoStreamWriterTest.php
+++ b/core/tests/Drupal/Tests/Component/Gettext/PoStreamWriterTest.php
@@ -4,6 +4,7 @@
 
 use Drupal\Component\Gettext\PoItem;
 use Drupal\Component\Gettext\PoStreamWriter;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use org\bovigo\vfs\vfsStream;
 use org\bovigo\vfs\vfsStreamFile;
 use PHPUnit\Framework\TestCase;
@@ -14,6 +15,8 @@
  */
 class PoStreamWriterTest extends TestCase {
 
+  use PhpUnitWarningsCompatibilityTrait;
+
   /**
    * The PO writer object under test.
    *
diff --git a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php
index 6230bdbd7b..4ac9559ab7 100644
--- a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php
+++ b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php
@@ -4,8 +4,8 @@
 
 use Drupal\Component\PhpStorage\FileStorage;
 use Drupal\Component\Utility\Random;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use org\bovigo\vfs\vfsStreamDirectory;
-use PHPUnit\Framework\Error\Warning;
 
 /**
  * @coversDefaultClass \Drupal\Component\PhpStorage\FileStorage
@@ -14,6 +14,8 @@
  */
 class FileStorageTest extends PhpStorageTestBase {
 
+  use PhpUnitWarningsCompatibilityTrait;
+
   /**
    * Standard test settings to pass to storage instances.
    *
@@ -99,8 +101,8 @@ public function testCreateDirectoryFailWarning() {
       'bin' => 'test',
     ]);
     $code = "<?php\n echo 'here';";
-    $this->expectException(Warning::class);
-    $this->expectExceptionMessage('mkdir(): Permission Denied');
+    $this->expectWarning();
+    $this->expectWarningMessage('mkdir(): Permission Denied');
     $storage->save('subdirectory/foo.php', $code);
   }
 
diff --git a/core/tests/Drupal/Tests/Component/Plugin/PluginManagerBaseTest.php b/core/tests/Drupal/Tests/Component/Plugin/PluginManagerBaseTest.php
index 954ff70110..64586b2988 100644
--- a/core/tests/Drupal/Tests/Component/Plugin/PluginManagerBaseTest.php
+++ b/core/tests/Drupal/Tests/Component/Plugin/PluginManagerBaseTest.php
@@ -5,6 +5,7 @@
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
 use Drupal\Component\Plugin\Mapper\MapperInterface;
 use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -13,6 +14,8 @@
  */
 class PluginManagerBaseTest extends TestCase {
 
+  use PhpUnitWarningsCompatibilityTrait;
+
   /**
    * A callback method for mocking FactoryInterface objects.
    */
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/AssertUtilsTrait.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/AssertUtilsTrait.php
index 96dab84912..6119fe2ae3 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/AssertUtilsTrait.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/AssertUtilsTrait.php
@@ -2,13 +2,13 @@
 
 namespace Drupal\Tests\Composer\Plugin\Scaffold;
 
-use Drupal\Tests\Traits\PHPUnit8Warnings;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 
 /**
  * Convenience class for creating fixtures.
  */
 trait AssertUtilsTrait {
-  use PHPUnit8Warnings;
+  use PhpUnitWarningsCompatibilityTrait;
 
   /**
    * Asserts that a given file exists and is/is not a symlink.
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php
index e37d06f1f5..f358a815e4 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php
@@ -5,7 +5,7 @@
 use Drupal\Composer\Plugin\Scaffold\Operations\AppendOp;
 use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions;
 use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures;
-use Drupal\Tests\Traits\PHPUnit8Warnings;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -14,7 +14,7 @@
  * @group Scaffold
  */
 class AppendOpTest extends TestCase {
-  use PHPUnit8Warnings;
+  use PhpUnitWarningsCompatibilityTrait;
 
   /**
    * @covers ::process
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ReplaceOpTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ReplaceOpTest.php
index cb5d6c7c4b..1df61ff75f 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ReplaceOpTest.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ReplaceOpTest.php
@@ -5,7 +5,7 @@
 use Drupal\Composer\Plugin\Scaffold\Operations\ReplaceOp;
 use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions;
 use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures;
-use Drupal\Tests\Traits\PHPUnit8Warnings;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -14,7 +14,7 @@
  * @group Scaffold
  */
 class ReplaceOpTest extends TestCase {
-  use PHPUnit8Warnings;
+  use PhpUnitWarningsCompatibilityTrait;
 
   /**
    * @covers ::process
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/SkipOpTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/SkipOpTest.php
index ba8313bbce..859f931d6c 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/SkipOpTest.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/SkipOpTest.php
@@ -5,7 +5,7 @@
 use Drupal\Composer\Plugin\Scaffold\Operations\SkipOp;
 use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions;
 use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures;
-use Drupal\Tests\Traits\PHPUnit8Warnings;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -14,7 +14,7 @@
  * @group Scaffold
  */
 class SkipOpTest extends TestCase {
-  use PHPUnit8Warnings;
+  use PhpUnitWarningsCompatibilityTrait;
 
   /**
    * @covers ::process
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/ConfigTest.php b/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/ConfigTest.php
index 9755278b51..670ddff03f 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/ConfigTest.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/ConfigTest.php
@@ -4,6 +4,7 @@
 
 use Composer\Package\RootPackageInterface;
 use Drupal\Composer\Plugin\VendorHardening\Config;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -12,6 +13,8 @@
  */
 class ConfigTest extends TestCase {
 
+  use PhpUnitWarningsCompatibilityTrait;
+
   /**
    * @covers ::getPathsForPackage
    */
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/VendorHardeningPluginTest.php b/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/VendorHardeningPluginTest.php
index 089b906718..4378c1bdf5 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/VendorHardeningPluginTest.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/VendorHardeningPluginTest.php
@@ -8,6 +8,7 @@
 use Composer\Package\RootPackageInterface;
 use Drupal\Composer\VendorHardening\Config;
 use Drupal\Composer\Plugin\VendorHardening\VendorHardeningPlugin;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use org\bovigo\vfs\vfsStream;
 use PHPUnit\Framework\TestCase;
 
@@ -17,6 +18,8 @@
  */
 class VendorHardeningPluginTest extends TestCase {
 
+  use PhpUnitWarningsCompatibilityTrait;
+
   public function setUp(): void {
     parent::setUp();
     vfsStream::setup('vendor', NULL, [
diff --git a/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php
index 7e5fe84836..40efadf4ea 100644
--- a/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php
@@ -113,8 +113,8 @@ public function testGrouper() {
     $this->assertSame('all', $group['media']);
     $this->assertTrue($group['preprocess']);
     $this->assertCount(3, $group['items']);
-    $this->assertContains($css_assets['system.base.css'], $group['items']);
-    $this->assertContains($css_assets['js.module.css'], $group['items']);
+    $this->assertContainsEquals($css_assets['system.base.css'], $group['items']);
+    $this->assertContainsEquals($css_assets['js.module.css'], $group['items']);
 
     // Check group 2.
     $group = $groups[1];
@@ -123,7 +123,7 @@ public function testGrouper() {
     $this->assertSame('all', $group['media']);
     $this->assertTrue($group['preprocess']);
     $this->assertCount(1, $group['items']);
-    $this->assertContains($css_assets['field.css'], $group['items']);
+    $this->assertContainsEquals($css_assets['field.css'], $group['items']);
 
     // Check group 3.
     $group = $groups[2];
@@ -132,7 +132,7 @@ public function testGrouper() {
     $this->assertSame('all', $group['media']);
     $this->assertTrue($group['preprocess']);
     $this->assertCount(1, $group['items']);
-    $this->assertContains($css_assets['external.css'], $group['items']);
+    $this->assertContainsEquals($css_assets['external.css'], $group['items']);
 
     // Check group 4.
     $group = $groups[3];
@@ -141,7 +141,7 @@ public function testGrouper() {
     $this->assertSame('all', $group['media']);
     $this->assertTrue($group['preprocess']);
     $this->assertCount(1, $group['items']);
-    $this->assertContains($css_assets['elements.css'], $group['items']);
+    $this->assertContainsEquals($css_assets['elements.css'], $group['items']);
 
     // Check group 5.
     $group = $groups[4];
@@ -150,7 +150,7 @@ public function testGrouper() {
     $this->assertSame('print', $group['media']);
     $this->assertTrue($group['preprocess']);
     $this->assertCount(1, $group['items']);
-    $this->assertContains($css_assets['print.css'], $group['items']);
+    $this->assertContainsEquals($css_assets['print.css'], $group['items']);
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php
index e4bed4e3a4..dcb3b0f196 100644
--- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php
+++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php
@@ -103,13 +103,13 @@ public function testGetLibraryByName() {
    * Tests getting a deprecated library.
    */
   public function testAssetLibraryDeprecation() {
-    $previous_error_handler = set_error_handler(function ($severity, $message, $file, $line, $context) use (&$previous_error_handler) {
+    $previous_error_handler = set_error_handler(function ($severity, $message, $file, $line) use (&$previous_error_handler) {
       // Convert deprecation error into a catchable exception.
       if ($severity === E_USER_DEPRECATED) {
         throw new \ErrorException($message, 0, $severity, $file, $line);
       }
       if ($previous_error_handler) {
-        return $previous_error_handler($severity, $message, $file, $line, $context);
+        return $previous_error_handler($severity, $message, $file, $line);
       }
     });
 
diff --git a/core/tests/Drupal/Tests/Core/Config/ConfigTest.php b/core/tests/Drupal/Tests/Core/Config/ConfigTest.php
index ac09beb7a6..0ab90fe518 100644
--- a/core/tests/Drupal/Tests/Core/Config/ConfigTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/ConfigTest.php
@@ -270,7 +270,7 @@ public function testSetIllegalOffsetValue() {
     $this->config->set('testData', 1);
 
     // Attempt to treat the single value as a nested item.
-    $this->expectException(Warning::class);
+    $this->expectException(PHP_VERSION_ID >= 80000 ? \Error::class : Warning::class);
     $this->config->set('testData.illegalOffset', 1);
   }
 
diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
index b571e94d8b..cf93e04baf 100644
--- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -199,6 +199,8 @@ public static function getSkippedDeprecations() {
       'AssertLegacyTrait::assertPattern() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseMatches() instead. See https://www.drupal.org/node/3129738',
       'AssertLegacyTrait::buildXPathQuery() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->buildXPathQuery() instead. See https://www.drupal.org/node/3129738',
       'AssertLegacyTrait::constructFieldXpath() is deprecated in drupal:8.5.0 and is removed from drupal:10.0.0. Use $this->getSession()->getPage()->findField() instead. See https://www.drupal.org/node/3129738',
+      // PHPUnit 9.
+      "The \"PHPUnit\TextUI\DefaultResultPrinter\" class is considered internal This class is not covered by the backward compatibility promise for PHPUnit. It may change without further notice. You should not use it from \"Drupal\Tests\Listeners\HtmlOutputPrinter\".",
     ];
   }
 
diff --git a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php
index a3f7916e98..a85c320499 100644
--- a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php
+++ b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php
@@ -2,15 +2,27 @@
 
 namespace Drupal\Tests\Listeners;
 
+use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion;
 use PHPUnit\Framework\TestResult;
-use PHPUnit\TextUI\ResultPrinter;
+
+// In order to manage different implementations across PHPUnit versions, we
+// dynamically load the base ResultPrinter class dependent on the PHPUnit runner
+// version.
+if (!class_exists(ResultPrinterBase::class, FALSE)) {
+  if (RunnerVersion::getMajor() < 9) {
+    class_alias('PHPUnit\TextUI\ResultPrinter', ResultPrinterBase::class);
+  }
+  else {
+    class_alias('PHPUnit\TextUI\DefaultResultPrinter', ResultPrinterBase::class);
+  }
+}
 
 /**
  * Defines a class for providing html output results for functional tests.
  *
  * @internal
  */
-class HtmlOutputPrinter extends ResultPrinter {
+class HtmlOutputPrinter extends ResultPrinterBase {
 
   use HtmlOutputPrinterTrait;
 
diff --git a/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php b/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php
deleted file mode 100644
index 5b80264681..0000000000
--- a/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-namespace Drupal\Tests\Traits;
-
-/**
- * Used to ignore warnings being added by PHPUnit 8.
- *
- * This trait exists to allow Drupal 8 tests using PHPUnit 7 and Drupal 9 tests
- * using PHPUnit 8 to happily co-exist. Once Drupal 8 and Drupal 9 are not so
- * closely aligned these will be fixed in core and the warnings will be emitted
- * from the test runner.
- *
- * @todo https://www.drupal.org/project/drupal/issues/3110543 Remove the ignored
- *   warnings to support PHPUnit 9.
- *
- * @internal
- */
-trait PHPUnit8Warnings {
-
-  /**
-   * The list of warnings to ignore.
-   *
-   * @var string[]
-   */
-  private static $ignoredWarnings = [
-    'expectExceptionMessageRegExp() is deprecated in PHPUnit 8 and will be removed in PHPUnit 9.',
-  ];
-
-  /**
-   * Ignores specific PHPUnit 8 warnings.
-   *
-   * @see \PHPUnit\Framework\TestCase::addWarning()
-   *
-   * @internal
-   */
-  public function addWarning(string $warning): void {
-    if (in_array($warning, self::$ignoredWarnings, TRUE)) {
-      return;
-    }
-    parent::addWarning($warning);
-  }
-
-}
diff --git a/core/tests/Drupal/Tests/Traits/PhpUnitWarningsCompatibilityTrait.php b/core/tests/Drupal/Tests/Traits/PhpUnitWarningsCompatibilityTrait.php
new file mode 100644
index 0000000000..7babd00bbe
--- /dev/null
+++ b/core/tests/Drupal/Tests/Traits/PhpUnitWarningsCompatibilityTrait.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Drupal\Tests\Traits;
+
+/**
+ * Used to ignore warnings being added by PHPUnit.
+ *
+ * This trait exists to allow Drupal tests using different PHPUnit versions to
+ * happily co-exist.
+ *
+ * @internal
+ */
+trait PhpUnitWarningsCompatibilityTrait {
+
+  /**
+   * The list of warnings to ignore.
+   *
+   * @var string[]
+   */
+  private static $ignoredWarnings = [
+    // PHPUnit 9.
+    'assertFileNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileDoesNotExist() instead.',
+    'PHPUnit\Framework\TestCase::prophesize() is deprecated and will be removed in PHPUnit 10. Please use the trait provided by phpspec/prophecy-phpunit.',
+    'assertRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertMatchesRegularExpression() instead.',
+    'assertNotRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDoesNotMatchRegularExpression() instead.',
+    'assertDirectoryNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryDoesNotExist() instead.',
+    'Support for using expectException() with PHPUnit\Framework\Error\Warning is deprecated and will be removed in PHPUnit 10. Use expectWarning() instead.',
+    'Support for using expectException() with PHPUnit\Framework\Error\Error is deprecated and will be removed in PHPUnit 10. Use expectError() instead.',
+    'assertDirectoryNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotWritable() instead.',
+    'assertFileNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotWritable() instead.',
+  ];
+
+  /**
+   * Overrides ::addWarning to selectively ignore specific PHPUnit warnings.
+   *
+   * @see \PHPUnit\Framework\TestCase::addWarning()
+   *
+   * @internal
+   */
+  public function addWarning(string $warning): void {
+    if (in_array($warning, self::$ignoredWarnings, TRUE)) {
+      return;
+    }
+    parent::addWarning($warning);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php
index 2c2257b2da..a893142d47 100644
--- a/core/tests/Drupal/Tests/UnitTestCase.php
+++ b/core/tests/Drupal/Tests/UnitTestCase.php
@@ -9,7 +9,7 @@
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
-use Drupal\Tests\Traits\PHPUnit8Warnings;
+use Drupal\Tests\Traits\PhpUnitWarningsCompatibilityTrait;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -19,7 +19,7 @@
  */
 abstract class UnitTestCase extends TestCase {
 
-  use PHPUnit8Warnings;
+  use PhpUnitWarningsCompatibilityTrait;
 
   /**
    * The random generator.
