From a0a22f8d41d7f5926378bcef00b5bb9256ba5fc2 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Mon, 2 Aug 2021 09:24:49 +0100
Subject: [PATCH] Issue #3180351 by longwave, alexpott, daffie, catch,
 andypost, Charlie ChX Negyesi: doctrine/reflection is abandoned

---
 .../Compatibility/Php7/ReflectionClass.php    |  48 ++
 .../Compatibility/Php8/ReflectionClass.php    |  48 ++
 .../Compatibility/ReflectionClass.php         |  42 ++
 .../Doctrine/StaticReflectionClass.php        | 424 ++++++++++++++++++
 .../Doctrine/StaticReflectionParser.php       | 344 ++++++++++++++
 .../Discovery/AnnotatedClassDiscovery.php     |   2 +-
 .../Annotation/Reflection/MockFileFinder.php  |   9 +-
 .../Component/ClassFinder/ClassFinder.php     |   4 +-
 .../ClassFinder/ClassFinderInterface.php      |  21 +
 .../Component/ClassFinder/composer.json       |   3 +-
 core/lib/Drupal/Core/Test/TestDiscovery.php   |   2 +-
 ...otatedClassDiscoveryAutomatedProviders.php |   4 +-
 .../Discovery/StaticReflectionParser.php      |   6 +-
 13 files changed, 941 insertions(+), 16 deletions(-)
 create mode 100644 core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/Php7/ReflectionClass.php
 create mode 100644 core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/Php8/ReflectionClass.php
 create mode 100644 core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/ReflectionClass.php
 create mode 100644 core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionClass.php
 create mode 100644 core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionParser.php
 create mode 100644 core/lib/Drupal/Component/ClassFinder/ClassFinderInterface.php

diff --git a/core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/Php7/ReflectionClass.php b/core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/Php7/ReflectionClass.php
new file mode 100644
index 000000000000..8ae2a6750458
--- /dev/null
+++ b/core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/Php7/ReflectionClass.php
@@ -0,0 +1,48 @@
+<?php
+// phpcs:ignoreFile
+
+/**
+ * @file
+ *
+ * This class is a near-copy of
+ * Doctrine\Common\Reflection\Compatibility\Php7\ReflectionClass, which is part
+ * of the Doctrine project: <http://www.doctrine-project.org>. It was copied
+ * from version 1.2.2.
+ *
+ * Original copyright:
+ *
+ * Copyright (c) 2006-2015 Doctrine Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ */
+
+namespace Drupal\Component\Annotation\Doctrine\Compatibility\Php7;
+
+use ReflectionException;
+
+trait ReflectionClass
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function getConstants()
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function newInstance($args)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+}
diff --git a/core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/Php8/ReflectionClass.php b/core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/Php8/ReflectionClass.php
new file mode 100644
index 000000000000..015939e147c9
--- /dev/null
+++ b/core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/Php8/ReflectionClass.php
@@ -0,0 +1,48 @@
+<?php
+// phpcs:ignoreFile
+
+/**
+ * @file
+ *
+ * This class is a near-copy of
+ * Doctrine\Common\Reflection\Compatibility\Php8\ReflectionClass, which is part
+ * of the Doctrine project: <http://www.doctrine-project.org>. It was copied
+ * from version 1.2.2.
+ *
+ * Original copyright:
+ *
+ * Copyright (c) 2006-2015 Doctrine Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ */
+
+namespace Drupal\Component\Annotation\Doctrine\Compatibility\Php8;
+
+use ReflectionException;
+
+trait ReflectionClass
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function getConstants(?int $filter = null)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function newInstance(mixed ...$args)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+}
diff --git a/core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/ReflectionClass.php b/core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/ReflectionClass.php
new file mode 100644
index 000000000000..acb5887a72b0
--- /dev/null
+++ b/core/lib/Drupal/Component/Annotation/Doctrine/Compatibility/ReflectionClass.php
@@ -0,0 +1,42 @@
+<?php
+// phpcs:ignoreFile
+
+/**
+ * @file
+ *
+ * This class is a near-copy of
+ * Doctrine\Common\Reflection\Compatibility\ReflectionClass, which is part of
+ * the Doctrine project: <http://www.doctrine-project.org>. It was copied from
+ * version 1.2.2.
+ *
+ * Original copyright:
+ *
+ * Copyright (c) 2006-2015 Doctrine Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ */
+
+namespace Drupal\Component\Annotation\Doctrine\Compatibility;
+
+use const PHP_VERSION_ID;
+use function class_alias;
+
+if (PHP_VERSION_ID >= 80000) {
+    class_alias('Drupal\Component\Annotation\Doctrine\Compatibility\Php8\ReflectionClass', 'Drupal\Component\Annotation\Doctrine\Compatibility\ReflectionClass');
+} else {
+    class_alias('Drupal\Component\Annotation\Doctrine\Compatibility\Php7\ReflectionClass', 'Drupal\Component\Annotation\Doctrine\Compatibility\ReflectionClass');
+}
+
+if (false) {
+    class ReflectionClass
+    {
+    }
+}
diff --git a/core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionClass.php b/core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionClass.php
new file mode 100644
index 000000000000..0f1ff5888d76
--- /dev/null
+++ b/core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionClass.php
@@ -0,0 +1,424 @@
+<?php
+// phpcs:ignoreFile
+
+/**
+ * @file
+ *
+ * This class is a near-copy of
+ * Doctrine\Common\Reflection\StaticReflectionClass, which is part of the
+ * Doctrine project: <http://www.doctrine-project.org>. It was copied from
+ * version 1.2.2.
+ *
+ * Original copyright:
+ *
+ * Copyright (c) 2006-2015 Doctrine Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ */
+
+namespace Drupal\Component\Annotation\Doctrine;
+
+use Drupal\Component\Annotation\Doctrine\Compatibility\ReflectionClass as CompatibilityReflectionClass;
+use ReflectionClass;
+use ReflectionException;
+
+class StaticReflectionClass extends ReflectionClass
+{
+    use CompatibilityReflectionClass;
+
+    /**
+     * 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)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getProperty($name)
+    {
+        throw new ReflectionException('Method not implemented');
+    }
+
+    /**
+     * {@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 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 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/Component/Annotation/Doctrine/StaticReflectionParser.php b/core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionParser.php
new file mode 100644
index 000000000000..78fdbebe585c
--- /dev/null
+++ b/core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionParser.php
@@ -0,0 +1,344 @@
+<?php
+// phpcs:ignoreFile
+// cspell:ignore paamayim nekudotayim
+
+/**
+ * @file
+ *
+ * This class is a near-copy of
+ * Doctrine\Common\Reflection\StaticReflectionParser, which is part of the
+ * Doctrine project: <http://www.doctrine-project.org>. It was copied from
+ * version 1.2.2.
+ *
+ * Original copyright:
+ *
+ * Copyright (c) 2006-2015 Doctrine Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ */
+
+namespace Drupal\Component\Annotation\Doctrine;
+
+use Doctrine\Common\Annotations\TokenParser;
+use ReflectionException;
+use const T_CLASS;
+use const T_DOC_COMMENT;
+use const T_EXTENDS;
+use const T_FUNCTION;
+use const T_NEW;
+use const T_PAAMAYIM_NEKUDOTAYIM;
+use const T_PRIVATE;
+use const T_PROTECTED;
+use const T_PUBLIC;
+use const T_STRING;
+use const T_USE;
+use const T_VAR;
+use const T_VARIABLE;
+use function array_merge;
+use function file_get_contents;
+use function is_array;
+use function ltrim;
+use function preg_match;
+use function sprintf;
+use function strpos;
+use function strrpos;
+use function strtolower;
+use function substr;
+
+/**
+ * Parses a file for namespaces/use/class declarations.
+ */
+class StaticReflectionParser
+{
+    /**
+     * The fully qualified class name.
+     *
+     * @var string
+     */
+    protected $className;
+
+    /**
+     * The short class name.
+     *
+     * @var string
+     */
+    protected $shortClassName;
+
+    /**
+     * Whether the caller only wants class annotations.
+     *
+     * @var bool
+     */
+    protected $classAnnotationOptimize;
+
+    /**
+     * A ClassFinder object which finds the class.
+     *
+     * @var ClassFinderInterface
+     */
+    protected $finder;
+
+    /**
+     * Whether the parser has run.
+     *
+     * @var bool
+     */
+    protected $parsed = false;
+
+    /**
+     * The namespace of the class.
+     *
+     * @var string
+     */
+    protected $namespace = '';
+
+    /**
+     * The use statements of the class.
+     *
+     * @var string[]
+     */
+    protected $useStatements = [];
+
+    /**
+     * The docComment of the class.
+     *
+     * @var mixed[]
+     */
+    protected $docComment = [
+        'class' => '',
+        'property' => [],
+        'method' => [],
+    ];
+
+    /**
+     * The name of the class this class extends, if any.
+     *
+     * @var string
+     */
+    protected $parentClassName = '';
+
+    /**
+     * The parent PSR-0 Parser.
+     *
+     * @var \Doctrine\Common\Reflection\StaticReflectionParser
+     */
+    protected $parentStaticReflectionParser;
+
+    /**
+     * Parses a class residing in a PSR-0 hierarchy.
+     *
+     * @param string               $className               The full, namespaced class name.
+     * @param ClassFinderInterface $finder                  A ClassFinder object which finds the class.
+     * @param bool                 $classAnnotationOptimize Only retrieve the class docComment.
+     *                                                         Presumes there is only one statement per line.
+     */
+    public function __construct($className, $finder, $classAnnotationOptimize = false)
+    {
+        $this->className = ltrim($className, '\\');
+        $lastNsPos       = strrpos($this->className, '\\');
+
+        if ($lastNsPos !== false) {
+            $this->namespace      = substr($this->className, 0, $lastNsPos);
+            $this->shortClassName = substr($this->className, $lastNsPos + 1);
+        } else {
+            $this->shortClassName = $this->className;
+        }
+
+        $this->finder                  = $finder;
+        $this->classAnnotationOptimize = $classAnnotationOptimize;
+    }
+
+    /**
+     * @return void
+     */
+    protected function parse()
+    {
+        $fileName = $this->finder->findFile($this->className);
+
+        if ($this->parsed || ! $fileName) {
+            return;
+        }
+        $this->parsed = true;
+        $contents     = file_get_contents($fileName);
+        if ($this->classAnnotationOptimize) {
+            $regex = sprintf('/\A.*^\s*((abstract|final)\s+)?class\s+%s\s+/sm', $this->shortClassName);
+
+            if (preg_match($regex, $contents, $matches)) {
+                $contents = $matches[0];
+            }
+        }
+        $tokenParser = new TokenParser($contents);
+        $docComment  = '';
+        $last_token  = false;
+
+        while ($token = $tokenParser->next(false)) {
+            switch ($token[0]) {
+                case T_USE:
+                    $this->useStatements = array_merge($this->useStatements, $tokenParser->parseUseStatement());
+                    break;
+                case T_DOC_COMMENT:
+                    $docComment = $token[1];
+                    break;
+                case T_CLASS:
+                    if ($last_token !== T_PAAMAYIM_NEKUDOTAYIM && $last_token !== T_NEW) {
+                        $this->docComment['class'] = $docComment;
+                        $docComment                = '';
+                    }
+                    break;
+                case T_VAR:
+                case T_PRIVATE:
+                case T_PROTECTED:
+                case T_PUBLIC:
+                    $token = $tokenParser->next();
+                    if ($token[0] === T_VARIABLE) {
+                        $propertyName                                = substr($token[1], 1);
+                        $this->docComment['property'][$propertyName] = $docComment;
+                        continue 2;
+                    }
+                    if ($token[0] !== T_FUNCTION) {
+                        // For example, it can be T_FINAL.
+                        continue 2;
+                    }
+                    // No break.
+                case T_FUNCTION:
+                    // The next string after function is the name, but
+                    // there can be & before the function name so find the
+                    // string.
+                    while (($token = $tokenParser->next()) && $token[0] !== T_STRING) {
+                        continue;
+                    }
+                    if ($token === null) {
+                        break;
+                    }
+                    $methodName                              = $token[1];
+                    $this->docComment['method'][$methodName] = $docComment;
+                    $docComment                              = '';
+                    break;
+                case T_EXTENDS:
+                    $this->parentClassName = $tokenParser->parseClass();
+                    $nsPos                 = strpos($this->parentClassName, '\\');
+                    $fullySpecified        = false;
+                    if ($nsPos === 0) {
+                        $fullySpecified = true;
+                    } else {
+                        if ($nsPos) {
+                            $prefix  = strtolower(substr($this->parentClassName, 0, $nsPos));
+                            $postfix = substr($this->parentClassName, $nsPos);
+                        } else {
+                            $prefix  = strtolower($this->parentClassName);
+                            $postfix = '';
+                        }
+                        foreach ($this->useStatements as $alias => $use) {
+                            if ($alias !== $prefix) {
+                                continue;
+                            }
+
+                            $this->parentClassName = '\\' . $use . $postfix;
+                            $fullySpecified        = true;
+                        }
+                    }
+                    if (! $fullySpecified) {
+                        $this->parentClassName = '\\' . $this->namespace . '\\' . $this->parentClassName;
+                    }
+                    break;
+            }
+
+            $last_token = is_array($token) ? $token[0] : false;
+        }
+    }
+
+    /**
+     * @return StaticReflectionParser
+     */
+    protected function getParentStaticReflectionParser()
+    {
+        if (empty($this->parentStaticReflectionParser)) {
+            $this->parentStaticReflectionParser = new static($this->parentClassName, $this->finder);
+        }
+
+        return $this->parentStaticReflectionParser;
+    }
+
+    /**
+     * @return string
+     */
+    public function getClassName()
+    {
+        return $this->className;
+    }
+
+    /**
+     * @return string
+     */
+    public function getNamespaceName()
+    {
+        return $this->namespace;
+    }
+
+    /**
+     * Gets the ReflectionClass equivalent for this class.
+     *
+     * @return ReflectionClass
+     */
+    public function getReflectionClass()
+    {
+        return new StaticReflectionClass($this);
+    }
+
+    /**
+     * Gets the use statements from this file.
+     *
+     * @return string[]
+     */
+    public function getUseStatements()
+    {
+        $this->parse();
+
+        return $this->useStatements;
+    }
+
+    /**
+     * Gets the doc comment.
+     *
+     * @param string $type The type: 'class', 'property' or 'method'.
+     * @param string $name The name of the property or method, not needed for 'class'.
+     *
+     * @return string The doc comment, empty string if none.
+     */
+    public function getDocComment($type = 'class', $name = '')
+    {
+        $this->parse();
+
+        return $name ? $this->docComment[$type][$name] : $this->docComment[$type];
+    }
+
+    /**
+     * Gets the PSR-0 parser for the declaring class.
+     *
+     * @param string $type The type: 'property' or 'method'.
+     * @param string $name The name of the property or method.
+     *
+     * @return StaticReflectionParser A static reflection parser for the declaring class.
+     *
+     * @throws ReflectionException
+     */
+    public function getStaticReflectionParserForDeclaringClass($type, $name)
+    {
+        $this->parse();
+        if (isset($this->docComment[$type][$name])) {
+            return $this;
+        }
+        if (! empty($this->parentClassName)) {
+            return $this->getParentStaticReflectionParser()->getStaticReflectionParserForDeclaringClass($type, $name);
+        }
+        throw new ReflectionException('Invalid ' . $type . ' "' . $name . '"');
+    }
+}
diff --git a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
index 0c3881047c47..2c5f0b4985d0 100644
--- a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -6,9 +6,9 @@
 use Drupal\Component\FileCache\FileCacheFactory;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Annotation\Doctrine\SimpleAnnotationReader;
+use Drupal\Component\Annotation\Doctrine\StaticReflectionParser;
 use Drupal\Component\Annotation\Reflection\MockFileFinder;
 use Doctrine\Common\Annotations\AnnotationRegistry;
-use Doctrine\Common\Reflection\StaticReflectionParser;
 use Drupal\Component\Plugin\Discovery\DiscoveryTrait;
 use Drupal\Component\Utility\Crypt;
 
diff --git a/core/lib/Drupal/Component/Annotation/Reflection/MockFileFinder.php b/core/lib/Drupal/Component/Annotation/Reflection/MockFileFinder.php
index ecfefc371e74..7aed79f14fe3 100644
--- a/core/lib/Drupal/Component/Annotation/Reflection/MockFileFinder.php
+++ b/core/lib/Drupal/Component/Annotation/Reflection/MockFileFinder.php
@@ -2,14 +2,15 @@
 
 namespace Drupal\Component\Annotation\Reflection;
 
-use Doctrine\Common\Reflection\ClassFinderInterface;
+use Drupal\Component\ClassFinder\ClassFinderInterface;
 
 /**
  * Defines a mock file finder that only returns a single filename.
  *
- * This can be used with Doctrine\Common\Reflection\StaticReflectionParser if
- * the filename is known and inheritance is not a concern (for example, if
- * only the class annotation is needed).
+ * This can be used with
+ * Drupal\Component\Annotation\Doctrine\StaticReflectionParser if the filename
+ * is known and inheritance is not a concern (for example, if only the class
+ * annotation is needed).
  */
 class MockFileFinder implements ClassFinderInterface {
 
diff --git a/core/lib/Drupal/Component/ClassFinder/ClassFinder.php b/core/lib/Drupal/Component/ClassFinder/ClassFinder.php
index 346f7f66cd84..3d89e9b4daa2 100644
--- a/core/lib/Drupal/Component/ClassFinder/ClassFinder.php
+++ b/core/lib/Drupal/Component/ClassFinder/ClassFinder.php
@@ -2,8 +2,6 @@
 
 namespace Drupal\Component\ClassFinder;
 
-use Doctrine\Common\Reflection\ClassFinderInterface;
-
 /**
  * A Utility class that uses active autoloaders to find a file for a class.
  */
@@ -19,7 +17,7 @@ public function findFile($class) {
         $file = call_user_func_array([$loader[0], 'findFile'], [$class]);
         // Different implementations return different empty values. For example,
         // \Composer\Autoload\ClassLoader::findFile() returns FALSE whilst
-        // \Doctrine\Common\Reflection\ClassFinderInterface::findFile()
+        // \Drupal\Component\ClassFinder\ClassFinderInterface::findFile()
         // documents that a NULL should be returned.
         if (!empty($file)) {
           return $file;
diff --git a/core/lib/Drupal/Component/ClassFinder/ClassFinderInterface.php b/core/lib/Drupal/Component/ClassFinder/ClassFinderInterface.php
new file mode 100644
index 000000000000..7db1d1744ba2
--- /dev/null
+++ b/core/lib/Drupal/Component/ClassFinder/ClassFinderInterface.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\Component\ClassFinder;
+
+/**
+ * Finds a class in a PSR-0 structure.
+ */
+interface ClassFinderInterface {
+
+  /**
+   * Finds a class.
+   *
+   * @param string $class
+   *   The name of the class.
+   *
+   * @return string|null
+   *   The name of the class or NULL if not found.
+   */
+  public function findFile($class);
+
+}
diff --git a/core/lib/Drupal/Component/ClassFinder/composer.json b/core/lib/Drupal/Component/ClassFinder/composer.json
index ac17b5f999b0..9efde0acdda7 100644
--- a/core/lib/Drupal/Component/ClassFinder/composer.json
+++ b/core/lib/Drupal/Component/ClassFinder/composer.json
@@ -5,8 +5,7 @@
   "homepage": "https://www.drupal.org/project/drupal",
   "license": "GPL-2.0-or-later",
   "require": {
-    "php": ">=7.3.0",
-    "doctrine/reflection": "^1.1"
+    "php": ">=7.3.0"
   },
   "autoload": {
     "psr-4": {
diff --git a/core/lib/Drupal/Core/Test/TestDiscovery.php b/core/lib/Drupal/Core/Test/TestDiscovery.php
index b9eeb0db23f0..cdcc1eeda7f8 100644
--- a/core/lib/Drupal/Core/Test/TestDiscovery.php
+++ b/core/lib/Drupal/Core/Test/TestDiscovery.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\Core\Test;
 
-use Doctrine\Common\Reflection\StaticReflectionParser;
+use Drupal\Component\Annotation\Doctrine\StaticReflectionParser;
 use Drupal\Component\Annotation\Reflection\MockFileFinder;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Extension\ExtensionDiscovery;
diff --git a/core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php b/core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php
index 0d6c0704532c..49c9a268ab84 100644
--- a/core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php
+++ b/core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php
@@ -3,8 +3,8 @@
 namespace Drupal\migrate\Plugin\Discovery;
 
 use Doctrine\Common\Annotations\AnnotationRegistry;
-use Doctrine\Common\Reflection\StaticReflectionParser as BaseStaticReflectionParser;
 use Drupal\Component\Annotation\AnnotationInterface;
+use Drupal\Component\Annotation\Doctrine\StaticReflectionParser as BaseStaticReflectionParser;
 use Drupal\Component\Annotation\Reflection\MockFileFinder;
 use Drupal\Component\ClassFinder\ClassFinder;
 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
@@ -23,7 +23,7 @@ class AnnotatedClassDiscoveryAutomatedProviders extends AnnotatedClassDiscovery
   /**
    * A utility object that can use active autoloaders to find files for classes.
    *
-   * @var \Doctrine\Common\Reflection\ClassFinderInterface
+   * @var \Drupal\Component\ClassFinder\ClassFinderInterface
    */
   protected $finder;
 
diff --git a/core/modules/migrate/src/Plugin/Discovery/StaticReflectionParser.php b/core/modules/migrate/src/Plugin/Discovery/StaticReflectionParser.php
index 8c574f507025..a26a2c1e422a 100644
--- a/core/modules/migrate/src/Plugin/Discovery/StaticReflectionParser.php
+++ b/core/modules/migrate/src/Plugin/Discovery/StaticReflectionParser.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\migrate\Plugin\Discovery;
 
-use Doctrine\Common\Reflection\StaticReflectionParser as BaseStaticReflectionParser;
+use Drupal\Component\Annotation\Doctrine\StaticReflectionParser as BaseStaticReflectionParser;
 
 /**
  * Allows getting the reflection parser for the parent class.
@@ -17,11 +17,11 @@ class StaticReflectionParser extends BaseStaticReflectionParser {
   /**
    * If the current class extends another, get the parser for the latter.
    *
-   * @param \Doctrine\Common\Reflection\StaticReflectionParser $parser
+   * @param \Drupal\Component\Annotation\Doctrine\StaticReflectionParser $parser
    *   The current static parser.
    * @param $finder
    *   The class finder. Must implement
-   *   \Doctrine\Common\Reflection\ClassFinderInterface, but can do so
+   *   \Drupal\Component\ClassFinder\ClassFinderInterface, but can do so
    *   implicitly (i.e., implements the interface's methods but not the actual
    *   interface).
    *
-- 
GitLab