From 8901c2927fe606d684a3770a178815608491db8a Mon Sep 17 00:00:00 2001 From: Dennis Riehle Date: Fri, 8 Oct 2021 12:32:44 +0200 Subject: [PATCH 01/25] updated .doctrine-project.json Signed-off-by: Dennis Riehle --- .doctrine-project.json | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/.doctrine-project.json b/.doctrine-project.json index 38553aa66..d62df10e9 100644 --- a/.doctrine-project.json +++ b/.doctrine-project.json @@ -5,15 +5,15 @@ "docsSlug": "doctrine-annotations", "versions": [ { - "name": "1.11", - "branchName": "master", + "name": "1.14", + "branchName": "1.14.x", "slug": "latest", "upcoming": true }, { - "name": "1.10", - "branchName": "1.10.x", - "slug": "1.10", + "name": "1.13", + "branchName": "1.13.x", + "slug": "1.13", "aliases": [ "current", "stable" @@ -21,6 +21,24 @@ "current": true, "maintained": true }, + { + "name": "1.12", + "branchName": "1.12.x", + "slug": "1.12", + "maintained": false + }, + { + "name": "1.11", + "branchName": "1.11.x", + "slug": "1.11", + "maintained": false + }, + { + "name": "1.10", + "branchName": "1.10.x", + "slug": "1.10", + "maintained": false + }, { "name": "1.9", "branchName": "1.9.x", From a1b343daa92c52cb74424791b98a0f373c1dda5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Fri, 15 Oct 2021 19:42:58 +0200 Subject: [PATCH 02/25] Reuse workflows from upstream --- .github/workflows/coding-standards.yml | 27 +------ .github/workflows/continuous-integration.yml | 77 +------------------ .../workflows/release-on-milestone-closed.yml | 52 ++----------- 3 files changed, 11 insertions(+), 145 deletions(-) diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index f710db645..352847e3b 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -12,29 +12,4 @@ on: jobs: coding-standards: name: "Coding Standards" - runs-on: "ubuntu-20.04" - - strategy: - matrix: - php-version: - - "7.4" - - steps: - - name: "Checkout" - uses: "actions/checkout@v2" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - php-version: "${{ matrix.php-version }}" - tools: "cs2pr" - - - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v1" - with: - dependency-versions: "highest" - - # https://github.com/doctrine/.github/issues/3 - - name: "Run PHP_CodeSniffer" - run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr" + uses: "doctrine/.github/.github/workflows/coding-standards.yml@1.1.1" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index e4bdcf4b7..2720d0b2e 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -14,77 +14,6 @@ env: jobs: phpunit: name: "PHPUnit" - runs-on: "ubuntu-20.04" - - strategy: - matrix: - php-version: - - "7.1" - - "7.2" - - "7.3" - - "7.4" - - "8.0" - dependencies: - - "highest" - include: - - php-version: "7.2" - dependencies: "lowest" - - steps: - - name: "Checkout" - uses: "actions/checkout@v2" - with: - fetch-depth: 2 - - - name: "Install PHP with PCOV" - uses: "shivammathur/setup-php@v2" - if: "${{ matrix.php-version != '7.1' }}" - with: - php-version: "${{ matrix.php-version }}" - coverage: "pcov" - ini-values: "zend.assertions=1" - - - name: "Install PHP with XDebug" - uses: "shivammathur/setup-php@v2" - if: "${{ matrix.php-version == '7.1' }}" - with: - php-version: "${{ matrix.php-version }}" - coverage: "xdebug" - ini-values: "zend.assertions=1" - - - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v1" - with: - dependency-versions: "${{ matrix.dependencies }}" - composer-options: "--prefer-dist" - - - name: "Run PHPUnit" - run: "vendor/bin/phpunit --coverage-clover=coverage.xml" - - - name: "Upload coverage file" - uses: "actions/upload-artifact@v2" - with: - name: "phpunit-${{ matrix.php-version }}.coverage" - path: "coverage.xml" - - upload_coverage: - name: "Upload coverage to Codecov" - runs-on: "ubuntu-20.04" - needs: - - "phpunit" - - steps: - - name: "Checkout" - uses: "actions/checkout@v2" - with: - fetch-depth: 2 - - - name: "Download coverage files" - uses: "actions/download-artifact@v2" - with: - path: "reports" - - - name: "Upload to Codecov" - uses: "codecov/codecov-action@v1" - with: - directory: "reports" + uses: "doctrine/.github/.github/workflows/continuous-integration.yml@1.1.1" + with: + php-versions: '["7.1", "7.2", "7.3", "7.4", "8.0"]' diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml index 70ccbdb2d..563333bb4 100644 --- a/.github/workflows/release-on-milestone-closed.yml +++ b/.github/workflows/release-on-milestone-closed.yml @@ -8,48 +8,10 @@ on: jobs: release: name: "Git tag, release & create merge-up PR" - runs-on: "ubuntu-20.04" - - steps: - - name: "Checkout" - uses: "actions/checkout@v2" - - - name: "Release" - uses: "laminas/automatic-releases@v1" - with: - command-name: "laminas:automatic-releases:release" - env: - "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} - "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} - "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} - "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} - - - name: "Create Merge-Up Pull Request" - uses: "laminas/automatic-releases@v1" - with: - command-name: "laminas:automatic-releases:create-merge-up-pull-request" - env: - "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} - "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} - "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} - "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} - - - name: "Create and/or Switch to new Release Branch" - uses: "laminas/automatic-releases@v1" - with: - command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor" - env: - "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} - "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} - "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} - "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} - - - name: "Create new milestones" - uses: "laminas/automatic-releases@v1" - with: - command-name: "laminas:automatic-releases:create-milestones" - env: - "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} - "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} - "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} - "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@1.1.1" + secrets: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }} + GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }} + ORGANIZATION_ADMIN_TOKEN: ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} + SIGNING_SECRET_KEY: ${{ secrets.SIGNING_SECRET_KEY }} From 93e1ecf2047b9e33230e2f6fb04b24b735adffcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Fri, 15 Oct 2021 20:02:53 +0200 Subject: [PATCH 03/25] Setup Psalm --- .github/workflows/static-analysis.yml | 29 ++----------------- composer.json | 3 +- .../Common/Annotations/IndexedReader.php | 12 ++++---- psalm.xml | 15 ++++++++++ 4 files changed, 26 insertions(+), 33 deletions(-) create mode 100644 psalm.xml diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index d0e0d857d..ddcf2ddf7 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -10,29 +10,6 @@ on: - "*.x" jobs: - static-analysis-phpstan: - name: "Static Analysis with PHPStan" - runs-on: "ubuntu-20.04" - - strategy: - matrix: - php-version: - - "7.4" - - steps: - - name: "Checkout code" - uses: "actions/checkout@v2" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - php-version: "${{ matrix.php-version }}" - - - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v1" - with: - dependency-versions: "highest" - - - name: "Run a static analysis with phpstan/phpstan" - run: "vendor/bin/phpstan analyse" + static-analysis: + name: "Static Analysis" + uses: "doctrine/.github/.github/workflows/static-analysis.yml@1.1.1" diff --git a/composer.json b/composer.json index 00d023107..a29863a71 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "doctrine/coding-standard": "^6.0 || ^8.1", "phpstan/phpstan": "^0.12.20", "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", - "symfony/cache": "^4.4 || ^5.2" + "symfony/cache": "^4.4 || ^5.2", + "vimeo/psalm": "^4.10" }, "config": { "sort-packages": true diff --git a/lib/Doctrine/Common/Annotations/IndexedReader.php b/lib/Doctrine/Common/Annotations/IndexedReader.php index 42e70765d..62dcf7487 100644 --- a/lib/Doctrine/Common/Annotations/IndexedReader.php +++ b/lib/Doctrine/Common/Annotations/IndexedReader.php @@ -38,9 +38,9 @@ public function getClassAnnotations(ReflectionClass $class) /** * {@inheritDoc} */ - public function getClassAnnotation(ReflectionClass $class, $annotation) + public function getClassAnnotation(ReflectionClass $class, $annotationName) { - return $this->delegate->getClassAnnotation($class, $annotation); + return $this->delegate->getClassAnnotation($class, $annotationName); } /** @@ -59,9 +59,9 @@ public function getMethodAnnotations(ReflectionMethod $method) /** * {@inheritDoc} */ - public function getMethodAnnotation(ReflectionMethod $method, $annotation) + public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { - return $this->delegate->getMethodAnnotation($method, $annotation); + return $this->delegate->getMethodAnnotation($method, $annotationName); } /** @@ -80,9 +80,9 @@ public function getPropertyAnnotations(ReflectionProperty $property) /** * {@inheritDoc} */ - public function getPropertyAnnotation(ReflectionProperty $property, $annotation) + public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { - return $this->delegate->getPropertyAnnotation($property, $annotation); + return $this->delegate->getPropertyAnnotation($property, $annotationName); } /** diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 000000000..e6af38923 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,15 @@ + + + + + + + + + From 065fad5dd716558d954075bbc32093cec55594e2 Mon Sep 17 00:00:00 2001 From: Jochem Klaver Date: Fri, 13 May 2022 16:53:49 +0200 Subject: [PATCH 04/25] Catch and process errors that occure during annotation instantiation When an annotation class is instantiated and values are passed to the constructor, TypeErrors and ArgumentCountErrors can be thrown. This change catches those exceptions and shows an AnnotationException containing the annotation name and context. --- .../Annotations/AnnotationException.php | 5 +- lib/Doctrine/Common/Annotations/DocParser.php | 36 ++++++- phpstan.neon | 1 + .../Common/Annotations/DocParserTest.php | 100 +++++++++++++++--- 4 files changed, 122 insertions(+), 20 deletions(-) diff --git a/lib/Doctrine/Common/Annotations/AnnotationException.php b/lib/Doctrine/Common/Annotations/AnnotationException.php index b1ea64e6f..4d91825e5 100644 --- a/lib/Doctrine/Common/Annotations/AnnotationException.php +++ b/lib/Doctrine/Common/Annotations/AnnotationException.php @@ -3,6 +3,7 @@ namespace Doctrine\Common\Annotations; use Exception; +use Throwable; use function get_class; use function gettype; @@ -47,9 +48,9 @@ public static function semanticalError($message) * * @return AnnotationException */ - public static function creationError($message) + public static function creationError($message, ?Throwable $previous = null) { - return new self('[Creation Error] ' . $message); + return new self('[Creation Error] ' . $message, 0, $previous); } /** diff --git a/lib/Doctrine/Common/Annotations/DocParser.php b/lib/Doctrine/Common/Annotations/DocParser.php index ae530c50f..80f307cab 100644 --- a/lib/Doctrine/Common/Annotations/DocParser.php +++ b/lib/Doctrine/Common/Annotations/DocParser.php @@ -12,6 +12,7 @@ use ReflectionProperty; use RuntimeException; use stdClass; +use Throwable; use function array_keys; use function array_map; @@ -941,7 +942,7 @@ private function Annotation() if (self::$annotationMetadata[$name]['has_named_argument_constructor']) { if (PHP_VERSION_ID >= 80000) { - return new $name(...$values); + return $this->instantiateAnnotiation($originalName, $this->context, $name, $values); } $positionalValues = []; @@ -968,16 +969,16 @@ private function Annotation() $positionalValues[self::$annotationMetadata[$name]['constructor_args'][$property]['position']] = $value; } - return new $name(...$positionalValues); + return $this->instantiateAnnotiation($originalName, $this->context, $name, $positionalValues); } // check if the annotation expects values via the constructor, // or directly injected into public properties if (self::$annotationMetadata[$name]['has_constructor'] === true) { - return new $name($values); + return $this->instantiateAnnotiation($originalName, $this->context, $name, [$values]); } - $instance = new $name(); + $instance = $this->instantiateAnnotiation($originalName, $this->context, $name, []); foreach ($values as $property => $value) { if (! isset(self::$annotationMetadata[$name]['properties'][$property])) { @@ -1456,4 +1457,31 @@ private function resolvePositionalValues(array $arguments, string $name): array return $values; } + + /** + * Try to instantiate the annotation and catch and process any exceptions related to failure + * + * @param class-string $name + * @param array $arguments + * + * @return object + * + * @throws AnnotationException + */ + private function instantiateAnnotiation(string $originalName, string $context, string $name, array $arguments) + { + try { + return new $name(...$arguments); + } catch (Throwable $exception) { + throw AnnotationException::creationError( + sprintf( + 'An error occurred while instantiating the annotation @%s declared on %s: "%s".', + $originalName, + $context, + $exception->getMessage() + ), + $exception + ); + } + } } diff --git a/phpstan.neon b/phpstan.neon index ad8f23951..78663a129 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -13,6 +13,7 @@ parameters: ignoreErrors: - '#Instantiated class Doctrine_Tests_Common_Annotations_Fixtures_ClassNoNamespaceNoComment not found#' - '#Property Doctrine\\Tests\\Common\\Annotations\\DummyClassNonAnnotationProblem::\$foo has unknown class#' + - '#Call to an undefined static method PHPUnit\\Framework\\TestCase::expectExceptionMessageRegExp\(\)#' # That tag is empty on purpose - '#PHPDoc tag @var has invalid value \(\)\: Unexpected token "\*/", expected type at offset 9#' diff --git a/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php b/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php index fad9bf93b..5e6f35cb2 100644 --- a/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php +++ b/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php @@ -14,15 +14,19 @@ use Doctrine\Tests\Common\Annotations\Fixtures\ClassWithConstants; use Doctrine\Tests\Common\Annotations\Fixtures\InterfaceWithConstants; use InvalidArgumentException; +use PHPUnit\Framework\Constraint\ExceptionMessage; use PHPUnit\Framework\TestCase; use ReflectionClass; +use TypeError; use function array_column; use function array_combine; use function assert; use function class_exists; use function extension_loaded; +use function get_parent_class; use function ini_get; +use function method_exists; use function sprintf; use function ucfirst; @@ -886,9 +890,16 @@ public function testAnnotationEnumInvalidTypeDeclarationException(): void $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumInvalid("foo")'; $parser->setIgnoreNotImportedAnnotations(false); - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('@Enum supports only scalar values "array" given.'); - $parser->parse($docblock); + $this->expectException(AnnotationException::class); + try { + $parser->parse($docblock); + } catch (AnnotationException $exc) { + $previous = $exc->getPrevious(); + $this->assertInstanceOf(InvalidArgumentException::class, $previous); + $this->assertThat($previous, new ExceptionMessage('@Enum supports only scalar values "array" given.')); + + throw $exc; + } } public function testAnnotationEnumInvalidLiteralDeclarationException(): void @@ -897,9 +908,19 @@ public function testAnnotationEnumInvalidLiteralDeclarationException(): void $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteralInvalid("foo")'; $parser->setIgnoreNotImportedAnnotations(false); - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Undefined enumerator value "3" for literal "AnnotationEnumLiteral::THREE".'); - $parser->parse($docblock); + $this->expectException(AnnotationException::class); + try { + $parser->parse($docblock); + } catch (AnnotationException $exc) { + $previous = $exc->getPrevious(); + $this->assertInstanceOf(InvalidArgumentException::class, $previous); + $this->assertThat( + $previous, + new ExceptionMessage('Undefined enumerator value "3" for literal "AnnotationEnumLiteral::THREE".') + ); + + throw $exc; + } } /** @@ -1100,11 +1121,21 @@ public function testAnnotationWithInvalidTargetDeclarationError(): void DOCBLOCK; $parser->setTarget(Target::TARGET_CLASS); - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage( - 'Invalid Target "Foo". Available targets: [ALL, CLASS, METHOD, PROPERTY, FUNCTION, ANNOTATION]' - ); - $parser->parse($docblock, $context); + $this->expectException(AnnotationException::class); + try { + $parser->parse($docblock, $context); + } catch (AnnotationException $exc) { + $previous = $exc->getPrevious(); + $this->assertInstanceOf(InvalidArgumentException::class, $previous); + $this->assertThat( + $previous, + new ExceptionMessage( + 'Invalid Target "Foo". Available targets: [ALL, CLASS, METHOD, PROPERTY, FUNCTION, ANNOTATION]' + ) + ); + + throw $exc; + } } public function testAnnotationWithTargetEmptyError(): void @@ -1118,9 +1149,19 @@ public function testAnnotationWithTargetEmptyError(): void DOCBLOCK; $parser->setTarget(Target::TARGET_CLASS); - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('@Target expects either a string value, or an array of strings, "NULL" given.'); - $parser->parse($docblock, $context); + $this->expectException(AnnotationException::class); + try { + $parser->parse($docblock, $context); + } catch (AnnotationException $exc) { + $previous = $exc->getPrevious(); + $this->assertInstanceOf(InvalidArgumentException::class, $previous); + $this->assertThat( + $previous, + new ExceptionMessage('@Target expects either a string value, or an array of strings, "NULL" given.') + ); + + throw $exc; + } } /** @@ -1683,6 +1724,37 @@ public function testNamedArgumentsConstructorAnnotationWithInvalidArguments(): v ); $parser->parse('/** @AnotherNamedAnnotation("foo", bar=666, "hey") */'); } + + public function testNamedArgumentsConstructorAnnotationWithWrongArgumentType(): void + { + $context = 'property SomeClassName::invalidProperty.'; + $docblock = '@NamedAnnotationWithArray(foo = "no array!")'; + $parser = $this->createTestParser(); + $this->expectException(AnnotationException::class); + $this->expectExceptionMessageMatches( + '/\[Creation Error\] An error occurred while instantiating the annotation ' + . '@NamedAnnotationWithArray declared on property SomeClassName::invalidProperty\.: ".*"\.$/' + ); + try { + $parser->parse($docblock, $context); + } catch (AnnotationException $exc) { + $this->assertInstanceOf(TypeError::class, $exc->getPrevious()); + + throw $exc; + } + } + + /** + * Override for BC with PHPUnit <8 + */ + public function expectExceptionMessageMatches(string $regularExpression): void + { + if (method_exists(get_parent_class($this), 'expectExceptionMessageMatches')) { + parent::expectExceptionMessageMatches($regularExpression); + } else { + parent::expectExceptionMessageRegExp($regularExpression); + } + } } /** @Annotation */ From 296911c4ab9e4d8254de3ce34317283637b47e22 Mon Sep 17 00:00:00 2001 From: Dorian V Date: Fri, 1 Jul 2022 17:39:35 +0200 Subject: [PATCH 05/25] Ignore PHPStan annotation @readonly see https://github.com/phpstan/phpstan-src/pull/1295 --- .../Common/Annotations/ImplicitlyIgnoredAnnotationNames.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php b/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php index 2efeb1d22..ab27f8a5c 100644 --- a/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php +++ b/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php @@ -147,6 +147,7 @@ final class ImplicitlyIgnoredAnnotationNames // PHPStan, Psalm 'extends' => true, 'implements' => true, + 'readonly' => true, 'template' => true, 'use' => true, From 47fb9f9abdb12067d4b661f67d26e4dcf36a3953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Fri, 1 Jul 2022 19:24:12 +0200 Subject: [PATCH 06/25] Whitelist necessary plugin Not doing so results in the coding standard job to fail. --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a29863a71..8bddde7ac 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,10 @@ "vimeo/psalm": "^4.10" }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } }, "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } From 32305b94b874665895e1114c0778c53334808947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 2 Jul 2022 12:17:31 +0200 Subject: [PATCH 07/25] Use latest versions of shared workflows --- .github/workflows/coding-standards.yml | 2 +- .github/workflows/continuous-integration.yml | 2 +- .github/workflows/release-on-milestone-closed.yml | 3 +-- .github/workflows/static-analysis.yml | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 352847e3b..3ae99378a 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -12,4 +12,4 @@ on: jobs: coding-standards: name: "Coding Standards" - uses: "doctrine/.github/.github/workflows/coding-standards.yml@1.1.1" + uses: "doctrine/.github/.github/workflows/coding-standards.yml@1.5.1" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 2720d0b2e..ac5f75bb6 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -14,6 +14,6 @@ env: jobs: phpunit: name: "PHPUnit" - uses: "doctrine/.github/.github/workflows/continuous-integration.yml@1.1.1" + uses: "doctrine/.github/.github/workflows/continuous-integration.yml@1.5.1" with: php-versions: '["7.1", "7.2", "7.3", "7.4", "8.0"]' diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml index 563333bb4..d32d76ef7 100644 --- a/.github/workflows/release-on-milestone-closed.yml +++ b/.github/workflows/release-on-milestone-closed.yml @@ -8,9 +8,8 @@ on: jobs: release: name: "Git tag, release & create merge-up PR" - uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@1.1.1" + uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@1.5.1" secrets: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }} GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }} ORGANIZATION_ADMIN_TOKEN: ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index ddcf2ddf7..4150ff29b 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -12,4 +12,4 @@ on: jobs: static-analysis: name: "Static Analysis" - uses: "doctrine/.github/.github/workflows/static-analysis.yml@1.1.1" + uses: "doctrine/.github/.github/workflows/static-analysis.yml@1.5.1" From 06a3f83864b622ace2dc5f6d7acdc65eb86a2cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 2 Jul 2022 12:18:57 +0200 Subject: [PATCH 08/25] Add Composer Lint workflow --- .github/workflows/composer-lint.yml | 18 ++++++++++ composer.json | 51 ++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/composer-lint.yml diff --git a/.github/workflows/composer-lint.yml b/.github/workflows/composer-lint.yml new file mode 100644 index 000000000..ae17b7af3 --- /dev/null +++ b/.github/workflows/composer-lint.yml @@ -0,0 +1,18 @@ +name: "Composer Lint" + +on: + pull_request: + branches: + - "*.x" + paths: + - "composer.json" + push: + branches: + - "*.x" + paths: + - "composer.json" + +jobs: + composer-lint: + name: "Composer Lint" + uses: "doctrine/.github/.github/workflows/composer-lint.yml@1.5.1" diff --git a/composer.json b/composer.json index 8bddde7ac..88fbc51ec 100644 --- a/composer.json +++ b/composer.json @@ -1,17 +1,36 @@ { "name": "doctrine/annotations", - "type": "library", "description": "Docblock Annotations Parser", - "keywords": ["annotations", "docblock", "parser"], - "homepage": "https://www.doctrine-project.org/projects/annotations.html", "license": "MIT", + "type": "library", + "keywords": [ + "annotations", + "docblock", + "parser" + ], "authors": [ - {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, - {"name": "Roman Borschel", "email": "roman@code-factory.org"}, - {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, - {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, - {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } ], + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "require": { "php": "^7.1 || ^8.0", "ext-tokenizer": "*", @@ -26,14 +45,10 @@ "symfony/cache": "^4.4 || ^5.2", "vimeo/psalm": "^4.10" }, - "config": { - "sort-packages": true, - "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true - } - }, "autoload": { - "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } }, "autoload-dev": { "psr-4": { @@ -44,5 +59,11 @@ "tests/Doctrine/Tests/Common/Annotations/Fixtures/functions.php", "tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php" ] + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + }, + "sort-packages": true } } From 46720608c1f208d70599f186976502ec0c61da6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 2 Jul 2022 12:26:57 +0200 Subject: [PATCH 09/25] Update PHPStan --- composer.json | 2 +- phpstan.neon | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 88fbc51ec..b4dfbd390 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "require-dev": { "doctrine/cache": "^1.11 || ^2.0", "doctrine/coding-standard": "^6.0 || ^8.1", - "phpstan/phpstan": "^0.12.20", + "phpstan/phpstan": "^1.4.10 || ^1.8.0", "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", "symfony/cache": "^4.4 || ^5.2", "vimeo/psalm": "^4.10" diff --git a/phpstan.neon b/phpstan.neon index 78663a129..4d2b2f1d3 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,7 +3,12 @@ parameters: paths: - lib - tests - excludes_analyse: + scanFiles: + - tests/Doctrine/Tests/Common/Annotations/Fixtures/GlobalNamespacesPerFileWithClassAsFirst.php + - tests/Doctrine/Tests/Common/Annotations/Fixtures/GlobalNamespacesPerFileWithClassAsLast.php + - tests/Doctrine/Tests/Common/Annotations/Fixtures/NonNamespacedClass.php + - tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php + excludePaths: - tests/*/Fixtures/* - tests/Doctrine/Tests/Common/Annotations/ReservedKeywordsClasses.php - tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php From 914c6de7c345ebe96a299a15475e6d233b301bd5 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Tue, 12 Jul 2022 07:47:05 +0200 Subject: [PATCH 10/25] Bump dev dependencies --- composer.json | 8 ++++---- .../Common/Annotations/Annotation/Enum.php | 4 ++-- .../Annotations/Annotation/IgnoreAnnotation.php | 4 ++-- .../Common/Annotations/Annotation/Target.php | 4 ++-- .../Common/Annotations/AnnotationException.php | 3 +-- lib/Doctrine/Common/Annotations/DocParser.php | 16 ++++++++-------- phpcs.xml.dist | 12 ++---------- 7 files changed, 21 insertions(+), 30 deletions(-) diff --git a/composer.json b/composer.json index b4dfbd390..62f76d217 100644 --- a/composer.json +++ b/composer.json @@ -39,10 +39,10 @@ }, "require-dev": { "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^6.0 || ^8.1", - "phpstan/phpstan": "^1.4.10 || ^1.8.0", - "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", - "symfony/cache": "^4.4 || ^5.2", + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "~1.4.10 || ^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^4.4 || ^5.4 || ^6", "vimeo/psalm": "^4.10" }, "autoload": { diff --git a/lib/Doctrine/Common/Annotations/Annotation/Enum.php b/lib/Doctrine/Common/Annotations/Annotation/Enum.php index 35d6410b1..6f24d9f1b 100644 --- a/lib/Doctrine/Common/Annotations/Annotation/Enum.php +++ b/lib/Doctrine/Common/Annotations/Annotation/Enum.php @@ -34,9 +34,9 @@ final class Enum public $literal; /** - * @throws InvalidArgumentException - * * @phpstan-param array{literal?: mixed[], value: list} $values + * + * @throws InvalidArgumentException */ public function __construct(array $values) { diff --git a/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php b/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php index ae60f7d5b..97a15c257 100644 --- a/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php +++ b/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php @@ -21,9 +21,9 @@ final class IgnoreAnnotation public $names; /** - * @throws RuntimeException - * * @phpstan-param array{value: string|list} $values + * + * @throws RuntimeException */ public function __construct(array $values) { diff --git a/lib/Doctrine/Common/Annotations/Annotation/Target.php b/lib/Doctrine/Common/Annotations/Annotation/Target.php index 7fd75e2bb..ba1d489ac 100644 --- a/lib/Doctrine/Common/Annotations/Annotation/Target.php +++ b/lib/Doctrine/Common/Annotations/Annotation/Target.php @@ -56,9 +56,9 @@ final class Target public $literal; /** - * @throws InvalidArgumentException - * * @phpstan-param array{value?: string|list} $values + * + * @throws InvalidArgumentException */ public function __construct(array $values) { diff --git a/lib/Doctrine/Common/Annotations/AnnotationException.php b/lib/Doctrine/Common/Annotations/AnnotationException.php index 4d91825e5..600bddf4d 100644 --- a/lib/Doctrine/Common/Annotations/AnnotationException.php +++ b/lib/Doctrine/Common/Annotations/AnnotationException.php @@ -133,10 +133,9 @@ public static function requiredError($attributeName, $annotationName, $context, * @param string $annotationName * @param string $context * @param mixed $given + * @phpstan-param list $available * * @return AnnotationException - * - * @phpstan-param list $available */ public static function enumeratorError($attributeName, $annotationName, $context, $available, $given) { diff --git a/lib/Doctrine/Common/Annotations/DocParser.php b/lib/Doctrine/Common/Annotations/DocParser.php index 80f307cab..eda440abd 100644 --- a/lib/Doctrine/Common/Annotations/DocParser.php +++ b/lib/Doctrine/Common/Annotations/DocParser.php @@ -357,10 +357,10 @@ public function setTarget($target) * @param string $input The docblock string to parse. * @param string $context The parsing context. * + * @phpstan-return list Array of annotations. If no annotations are found, an empty array is returned. + * * @throws AnnotationException * @throws ReflectionException - * - * @phpstan-return list Array of annotations. If no annotations are found, an empty array is returned. */ public function parse($input, $context = '') { @@ -426,9 +426,9 @@ private function match(int $token): bool * If any of them matches, this method updates the lookahead token; otherwise * a syntax error is raised. * - * @throws AnnotationException - * * @phpstan-param list $tokens + * + * @throws AnnotationException */ private function matchAny(array $tokens): bool { @@ -674,10 +674,10 @@ private function collectAttributeTypeMetadata(array &$metadata, Attribute $attri /** * Annotations ::= Annotation {[ "*" ]* [Annotation]}* * + * @phpstan-return list + * * @throws AnnotationException * @throws ReflectionException - * - * @phpstan-return list */ private function Annotations(): array { @@ -1357,10 +1357,10 @@ private function Arrayx(): array * KeyValuePair ::= Key ("=" | ":") PlainValue | Constant * Key ::= string | integer | Constant * + * @phpstan-return array{mixed, mixed} + * * @throws AnnotationException * @throws ReflectionException - * - * @phpstan-return array{mixed, mixed} */ private function ArrayEntry(): array { diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 038d03b00..0a170cdf1 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -12,6 +12,8 @@ + + lib tests @@ -105,16 +107,6 @@ */tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithRequire.php - - - */tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php - */tests/Doctrine/Tests/Common/Annotations/Fixtures/NamespacedSingleClassLOC1000.php - - - - */tests/Doctrine/Tests/Common/Annotations/Fixtures/Controller.php - - */tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php From 4791d547526c3490a7521ce709aa2d86522ae635 Mon Sep 17 00:00:00 2001 From: Jorrit Schippers Date: Fri, 30 Sep 2022 21:32:14 +0200 Subject: [PATCH 11/25] Fix typo in custom.rst --- docs/en/custom.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/custom.rst b/docs/en/custom.rst index 11fbe1a31..e8f79af71 100644 --- a/docs/en/custom.rst +++ b/docs/en/custom.rst @@ -69,7 +69,7 @@ When using the ``@NamedArgumentConstructor`` tag, the first argument of the constructor is considered as the default one. -Usage with the ``@NamedArgumentContrustor`` tag +Usage with the ``@NamedArgumentConstructor`` tag .. code-block:: php From cbc5a5188d9eecf2e878a201351d414a1bb3bee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Tue, 4 Oct 2022 21:37:20 +0200 Subject: [PATCH 12/25] Encourage migration towards attributes We will deprecate this libary eventually. --- README.md | 6 ++++++ composer.json | 3 +++ docs/en/index.rst | 9 +++++++++ 3 files changed, 18 insertions(+) diff --git a/README.md b/README.md index c2c7eb7ba..6b8c0359b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +⚠️ PHP 8 introduced +[attributes](https://www.php.net/manual/en/language.attributes.overview.php), +which are a native replacement for annotations. As such, this library is +considered feature complete, and should receive exclusively bugfixes and +security fixes. + # Doctrine Annotations [![Build Status](https://github.com/doctrine/annotations/workflows/Continuous%20Integration/badge.svg?label=build)](https://github.com/doctrine/persistence/actions) diff --git a/composer.json b/composer.json index 62f76d217..b5839c84a 100644 --- a/composer.json +++ b/composer.json @@ -45,6 +45,9 @@ "symfony/cache": "^4.4 || ^5.4 || ^6", "vimeo/psalm": "^4.10" }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + }, "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" diff --git a/docs/en/index.rst b/docs/en/index.rst index 95476c313..7caffb50a 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -1,3 +1,12 @@ +Deprecation notice +================== + +PHP 8 introduced `attributes +`_, +which are a native replacement for annotations. As such, this library is +considered feature complete, and should receive exclusively bugfixes and +security fixes. + Introduction ============ From 8fb4048e0b24e05b0a14be7e1eb1a7e3afaf072f Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Wed, 5 Oct 2022 23:45:17 +0200 Subject: [PATCH 13/25] Bump GHA workflows --- .github/workflows/coding-standards.yml | 2 +- .github/workflows/composer-lint.yml | 2 +- .github/workflows/continuous-integration.yml | 4 ++-- .github/workflows/release-on-milestone-closed.yml | 2 +- .github/workflows/static-analysis.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 3ae99378a..83fd68b5e 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -12,4 +12,4 @@ on: jobs: coding-standards: name: "Coding Standards" - uses: "doctrine/.github/.github/workflows/coding-standards.yml@1.5.1" + uses: "doctrine/.github/.github/workflows/coding-standards.yml@2.1.0" diff --git a/.github/workflows/composer-lint.yml b/.github/workflows/composer-lint.yml index ae17b7af3..091b491c3 100644 --- a/.github/workflows/composer-lint.yml +++ b/.github/workflows/composer-lint.yml @@ -15,4 +15,4 @@ on: jobs: composer-lint: name: "Composer Lint" - uses: "doctrine/.github/.github/workflows/composer-lint.yml@1.5.1" + uses: "doctrine/.github/.github/workflows/composer-lint.yml@2.1.0" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ac5f75bb6..f160d3efb 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -14,6 +14,6 @@ env: jobs: phpunit: name: "PHPUnit" - uses: "doctrine/.github/.github/workflows/continuous-integration.yml@1.5.1" + uses: "doctrine/.github/.github/workflows/continuous-integration.yml@2.1.0" with: - php-versions: '["7.1", "7.2", "7.3", "7.4", "8.0"]' + php-versions: '["7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2"]' diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml index d32d76ef7..949de8201 100644 --- a/.github/workflows/release-on-milestone-closed.yml +++ b/.github/workflows/release-on-milestone-closed.yml @@ -8,7 +8,7 @@ on: jobs: release: name: "Git tag, release & create merge-up PR" - uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@1.5.1" + uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@2.1.0" secrets: GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }} GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 4150ff29b..0adc3d08e 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -12,4 +12,4 @@ on: jobs: static-analysis: name: "Static Analysis" - uses: "doctrine/.github/.github/workflows/static-analysis.yml@1.5.1" + uses: "doctrine/.github/.github/workflows/static-analysis.yml@2.1.0" From 8cd57e880a60372f333610b77a3ecdf54fe162e5 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Wed, 5 Oct 2022 23:44:27 +0200 Subject: [PATCH 14/25] Doctrine CS 10 --- composer.json | 2 +- .../Common/Annotations/Annotation.php | 4 +- .../Annotations/AnnotationException.php | 8 +-- .../Common/Annotations/CachedReader.php | 4 +- lib/Doctrine/Common/Annotations/DocParser.php | 4 +- .../Common/Annotations/TokenParser.php | 4 +- phpcs.xml.dist | 5 ++ .../Common/Annotations/DocParserTest.php | 50 +++++++++---------- 8 files changed, 37 insertions(+), 44 deletions(-) diff --git a/composer.json b/composer.json index b5839c84a..47971b5cc 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ }, "require-dev": { "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^9", + "doctrine/coding-standard": "^9 || ^10", "phpstan/phpstan": "~1.4.10 || ^1.8.0", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "symfony/cache": "^4.4 || ^5.4 || ^6", diff --git a/lib/Doctrine/Common/Annotations/Annotation.php b/lib/Doctrine/Common/Annotations/Annotation.php index 750270e42..9cae3dacd 100644 --- a/lib/Doctrine/Common/Annotations/Annotation.php +++ b/lib/Doctrine/Common/Annotations/Annotation.php @@ -18,9 +18,7 @@ class Annotation */ public $value; - /** - * @param array $data Key-value for properties to be defined in this class. - */ + /** @param array $data Key-value for properties to be defined in this class. */ final public function __construct(array $data) { foreach ($data as $key => $value) { diff --git a/lib/Doctrine/Common/Annotations/AnnotationException.php b/lib/Doctrine/Common/Annotations/AnnotationException.php index 600bddf4d..dcdfe4df6 100644 --- a/lib/Doctrine/Common/Annotations/AnnotationException.php +++ b/lib/Doctrine/Common/Annotations/AnnotationException.php @@ -149,9 +149,7 @@ public static function enumeratorError($attributeName, $annotationName, $context )); } - /** - * @return AnnotationException - */ + /** @return AnnotationException */ public static function optimizerPlusSaveComments() { return new self( @@ -159,9 +157,7 @@ public static function optimizerPlusSaveComments() ); } - /** - * @return AnnotationException - */ + /** @return AnnotationException */ public static function optimizerPlusLoadComments() { return new self( diff --git a/lib/Doctrine/Common/Annotations/CachedReader.php b/lib/Doctrine/Common/Annotations/CachedReader.php index c036b2dab..85dbefab5 100644 --- a/lib/Doctrine/Common/Annotations/CachedReader.php +++ b/lib/Doctrine/Common/Annotations/CachedReader.php @@ -38,9 +38,7 @@ final class CachedReader implements Reader /** @var int[] */ private $loadedFilemtimes = []; - /** - * @param bool $debug - */ + /** @param bool $debug */ public function __construct(Reader $reader, Cache $cache, $debug = false) { $this->delegate = $reader; diff --git a/lib/Doctrine/Common/Annotations/DocParser.php b/lib/Doctrine/Common/Annotations/DocParser.php index eda440abd..4133fe5f4 100644 --- a/lib/Doctrine/Common/Annotations/DocParser.php +++ b/lib/Doctrine/Common/Annotations/DocParser.php @@ -1166,9 +1166,7 @@ private function identifierEndsWithClassConstant(string $identifier): bool return $this->getClassConstantPositionInIdentifier($identifier) === strlen($identifier) - strlen('::class'); } - /** - * @return int|false - */ + /** @return int|false */ private function getClassConstantPositionInIdentifier(string $identifier) { return stripos($identifier, '::class'); diff --git a/lib/Doctrine/Common/Annotations/TokenParser.php b/lib/Doctrine/Common/Annotations/TokenParser.php index 9605fb8dd..69259fccf 100644 --- a/lib/Doctrine/Common/Annotations/TokenParser.php +++ b/lib/Doctrine/Common/Annotations/TokenParser.php @@ -46,9 +46,7 @@ class TokenParser */ private $pointer = 0; - /** - * @param string $contents - */ + /** @param string $contents */ public function __construct($contents) { $this->tokens = token_get_all($contents); diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 0a170cdf1..eeadabad2 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -148,4 +148,9 @@ */tests/Doctrine/Tests/Common/Annotations/Fixtures/functions.php + + + + */tests + diff --git a/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php b/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php index 5e6f35cb2..297d7e90c 100644 --- a/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php +++ b/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php @@ -130,7 +130,7 @@ public function testBasicAnnotations(): void self::assertEquals('value2', $annot->value[1]['key2']); // Complete docblock - $docblock = <<setIgnoreNotImportedAnnotations(true); - $docblock = <<createTestParser(); - $docblock = <<createTestParser(); - $docblock = <<data); self::assertEquals($annot->data, 'Some data'); - $docblock = <<name, 'Some Name'); self::assertEquals($annot->data, 'Some data'); - $docblock = <<data, 'Some data'); self::assertNull($annot->name); - $docblock = <<name, 'Some name'); self::assertNull($annot->data); - $docblock = <<data, 'Some data'); self::assertNull($annot->name); - $docblock = <<name, 'Some name'); self::assertEquals($annot->data, 'Some data'); - $docblock = <<name, 'Some name'); self::assertEquals($annot->data, 'Some data'); - $docblock = <<createTestParser(); - $docblock = <<createTestParser(); - $docblock = <<createTestParser(); $context = 'class SomeClassName'; - $docblock = <<createTestParser(); $context = 'class SomeClassName'; - $docblock = <<createTestParser(); $context = 'class SomeClassName'; - $docblock = <<createTestParser(); - $docblock = <<markTestSkipped('This test requires mbstring function overloading is turned on'); } - $docblock = << Date: Wed, 26 Oct 2022 10:01:44 +0200 Subject: [PATCH 15/25] Added test cases for enums (#459) --- .../Annotations/AnnotationReaderTest.php | 34 +++++++++++++++++++ .../Fixtures/AnnotationWithEnumProperty.php | 18 ++++++++++ .../Fixtures/ClassWithEnumAnnotations.php | 14 ++++++++ .../Common/Annotations/Fixtures/Suit.php | 13 +++++++ 4 files changed, 79 insertions(+) create mode 100644 tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithEnumProperty.php create mode 100644 tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithEnumAnnotations.php create mode 100644 tests/Doctrine/Tests/Common/Annotations/Fixtures/Suit.php diff --git a/tests/Doctrine/Tests/Common/Annotations/AnnotationReaderTest.php b/tests/Doctrine/Tests/Common/Annotations/AnnotationReaderTest.php index 5c8754295..04ffe1b37 100644 --- a/tests/Doctrine/Tests/Common/Annotations/AnnotationReaderTest.php +++ b/tests/Doctrine/Tests/Common/Annotations/AnnotationReaderTest.php @@ -6,6 +6,8 @@ use Doctrine\Common\Annotations\DocParser; use Doctrine\Common\Annotations\Reader; use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\SingleUseAnnotation; +use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithEnumProperty; +use Doctrine\Tests\Common\Annotations\Fixtures\ClassWithEnumAnnotations; use Doctrine\Tests\Common\Annotations\Fixtures\ClassWithFullPathUseStatement; use Doctrine\Tests\Common\Annotations\Fixtures\ClassWithImportedIgnoredAnnotation; use Doctrine\Tests\Common\Annotations\Fixtures\ClassWithPHPCodeSnifferAnnotation; @@ -15,6 +17,7 @@ use Doctrine\Tests\Common\Annotations\Fixtures\IgnoredNamespaces\AnnotatedAtMethodLevel; use Doctrine\Tests\Common\Annotations\Fixtures\IgnoredNamespaces\AnnotatedAtPropertyLevel; use Doctrine\Tests\Common\Annotations\Fixtures\IgnoredNamespaces\AnnotatedWithAlias; +use Doctrine\Tests\Common\Annotations\Fixtures\Suit; use InvalidArgumentException; use LogicException; use ReflectionClass; @@ -24,6 +27,8 @@ use function spl_autoload_register; use function spl_autoload_unregister; +use const PHP_VERSION_ID; + class AnnotationReaderTest extends AbstractReaderTest { /** @@ -295,4 +300,33 @@ public function testFunctionAnnotation(): void $annotation = $reader->getFunctionAnnotation($ref, Fixtures\Annotation\Autoload::class); self::assertInstanceOf(Fixtures\Annotation\Autoload::class, $annotation); } + + /** + * @requires PHP 8.1 + * @dataProvider provideEnumProperties + */ + public function testAnnotationWithEnum(string $property, Suit $expectedValue): void + { + $reader = $this->getReader(); + $ref = new ReflectionClass(ClassWithEnumAnnotations::class); + + $annotation = $reader->getPropertyAnnotation($ref->getProperty($property), AnnotationWithEnumProperty::class); + + self::assertSame($expectedValue, $annotation->suit); + } + + /** + * @return array + */ + public function provideEnumProperties(): array + { + if (PHP_VERSION_ID < 80100) { + return []; + } + + return [ + 'annotationWithDefaults' => ['annotationWithDefaults', Suit::Hearts], + 'annotationWithSpades' => ['annotationWithSpades', Suit::Spades], + ]; + } } diff --git a/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithEnumProperty.php b/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithEnumProperty.php new file mode 100644 index 000000000..97d299769 --- /dev/null +++ b/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithEnumProperty.php @@ -0,0 +1,18 @@ + Date: Sat, 10 Dec 2022 16:15:47 +0100 Subject: [PATCH 16/25] Allow doctrine/lexer 2 It is still possible to avoid a BC-break while doing so. --- composer.json | 2 +- lib/Doctrine/Common/Annotations/DocLexer.php | 14 ++++++++++++-- phpstan.neon | 2 ++ .../Tests/Common/Annotations/DocLexerTest.php | 2 ++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 47971b5cc..e322d82fc 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "require": { "php": "^7.1 || ^8.0", "ext-tokenizer": "*", - "doctrine/lexer": "1.*", + "doctrine/lexer": "^1 || ^2", "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { diff --git a/lib/Doctrine/Common/Annotations/DocLexer.php b/lib/Doctrine/Common/Annotations/DocLexer.php index f6567c512..7c0284b09 100644 --- a/lib/Doctrine/Common/Annotations/DocLexer.php +++ b/lib/Doctrine/Common/Annotations/DocLexer.php @@ -15,6 +15,8 @@ /** * Simple lexer for docblock annotations. + * + * @template-extends AbstractLexer */ final class DocLexer extends AbstractLexer { @@ -39,7 +41,7 @@ final class DocLexer extends AbstractLexer public const T_COLON = 112; public const T_MINUS = 113; - /** @var array */ + /** @var array */ protected $noCase = [ '@' => self::T_AT, ',' => self::T_COMMA, @@ -53,7 +55,7 @@ final class DocLexer extends AbstractLexer '\\' => self::T_NAMESPACE_SEPARATOR, ]; - /** @var array */ + /** @var array */ protected $withCase = [ 'true' => self::T_TRUE, 'false' => self::T_FALSE, @@ -126,4 +128,12 @@ protected function getType(&$value) return $type; } + + /** @return array{value: int|string, type:self::T_*|null, position:int} */ + public function peek(): array + { + $token = parent::peek(); + + return (array) $token; + } } diff --git a/phpstan.neon b/phpstan.neon index 4d2b2f1d3..40b25384a 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -22,3 +22,5 @@ parameters: # That tag is empty on purpose - '#PHPDoc tag @var has invalid value \(\)\: Unexpected token "\*/", expected type at offset 9#' + # Backwards-compatibility + - '#^Return type.*of method.*DocLexer::peek.*should be compatible.*$#' diff --git a/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php b/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php index 0df8a115a..c5c541984 100644 --- a/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php +++ b/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php @@ -19,9 +19,11 @@ public function testMarkerAnnotation(): void self::assertTrue($lexer->moveNext()); self::assertNull($lexer->token); + self::assertNotNull($lexer->lookahead); self::assertEquals('@', $lexer->lookahead['value']); self::assertTrue($lexer->moveNext()); + self::assertNotNull($lexer->token); self::assertEquals('@', $lexer->token['value']); self::assertEquals('Name', $lexer->lookahead['value']); From fd04499ddb0bddfc1facdc6a171fb6a70b33ac9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Mon, 12 Dec 2022 13:39:48 +0100 Subject: [PATCH 17/25] Do not cast null to array The possibility that null could be returned was overlooked in 2286f7ba1e943d3be4ce22503feac8c683e35bd5 . --- lib/Doctrine/Common/Annotations/DocLexer.php | 6 +++++- phpstan.neon | 2 -- tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php | 5 +++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/Common/Annotations/DocLexer.php b/lib/Doctrine/Common/Annotations/DocLexer.php index 7c0284b09..dbf60c0d5 100644 --- a/lib/Doctrine/Common/Annotations/DocLexer.php +++ b/lib/Doctrine/Common/Annotations/DocLexer.php @@ -130,10 +130,14 @@ protected function getType(&$value) } /** @return array{value: int|string, type:self::T_*|null, position:int} */ - public function peek(): array + public function peek(): ?array { $token = parent::peek(); + if ($token === null) { + return null; + } + return (array) $token; } } diff --git a/phpstan.neon b/phpstan.neon index 40b25384a..4d2b2f1d3 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -22,5 +22,3 @@ parameters: # That tag is empty on purpose - '#PHPDoc tag @var has invalid value \(\)\: Unexpected token "\*/", expected type at offset 9#' - # Backwards-compatibility - - '#^Return type.*of method.*DocLexer::peek.*should be compatible.*$#' diff --git a/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php b/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php index c5c541984..d32b6ad89 100644 --- a/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php +++ b/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php @@ -320,4 +320,9 @@ public function testTokenAdjacency(): void self::assertFalse($lexer->nextTokenIsAdjacent()); self::assertFalse($lexer->moveNext()); } + + public function testItReturnsNullWhenThereIsNothingToParse(): void + { + self::assertNull((new DocLexer())->peek()); + } } From f46ab81058ca5b5ec9ea9b22bd34eee01aa3b9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Wed, 14 Dec 2022 19:49:13 +0100 Subject: [PATCH 18/25] Specify second template parameter The by-ref $value parameter is modified in DocLexer::getType(), but not its type, which stays a string. This guarantee that the token value will always be a string. --- lib/Doctrine/Common/Annotations/DocLexer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/Common/Annotations/DocLexer.php b/lib/Doctrine/Common/Annotations/DocLexer.php index dbf60c0d5..dbba52522 100644 --- a/lib/Doctrine/Common/Annotations/DocLexer.php +++ b/lib/Doctrine/Common/Annotations/DocLexer.php @@ -16,7 +16,7 @@ /** * Simple lexer for docblock annotations. * - * @template-extends AbstractLexer + * @template-extends AbstractLexer */ final class DocLexer extends AbstractLexer { From 54d06bec181f72d3ab3130af0599a71f1ca698aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Fri, 16 Dec 2022 12:53:28 +0100 Subject: [PATCH 19/25] Drop Lexer 1 This is not a big deal because doctrine/lexer 1 has the same version constraints as doctrine/lexer 2. Note that I'm removing the BC-layer for DocLexer::peek() and DocLexer::glimpse(): in retrospect, I think it was a bad idea because there was already a BC-break with DocLexer::$lookahead and DocLexer::$token. --- UPGRADE.md | 6 +++ composer.json | 2 +- lib/Doctrine/Common/Annotations/DocLexer.php | 14 +------ lib/Doctrine/Common/Annotations/DocParser.php | 40 +++++++++---------- .../Tests/Common/Annotations/DocLexerTest.php | 33 +++++++-------- 5 files changed, 45 insertions(+), 50 deletions(-) create mode 100644 UPGRADE.md diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 000000000..ae9509cd5 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,6 @@ +# Upgrade from 1.0.x to 2.0.x + +`DocLexer::peek()` and `DocLexer::glimpse` now return +`Doctrine\Common\Lexer\Token` objects. When using `doctrine/lexer` 2, these +implement `ArrayAccess` as a way for you to still be able to treat them as +arrays in some ways. diff --git a/composer.json b/composer.json index e322d82fc..8dc634be3 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "require": { "php": "^7.1 || ^8.0", "ext-tokenizer": "*", - "doctrine/lexer": "^1 || ^2", + "doctrine/lexer": "^2", "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { diff --git a/lib/Doctrine/Common/Annotations/DocLexer.php b/lib/Doctrine/Common/Annotations/DocLexer.php index dbba52522..cbb86a708 100644 --- a/lib/Doctrine/Common/Annotations/DocLexer.php +++ b/lib/Doctrine/Common/Annotations/DocLexer.php @@ -70,7 +70,7 @@ public function nextTokenIsAdjacent(): bool { return $this->token === null || ($this->lookahead !== null - && ($this->lookahead['position'] - $this->token['position']) === strlen($this->token['value'])); + && ($this->lookahead->position - $this->token->position) === strlen($this->token->value)); } /** @@ -128,16 +128,4 @@ protected function getType(&$value) return $type; } - - /** @return array{value: int|string, type:self::T_*|null, position:int} */ - public function peek(): ?array - { - $token = parent::peek(); - - if ($token === null) { - return null; - } - - return (array) $token; - } } diff --git a/lib/Doctrine/Common/Annotations/DocParser.php b/lib/Doctrine/Common/Annotations/DocParser.php index 4133fe5f4..37c352409 100644 --- a/lib/Doctrine/Common/Annotations/DocParser.php +++ b/lib/Doctrine/Common/Annotations/DocParser.php @@ -454,7 +454,7 @@ private function syntaxError(string $expected, ?array $token = null): Annotation $message = sprintf('Expected %s, got ', $expected); $message .= $this->lexer->lookahead === null ? 'end of string' - : sprintf("'%s' at position %s", $token['value'], $token['position']); + : sprintf("'%s' at position %s", $token->value, $token->position); if (strlen($this->context)) { $message .= ' in ' . $this->context; @@ -684,7 +684,7 @@ private function Annotations(): array $annotations = []; while ($this->lexer->lookahead !== null) { - if ($this->lexer->lookahead['type'] !== DocLexer::T_AT) { + if ($this->lexer->lookahead->type !== DocLexer::T_AT) { $this->lexer->moveNext(); continue; } @@ -692,8 +692,8 @@ private function Annotations(): array // make sure the @ is preceded by non-catchable pattern if ( $this->lexer->token !== null && - $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen( - $this->lexer->token['value'] + $this->lexer->lookahead->position === $this->lexer->token->position + strlen( + $this->lexer->token->value ) ) { $this->lexer->moveNext(); @@ -705,12 +705,12 @@ private function Annotations(): array $peek = $this->lexer->glimpse(); if ( ($peek === null) - || ($peek['type'] !== DocLexer::T_NAMESPACE_SEPARATOR && ! in_array( - $peek['type'], + || ($peek->type !== DocLexer::T_NAMESPACE_SEPARATOR && ! in_array( + $peek->type, self::$classIdentifiers, true )) - || $peek['position'] !== $this->lexer->lookahead['position'] + 1 + || $peek->position !== $this->lexer->lookahead->position + 1 ) { $this->lexer->moveNext(); continue; @@ -1186,18 +1186,18 @@ private function Identifier(): string $this->lexer->moveNext(); - $className = $this->lexer->token['value']; + $className = $this->lexer->token->value; while ( $this->lexer->lookahead !== null && - $this->lexer->lookahead['position'] === ($this->lexer->token['position'] + - strlen($this->lexer->token['value'])) && + $this->lexer->lookahead->position === ($this->lexer->token->position + + strlen($this->lexer->token->value)) && $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR) ) { $this->match(DocLexer::T_NAMESPACE_SEPARATOR); $this->matchAny(self::$classIdentifiers); - $className .= '\\' . $this->lexer->token['value']; + $className .= '\\' . $this->lexer->token->value; } return $className; @@ -1215,7 +1215,7 @@ private function Value() { $peek = $this->lexer->glimpse(); - if ($peek['type'] === DocLexer::T_EQUALS) { + if ($peek->type === DocLexer::T_EQUALS) { return $this->FieldAssignment(); } @@ -1244,21 +1244,21 @@ private function PlainValue() return $this->Constant(); } - switch ($this->lexer->lookahead['type']) { + switch ($this->lexer->lookahead->type) { case DocLexer::T_STRING: $this->match(DocLexer::T_STRING); - return $this->lexer->token['value']; + return $this->lexer->token->value; case DocLexer::T_INTEGER: $this->match(DocLexer::T_INTEGER); - return (int) $this->lexer->token['value']; + return (int) $this->lexer->token->value; case DocLexer::T_FLOAT: $this->match(DocLexer::T_FLOAT); - return (float) $this->lexer->token['value']; + return (float) $this->lexer->token->value; case DocLexer::T_TRUE: $this->match(DocLexer::T_TRUE); @@ -1290,7 +1290,7 @@ private function PlainValue() private function FieldAssignment(): stdClass { $this->match(DocLexer::T_IDENTIFIER); - $fieldName = $this->lexer->token['value']; + $fieldName = $this->lexer->token->value; $this->match(DocLexer::T_EQUALS); @@ -1365,14 +1365,14 @@ private function ArrayEntry(): array $peek = $this->lexer->glimpse(); if ( - $peek['type'] === DocLexer::T_EQUALS - || $peek['type'] === DocLexer::T_COLON + $peek->type === DocLexer::T_EQUALS + || $peek->type === DocLexer::T_COLON ) { if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { $key = $this->Constant(); } else { $this->matchAny([DocLexer::T_INTEGER, DocLexer::T_STRING]); - $key = $this->lexer->token['value']; + $key = $this->lexer->token->value; } $this->matchAny([DocLexer::T_EQUALS, DocLexer::T_COLON]); diff --git a/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php b/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php index d32b6ad89..f0bc8b080 100644 --- a/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php +++ b/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php @@ -3,6 +3,7 @@ namespace Doctrine\Tests\Common\Annotations; use Doctrine\Common\Annotations\DocLexer; +use Doctrine\Common\Lexer\Token; use PHPUnit\Framework\TestCase; use function str_repeat; @@ -20,12 +21,12 @@ public function testMarkerAnnotation(): void self::assertTrue($lexer->moveNext()); self::assertNull($lexer->token); self::assertNotNull($lexer->lookahead); - self::assertEquals('@', $lexer->lookahead['value']); + self::assertEquals('@', $lexer->lookahead->value); self::assertTrue($lexer->moveNext()); self::assertNotNull($lexer->token); - self::assertEquals('@', $lexer->token['value']); - self::assertEquals('Name', $lexer->lookahead['value']); + self::assertEquals('@', $lexer->token->value); + self::assertEquals('Name', $lexer->lookahead->value); self::assertFalse($lexer->moveNext()); } @@ -114,9 +115,9 @@ public function testScannerTokenizesDocBlockWhitConstants(): void foreach ($tokens as $expected) { $lexer->moveNext(); $lookahead = $lexer->lookahead; - self::assertEquals($expected['value'], $lookahead['value']); - self::assertEquals($expected['type'], $lookahead['type']); - self::assertEquals($expected['position'], $lookahead['position']); + self::assertEquals($expected['value'], $lookahead->value); + self::assertEquals($expected['type'], $lookahead->type); + self::assertEquals($expected['position'], $lookahead->position); } self::assertFalse($lexer->moveNext()); @@ -155,9 +156,9 @@ public function testScannerTokenizesDocBlockWhitInvalidIdentifier(): void foreach ($tokens as $expected) { $lexer->moveNext(); $lookahead = $lexer->lookahead; - self::assertEquals($expected['value'], $lookahead['value']); - self::assertEquals($expected['type'], $lookahead['type']); - self::assertEquals($expected['position'], $lookahead['position']); + self::assertEquals($expected['value'], $lookahead->value); + self::assertEquals($expected['type'], $lookahead->type); + self::assertEquals($expected['position'], $lookahead->position); } self::assertFalse($lexer->moveNext()); @@ -172,7 +173,7 @@ public function testWithinDoubleQuotesVeryVeryLongStringWillNotOverflowPregSplit $lexer->setInput('"' . str_repeat('.', 20240) . '"'); - self::assertIsArray($lexer->glimpse()); + self::assertInstanceOf(Token::class, $lexer->glimpse()); } /** @@ -216,9 +217,9 @@ public function testRecognizesDoubleQuotesEscapeSequence(): void foreach ($tokens as $expected) { $lexer->moveNext(); $lookahead = $lexer->lookahead; - self::assertEquals($expected['value'], $lookahead['value']); - self::assertEquals($expected['type'], $lookahead['type']); - self::assertEquals($expected['position'], $lookahead['position']); + self::assertEquals($expected['value'], $lookahead->value); + self::assertEquals($expected['type'], $lookahead->type); + self::assertEquals($expected['position'], $lookahead->position); } self::assertFalse($lexer->moveNext()); @@ -296,9 +297,9 @@ private function expectDocblockTokens(string $docBlock, array $expectedTokens): $lookahead = $lexer->lookahead; $actualTokens[] = [ - 'value' => $lookahead['value'], - 'type' => $lookahead['type'], - 'position' => $lookahead['position'], + 'value' => $lookahead->value, + 'type' => $lookahead->type, + 'position' => $lookahead->position, ]; } From 6d4929214d8593ed80364267d6665edd70c2ecbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 18 Dec 2022 20:08:04 +0100 Subject: [PATCH 20/25] Remove deprecated APIs (#468) --- UPGRADE.md | 5 +- docs/en/annotations.rst | 65 +--- docs/en/custom.rst | 16 +- docs/en/index.rst | 8 - .../Common/Annotations/AnnotationRegistry.php | 149 +-------- .../Common/Annotations/CachedReader.php | 266 --------------- .../Common/Annotations/FileCacheReader.php | 315 ------------------ .../Annotations/AnnotationRegistryTest.php | 211 ------------ .../Common/Annotations/CachedReaderTest.php | 292 ---------------- .../Common/Annotations/DocParserTest.php | 6 - .../Annotations/FileCacheReaderTest.php | 56 ---- tests/Doctrine/Tests/TestInit.php | 7 - 12 files changed, 14 insertions(+), 1382 deletions(-) delete mode 100644 lib/Doctrine/Common/Annotations/CachedReader.php delete mode 100644 lib/Doctrine/Common/Annotations/FileCacheReader.php delete mode 100644 tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php delete mode 100644 tests/Doctrine/Tests/Common/Annotations/FileCacheReaderTest.php diff --git a/UPGRADE.md b/UPGRADE.md index ae9509cd5..de29a33cd 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,6 +1,9 @@ # Upgrade from 1.0.x to 2.0.x -`DocLexer::peek()` and `DocLexer::glimpse` now return +- `DocLexer::peek()` and `DocLexer::glimpse` now return `Doctrine\Common\Lexer\Token` objects. When using `doctrine/lexer` 2, these implement `ArrayAccess` as a way for you to still be able to treat them as arrays in some ways. +- `CachedReader` and `FileCacheReader` have been removed. +- `AnnotationRegistry` method related to registering annotations instead of + using autoloading have been removed. diff --git a/docs/en/annotations.rst b/docs/en/annotations.rst index 2c3c42865..d32b15d12 100644 --- a/docs/en/annotations.rst +++ b/docs/en/annotations.rst @@ -79,23 +79,11 @@ with Doctrine Annotations requires this setup: .. code-block:: php use Doctrine\Common\Annotations\AnnotationReader; - use Doctrine\Common\Annotations\AnnotationRegistry; - - AnnotationRegistry::registerFile("/path/to/doctrine/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php"); - AnnotationRegistry::registerAutoloadNamespace("Symfony\Component\Validator\Constraint", "/path/to/symfony/src"); - AnnotationRegistry::registerAutoloadNamespace("MyProject\Annotations", "/path/to/myproject/src"); $reader = new AnnotationReader(); AnnotationReader::addGlobalIgnoredName('dummy'); -The second block with the annotation registry calls registers all the -three different annotation namespaces that are used. -Doctrine Annotations saves all its annotations in a single file, that is -why ``AnnotationRegistry#registerFile`` is used in contrast to -``AnnotationRegistry#registerAutoloadNamespace`` which creates a PSR-0 -compatible loading mechanism for class to file names. - -In the third block, we create the actual ``AnnotationReader`` instance. +We create the actual ``AnnotationReader`` instance. Note that we also add ``dummy`` to the global list of ignored annotations for which we do not throw exceptions. Setting this is necessary in our example case, otherwise ``@dummy`` would trigger an @@ -165,57 +153,6 @@ class name you can wrap the reader in an ``IndexedReader``: indexed or numeric keys, otherwise your code may experience failures due to caching in a numerical or indexed format. -Registering Annotations -~~~~~~~~~~~~~~~~~~~~~~~ - -As explained in the introduction, Doctrine Annotations uses its own -autoloading mechanism to determine if a given annotation has a -corresponding PHP class that can be autoloaded. For annotation -autoloading you have to configure the -``Doctrine\Common\Annotations\AnnotationRegistry``. There are three -different mechanisms to configure annotation autoloading: - -- Calling ``AnnotationRegistry#registerFile($file)`` to register a file - that contains one or more annotation classes. -- Calling ``AnnotationRegistry#registerNamespace($namespace, $dirs = - null)`` to register that the given namespace contains annotations and - that their base directory is located at the given $dirs or in the - include path if ``NULL`` is passed. The given directories should *NOT* - be the directory where classes of the namespace are in, but the base - directory of the root namespace. The AnnotationRegistry uses a - namespace to directory separator approach to resolve the correct path. -- Calling ``AnnotationRegistry#registerLoader($callable)`` to register - an autoloader callback. The callback accepts the class as first and - only parameter and has to return ``true`` if the corresponding file - was found and included. - -.. note:: - - Loaders have to fail silently, if a class is not found even if it - matches for example the namespace prefix of that loader. Never is a - loader to throw a warning or exception if the loading failed - otherwise parsing doc block annotations will become a huge pain. - -A sample loader callback could look like: - -.. code-block:: php - - use Doctrine\Common\Annotations\AnnotationRegistry; - use Symfony\Component\ClassLoader\UniversalClassLoader; - - AnnotationRegistry::registerLoader(function($class) { - $file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php"; - - if (file_exists("/my/base/path/" . $file)) { - // file_exists() makes sure that the loader fails silently - require "/my/base/path/" . $file; - } - }); - - $loader = new UniversalClassLoader(); - AnnotationRegistry::registerLoader(array($loader, "loadClass")); - - Ignoring missing exceptions ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/en/custom.rst b/docs/en/custom.rst index e8f79af71..300516625 100644 --- a/docs/en/custom.rst +++ b/docs/en/custom.rst @@ -2,7 +2,7 @@ Custom Annotation Classes ========================= If you want to define your own annotations, you just have to group them -in a namespace and register this namespace in the ``AnnotationRegistry``. +in a namespace. Annotation classes have to contain a class-level docblock with the text ``@Annotation``: @@ -58,10 +58,10 @@ Optional: Constructors with Named Parameters Starting with Annotations v1.11 a new annotation instantiation strategy is available that aims at compatibility of Annotation classes with the PHP 8 -attribute feature. You need to declare a constructor with regular parameter +attribute feature. You need to declare a constructor with regular parameter names that match the named arguments in the annotation syntax. -To enable this feature, you can tag your annotation class with +To enable this feature, you can tag your annotation class with ``@NamedArgumentConstructor`` (available from v1.12) or implement the ``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation`` interface (available from v1.11 and deprecated as of v1.12). @@ -75,8 +75,8 @@ Usage with the ``@NamedArgumentConstructor`` tag namespace MyCompany\Annotations; - /** - * @Annotation + /** + * @Annotation * @NamedArgumentConstructor */ class Bar implements NamedArgumentConstructorAnnotation @@ -99,8 +99,8 @@ you can simplify this to: namespace MyCompany\Annotations; - /** - * @Annotation + /** + * @Annotation * @NamedArgumentConstructor */ class Bar implements NamedArgumentConstructorAnnotation @@ -109,7 +109,7 @@ you can simplify this to: } -Usage with the +Usage with the ``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation`` interface (v1.11, deprecated as of v1.12): .. code-block:: php diff --git a/docs/en/index.rst b/docs/en/index.rst index 7caffb50a..a6e338333 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -73,10 +73,6 @@ annotations of a class. A common one is .. code-block:: php use Doctrine\Common\Annotations\AnnotationReader; - use Doctrine\Common\Annotations\AnnotationRegistry; - - // Deprecated and will be removed in 2.0 but currently needed - AnnotationRegistry::registerLoader('class_exists'); $reflectionClass = new ReflectionClass(Foo::class); $property = $reflectionClass->getProperty('bar'); @@ -89,10 +85,6 @@ annotations of a class. A common one is echo $myAnnotation->myProperty; // result: "value" -Note that ``AnnotationRegistry::registerLoader('class_exists')`` only works -if you already have an autoloader configured (i.e. composer autoloader). -Otherwise, :ref:`please take a look to the other annotation autoload mechanisms `. - A reader has multiple methods to access the annotations of a class or function. diff --git a/lib/Doctrine/Common/Annotations/AnnotationRegistry.php b/lib/Doctrine/Common/Annotations/AnnotationRegistry.php index 259d497dd..290e60aba 100644 --- a/lib/Doctrine/Common/Annotations/AnnotationRegistry.php +++ b/lib/Doctrine/Common/Annotations/AnnotationRegistry.php @@ -3,37 +3,10 @@ namespace Doctrine\Common\Annotations; use function array_key_exists; -use function array_merge; use function class_exists; -use function in_array; -use function is_file; -use function str_replace; -use function stream_resolve_include_path; -use function strpos; - -use const DIRECTORY_SEPARATOR; final class AnnotationRegistry { - /** - * A map of namespaces to use for autoloading purposes based on a PSR-0 convention. - * - * Contains the namespace as key and an array of directories as value. If the value is NULL - * the include path is used for checking for the corresponding file. - * - * This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own. - * - * @var string[][]|string[]|null[] - */ - private static $autoloadNamespaces = []; - - /** - * A map of autoloader callables. - * - * @var callable[] - */ - private static $loaders = []; - /** * An array of classes which cannot be found * @@ -41,93 +14,9 @@ final class AnnotationRegistry */ private static $failedToAutoload = []; - /** - * Whenever registerFile() was used. Disables use of standard autoloader. - * - * @var bool - */ - private static $registerFileUsed = false; - public static function reset(): void { - self::$autoloadNamespaces = []; - self::$loaders = []; - self::$failedToAutoload = []; - self::$registerFileUsed = false; - } - - /** - * Registers file. - * - * @deprecated This method is deprecated and will be removed in - * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. - */ - public static function registerFile(string $file): void - { - self::$registerFileUsed = true; - - require_once $file; - } - - /** - * Adds a namespace with one or many directories to look for files or null for the include path. - * - * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. - * - * @deprecated This method is deprecated and will be removed in - * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. - * - * @phpstan-param string|list|null $dirs - */ - public static function registerAutoloadNamespace(string $namespace, $dirs = null): void - { - self::$autoloadNamespaces[$namespace] = $dirs; - } - - /** - * Registers multiple namespaces. - * - * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. - * - * @deprecated This method is deprecated and will be removed in - * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. - * - * @param string[][]|string[]|null[] $namespaces indexed by namespace name - */ - public static function registerAutoloadNamespaces(array $namespaces): void - { - self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces); - } - - /** - * Registers an autoloading callable for annotations, much like spl_autoload_register(). - * - * NOTE: These class loaders HAVE to be silent when a class was not found! - * IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class. - * - * @deprecated This method is deprecated and will be removed in - * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. - */ - public static function registerLoader(callable $callable): void - { - // Reset our static cache now that we have a new loader to work with self::$failedToAutoload = []; - self::$loaders[] = $callable; - } - - /** - * Registers an autoloading callable for annotations, if it is not already registered - * - * @deprecated This method is deprecated and will be removed in - * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. - */ - public static function registerUniqueLoader(callable $callable): void - { - if (in_array($callable, self::$loaders, true)) { - return; - } - - self::registerLoader($callable); } /** @@ -143,43 +32,7 @@ public static function loadAnnotationClass(string $class): bool return false; } - foreach (self::$autoloadNamespaces as $namespace => $dirs) { - if (strpos($class, $namespace) !== 0) { - continue; - } - - $file = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php'; - - if ($dirs === null) { - $path = stream_resolve_include_path($file); - if ($path) { - require $path; - - return true; - } - } else { - foreach ((array) $dirs as $dir) { - if (is_file($dir . DIRECTORY_SEPARATOR . $file)) { - require $dir . DIRECTORY_SEPARATOR . $file; - - return true; - } - } - } - } - - foreach (self::$loaders as $loader) { - if ($loader($class) === true) { - return true; - } - } - - if ( - self::$loaders === [] && - self::$autoloadNamespaces === [] && - self::$registerFileUsed === false && - class_exists($class) - ) { + if (class_exists($class)) { return true; } diff --git a/lib/Doctrine/Common/Annotations/CachedReader.php b/lib/Doctrine/Common/Annotations/CachedReader.php deleted file mode 100644 index 85dbefab5..000000000 --- a/lib/Doctrine/Common/Annotations/CachedReader.php +++ /dev/null @@ -1,266 +0,0 @@ -> */ - private $loadedAnnotations = []; - - /** @var int[] */ - private $loadedFilemtimes = []; - - /** @param bool $debug */ - public function __construct(Reader $reader, Cache $cache, $debug = false) - { - $this->delegate = $reader; - $this->cache = $cache; - $this->debug = (bool) $debug; - } - - /** - * {@inheritDoc} - */ - public function getClassAnnotations(ReflectionClass $class) - { - $cacheKey = $class->getName(); - - if (isset($this->loadedAnnotations[$cacheKey])) { - return $this->loadedAnnotations[$cacheKey]; - } - - $annots = $this->fetchFromCache($cacheKey, $class); - if ($annots === false) { - $annots = $this->delegate->getClassAnnotations($class); - $this->saveToCache($cacheKey, $annots); - } - - return $this->loadedAnnotations[$cacheKey] = $annots; - } - - /** - * {@inheritDoc} - */ - public function getClassAnnotation(ReflectionClass $class, $annotationName) - { - foreach ($this->getClassAnnotations($class) as $annot) { - if ($annot instanceof $annotationName) { - return $annot; - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - public function getPropertyAnnotations(ReflectionProperty $property) - { - $class = $property->getDeclaringClass(); - $cacheKey = $class->getName() . '$' . $property->getName(); - - if (isset($this->loadedAnnotations[$cacheKey])) { - return $this->loadedAnnotations[$cacheKey]; - } - - $annots = $this->fetchFromCache($cacheKey, $class); - if ($annots === false) { - $annots = $this->delegate->getPropertyAnnotations($property); - $this->saveToCache($cacheKey, $annots); - } - - return $this->loadedAnnotations[$cacheKey] = $annots; - } - - /** - * {@inheritDoc} - */ - public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) - { - foreach ($this->getPropertyAnnotations($property) as $annot) { - if ($annot instanceof $annotationName) { - return $annot; - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - public function getMethodAnnotations(ReflectionMethod $method) - { - $class = $method->getDeclaringClass(); - $cacheKey = $class->getName() . '#' . $method->getName(); - - if (isset($this->loadedAnnotations[$cacheKey])) { - return $this->loadedAnnotations[$cacheKey]; - } - - $annots = $this->fetchFromCache($cacheKey, $class); - if ($annots === false) { - $annots = $this->delegate->getMethodAnnotations($method); - $this->saveToCache($cacheKey, $annots); - } - - return $this->loadedAnnotations[$cacheKey] = $annots; - } - - /** - * {@inheritDoc} - */ - public function getMethodAnnotation(ReflectionMethod $method, $annotationName) - { - foreach ($this->getMethodAnnotations($method) as $annot) { - if ($annot instanceof $annotationName) { - return $annot; - } - } - - return null; - } - - /** - * Clears loaded annotations. - * - * @return void - */ - public function clearLoadedAnnotations() - { - $this->loadedAnnotations = []; - $this->loadedFilemtimes = []; - } - - /** - * Fetches a value from the cache. - * - * @param string $cacheKey The cache key. - * - * @return mixed The cached value or false when the value is not in cache. - */ - private function fetchFromCache($cacheKey, ReflectionClass $class) - { - $data = $this->cache->fetch($cacheKey); - if ($data !== false) { - if (! $this->debug || $this->isCacheFresh($cacheKey, $class)) { - return $data; - } - } - - return false; - } - - /** - * Saves a value to the cache. - * - * @param string $cacheKey The cache key. - * @param mixed $value The value. - * - * @return void - */ - private function saveToCache($cacheKey, $value) - { - $this->cache->save($cacheKey, $value); - if (! $this->debug) { - return; - } - - $this->cache->save('[C]' . $cacheKey, time()); - } - - /** - * Checks if the cache is fresh. - * - * @param string $cacheKey - * - * @return bool - */ - private function isCacheFresh($cacheKey, ReflectionClass $class) - { - $lastModification = $this->getLastModification($class); - if ($lastModification === 0) { - return true; - } - - return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification; - } - - /** - * Returns the time the class was last modified, testing traits and parents - */ - private function getLastModification(ReflectionClass $class): int - { - $filename = $class->getFileName(); - - if (isset($this->loadedFilemtimes[$filename])) { - return $this->loadedFilemtimes[$filename]; - } - - $parent = $class->getParentClass(); - - $lastModification = max(array_merge( - [$filename ? filemtime($filename) : 0], - array_map(function (ReflectionClass $reflectionTrait): int { - return $this->getTraitLastModificationTime($reflectionTrait); - }, $class->getTraits()), - array_map(function (ReflectionClass $class): int { - return $this->getLastModification($class); - }, $class->getInterfaces()), - $parent ? [$this->getLastModification($parent)] : [] - )); - - assert($lastModification !== false); - - return $this->loadedFilemtimes[$filename] = $lastModification; - } - - private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int - { - $fileName = $reflectionTrait->getFileName(); - - if (isset($this->loadedFilemtimes[$fileName])) { - return $this->loadedFilemtimes[$fileName]; - } - - $lastModificationTime = max(array_merge( - [$fileName ? filemtime($fileName) : 0], - array_map(function (ReflectionClass $reflectionTrait): int { - return $this->getTraitLastModificationTime($reflectionTrait); - }, $reflectionTrait->getTraits()) - )); - - assert($lastModificationTime !== false); - - return $this->loadedFilemtimes[$fileName] = $lastModificationTime; - } -} diff --git a/lib/Doctrine/Common/Annotations/FileCacheReader.php b/lib/Doctrine/Common/Annotations/FileCacheReader.php deleted file mode 100644 index 6c6c22c3a..000000000 --- a/lib/Doctrine/Common/Annotations/FileCacheReader.php +++ /dev/null @@ -1,315 +0,0 @@ -> */ - private $loadedAnnotations = []; - - /** @var array */ - private $classNameHashes = []; - - /** @var int */ - private $umask; - - /** - * @param string $cacheDir - * @param bool $debug - * @param int $umask - * - * @throws InvalidArgumentException - */ - public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002) - { - if (! is_int($umask)) { - throw new InvalidArgumentException(sprintf( - 'The parameter umask must be an integer, was: %s', - gettype($umask) - )); - } - - $this->reader = $reader; - $this->umask = $umask; - - if (! is_dir($cacheDir) && ! @mkdir($cacheDir, 0777 & (~$this->umask), true)) { - throw new InvalidArgumentException(sprintf( - 'The directory "%s" does not exist and could not be created.', - $cacheDir - )); - } - - $this->dir = rtrim($cacheDir, '\\/'); - $this->debug = $debug; - } - - /** - * {@inheritDoc} - */ - public function getClassAnnotations(ReflectionClass $class) - { - if (! isset($this->classNameHashes[$class->name])) { - $this->classNameHashes[$class->name] = sha1($class->name); - } - - $key = $this->classNameHashes[$class->name]; - - if (isset($this->loadedAnnotations[$key])) { - return $this->loadedAnnotations[$key]; - } - - $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php'; - if (! is_file($path)) { - $annot = $this->reader->getClassAnnotations($class); - $this->saveCacheFile($path, $annot); - - return $this->loadedAnnotations[$key] = $annot; - } - - $filename = $class->getFilename(); - if ( - $this->debug - && $filename !== false - && filemtime($path) < filemtime($filename) - ) { - @unlink($path); - - $annot = $this->reader->getClassAnnotations($class); - $this->saveCacheFile($path, $annot); - - return $this->loadedAnnotations[$key] = $annot; - } - - return $this->loadedAnnotations[$key] = include $path; - } - - /** - * {@inheritDoc} - */ - public function getPropertyAnnotations(ReflectionProperty $property) - { - $class = $property->getDeclaringClass(); - if (! isset($this->classNameHashes[$class->name])) { - $this->classNameHashes[$class->name] = sha1($class->name); - } - - $key = $this->classNameHashes[$class->name] . '$' . $property->getName(); - - if (isset($this->loadedAnnotations[$key])) { - return $this->loadedAnnotations[$key]; - } - - $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php'; - if (! is_file($path)) { - $annot = $this->reader->getPropertyAnnotations($property); - $this->saveCacheFile($path, $annot); - - return $this->loadedAnnotations[$key] = $annot; - } - - $filename = $class->getFilename(); - if ( - $this->debug - && $filename !== false - && filemtime($path) < filemtime($filename) - ) { - @unlink($path); - - $annot = $this->reader->getPropertyAnnotations($property); - $this->saveCacheFile($path, $annot); - - return $this->loadedAnnotations[$key] = $annot; - } - - return $this->loadedAnnotations[$key] = include $path; - } - - /** - * {@inheritDoc} - */ - public function getMethodAnnotations(ReflectionMethod $method) - { - $class = $method->getDeclaringClass(); - if (! isset($this->classNameHashes[$class->name])) { - $this->classNameHashes[$class->name] = sha1($class->name); - } - - $key = $this->classNameHashes[$class->name] . '#' . $method->getName(); - - if (isset($this->loadedAnnotations[$key])) { - return $this->loadedAnnotations[$key]; - } - - $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php'; - if (! is_file($path)) { - $annot = $this->reader->getMethodAnnotations($method); - $this->saveCacheFile($path, $annot); - - return $this->loadedAnnotations[$key] = $annot; - } - - $filename = $class->getFilename(); - if ( - $this->debug - && $filename !== false - && filemtime($path) < filemtime($filename) - ) { - @unlink($path); - - $annot = $this->reader->getMethodAnnotations($method); - $this->saveCacheFile($path, $annot); - - return $this->loadedAnnotations[$key] = $annot; - } - - return $this->loadedAnnotations[$key] = include $path; - } - - /** - * Saves the cache file. - * - * @param string $path - * @param mixed $data - * - * @return void - */ - private function saveCacheFile($path, $data) - { - if (! is_writable($this->dir)) { - throw new InvalidArgumentException(sprintf( - <<<'EXCEPTION' -The directory "%s" is not writable. Both the webserver and the console user need access. -You can manage access rights for multiple users with "chmod +a". -If your system does not support this, check out the acl package., -EXCEPTION - , - $this->dir - )); - } - - $tempfile = tempnam($this->dir, uniqid('', true)); - - if ($tempfile === false) { - throw new RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir)); - } - - @chmod($tempfile, 0666 & (~$this->umask)); - - $written = file_put_contents( - $tempfile, - 'umask)); - - if (rename($tempfile, $path) === false) { - @unlink($tempfile); - - throw new RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path)); - } - } - - /** - * {@inheritDoc} - */ - public function getClassAnnotation(ReflectionClass $class, $annotationName) - { - $annotations = $this->getClassAnnotations($class); - - foreach ($annotations as $annotation) { - if ($annotation instanceof $annotationName) { - return $annotation; - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - public function getMethodAnnotation(ReflectionMethod $method, $annotationName) - { - $annotations = $this->getMethodAnnotations($method); - - foreach ($annotations as $annotation) { - if ($annotation instanceof $annotationName) { - return $annotation; - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) - { - $annotations = $this->getPropertyAnnotations($property); - - foreach ($annotations as $annotation) { - if ($annotation instanceof $annotationName) { - return $annotation; - } - } - - return null; - } - - /** - * Clears loaded annotations. - * - * @return void - */ - public function clearLoadedAnnotations() - { - $this->loadedAnnotations = []; - } -} diff --git a/tests/Doctrine/Tests/Common/Annotations/AnnotationRegistryTest.php b/tests/Doctrine/Tests/Common/Annotations/AnnotationRegistryTest.php index 5fe6c733d..2a9bf27ea 100644 --- a/tests/Doctrine/Tests/Common/Annotations/AnnotationRegistryTest.php +++ b/tests/Doctrine/Tests/Common/Annotations/AnnotationRegistryTest.php @@ -4,202 +4,10 @@ use Doctrine\Common\Annotations\AnnotationRegistry; use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\CanBeAutoLoaded; -use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\LoadedUsingRegisterFile; -use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\ShouldNeverBeLoaded; use PHPUnit\Framework\TestCase; -use ReflectionProperty; -use TypeError; - -use function random_int; class AnnotationRegistryTest extends TestCase { - /** @var string */ - protected $class = AnnotationRegistry::class; - - /** - * @runInSeparateProcess - */ - public function testReset(): void - { - $data = ['foo' => 'bar']; - - $this->setStaticField($this->class, 'autoloadNamespaces', $data); - $this->setStaticField($this->class, 'loaders', $data); - - self::assertSame($data, $this->getStaticField($this->class, 'autoloadNamespaces')); - self::assertSame($data, $this->getStaticField($this->class, 'loaders')); - - AnnotationRegistry::reset(); - - self::assertEmpty($this->getStaticField($this->class, 'autoloadNamespaces')); - self::assertEmpty($this->getStaticField($this->class, 'loaders')); - } - - /** - * @runInSeparateProcess - */ - public function testRegisterAutoloadNamespaces(): void - { - $this->setStaticField($this->class, 'autoloadNamespaces', ['foo' => 'bar']); - - AnnotationRegistry::registerAutoloadNamespaces(['test' => 'bar']); - self::assertSame(['foo' => 'bar', 'test' => 'bar'], $this->getStaticField($this->class, 'autoloadNamespaces')); - } - - /** - * @runInSeparateProcess - */ - public function testRegisterLoaderNoCallable(): void - { - $this->expectException(TypeError::class); - - AnnotationRegistry::registerLoader('test' . random_int(10, 10000)); - } - - /** - * @param mixed[] $value - */ - protected function setStaticField(string $class, string $field, array $value): void - { - $reflection = new ReflectionProperty($class, $field); - - $reflection->setAccessible(true); - $reflection->setValue(null, $value); - } - - /** - * @return mixed - */ - protected function getStaticField(string $class, string $field) - { - $reflection = new ReflectionProperty($class, $field); - - $reflection->setAccessible(true); - - return $reflection->getValue(); - } - - /** - * @runInSeparateProcess - */ - public function testStopCallingLoadersIfClassIsNotFound(): void - { - AnnotationRegistry::reset(); - $i = 0; - $autoLoader = static function () use (&$i): bool { - $i += 1; - - return false; - }; - AnnotationRegistry::registerLoader($autoLoader); - AnnotationRegistry::loadAnnotationClass('unloadableClass'); - AnnotationRegistry::loadAnnotationClass('unloadableClass'); - AnnotationRegistry::loadAnnotationClass('unloadableClass'); - self::assertSame(1, $i, 'Autoloader should only be called once'); - } - - /** - * @runInSeparateProcess - */ - public function testStopCallingLoadersAfterClassIsFound(): void - { - $className = 'autoloadedClass' . random_int(10, 100000); - AnnotationRegistry::reset(); - $i = 0; - $autoLoader = static function () use (&$i, $className): bool { - eval('class ' . $className . ' {}'); - $i += 1; - - return true; - }; - AnnotationRegistry::registerLoader($autoLoader); - AnnotationRegistry::loadAnnotationClass($className); - AnnotationRegistry::loadAnnotationClass($className); - AnnotationRegistry::loadAnnotationClass($className); - self::assertSame(1, $i, 'Autoloader should only be called once'); - } - - /** - * @runInSeparateProcess - */ - public function testAddingANewLoaderClearsTheCache(): void - { - $failures = 0; - $failingLoader = static function () use (&$failures): bool { - $failures += 1; - - return false; - }; - - AnnotationRegistry::reset(); - AnnotationRegistry::registerLoader($failingLoader); - - self::assertSame(0, $failures); - - AnnotationRegistry::loadAnnotationClass('unloadableClass'); - - self::assertSame(1, $failures); - - AnnotationRegistry::loadAnnotationClass('unloadableClass'); - - self::assertSame(1, $failures); - - AnnotationRegistry::registerLoader(static function (): bool { - return false; - }); - AnnotationRegistry::loadAnnotationClass('unloadableClass'); - - self::assertSame(2, $failures); - } - - /** - * @runInSeparateProcess - */ - public function testResetClearsRegisteredAutoloaderFailures(): void - { - $failures = 0; - $failingLoader = static function () use (&$failures): bool { - $failures += 1; - - return false; - }; - - AnnotationRegistry::reset(); - AnnotationRegistry::registerLoader($failingLoader); - - self::assertSame(0, $failures); - - AnnotationRegistry::loadAnnotationClass('unloadableClass'); - - self::assertSame(1, $failures); - - AnnotationRegistry::loadAnnotationClass('unloadableClass'); - - self::assertSame(1, $failures); - - AnnotationRegistry::reset(); - AnnotationRegistry::registerLoader($failingLoader); - AnnotationRegistry::loadAnnotationClass('unloadableClass'); - - self::assertSame(2, $failures); - } - - /** - * @runInSeparateProcess - */ - public function testRegisterLoaderIfNotExistsOnlyRegisteresSameLoaderOnce(): void - { - $className = 'autoloadedClassThatDoesNotExist'; - AnnotationRegistry::reset(); - $autoLoader = self::createPartialMock(Autoloader::class, ['__invoke']); - $autoLoader->expects($this->once())->method('__invoke'); - AnnotationRegistry::registerUniqueLoader($autoLoader); - AnnotationRegistry::registerUniqueLoader($autoLoader); - AnnotationRegistry::loadAnnotationClass($className); - AnnotationRegistry::loadAnnotationClass($className); - } - /** * @runInSeparateProcess */ @@ -209,23 +17,4 @@ public function testClassExistsFallback(): void self::assertTrue(AnnotationRegistry::loadAnnotationClass(CanBeAutoLoaded::class)); } - - /** - * @runInSeparateProcess - */ - public function testClassExistsFallbackNotUsedWhenRegisterFileUsed(): void - { - AnnotationRegistry::reset(); - AnnotationRegistry::registerFile(__DIR__ . '/Fixtures/Annotation/LoadedUsingRegisterFile.php'); - - self::assertTrue(AnnotationRegistry::loadAnnotationClass(LoadedUsingRegisterFile::class)); - self::assertFalse(AnnotationRegistry::loadAnnotationClass(ShouldNeverBeLoaded::class)); - } -} - -class Autoloader -{ - public function __invoke(): void - { - } } diff --git a/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php b/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php deleted file mode 100644 index 63537e0d4..000000000 --- a/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php +++ /dev/null @@ -1,292 +0,0 @@ -markTestSkipped('Cannot test deprecated cached reader without doctrine/cache 1.x'); - } - - parent::setup(); - } - - public function testIgnoresStaleCache(): void - { - $cache = time() - 10; - touch(__DIR__ . '/Fixtures/Controller.php', $cache + 10); - - $this->doTestCacheStale(Fixtures\Controller::class, $cache); - } - - /** - * @group 62 - */ - public function testIgnoresStaleCacheWithParentClass(): void - { - $cache = time() - 10; - touch(__DIR__ . '/Fixtures/ControllerWithParentClass.php', $cache - 10); - touch(__DIR__ . '/Fixtures/AbstractController.php', $cache + 10); - - $this->doTestCacheStale(Fixtures\ControllerWithParentClass::class, $cache); - } - - /** - * @group 62 - */ - public function testIgnoresStaleCacheWithTraits(): void - { - $cache = time() - 10; - touch(__DIR__ . '/Fixtures/ControllerWithTrait.php', $cache - 10); - touch(__DIR__ . '/Fixtures/Traits/SecretRouteTrait.php', $cache + 10); - - $this->doTestCacheStale(Fixtures\ControllerWithTrait::class, $cache); - } - - /** - * @group 62 - */ - public function testIgnoresStaleCacheWithTraitsThatUseOtherTraits(): void - { - $cache = time() - 10; - - touch(__DIR__ . '/Fixtures/ClassThatUsesTraitThatUsesAnotherTrait.php', $cache - 10); - touch(__DIR__ . '/Fixtures/Traits/EmptyTrait.php', $cache + 10); - - $this->doTestCacheStale( - Fixtures\ClassThatUsesTraitThatUsesAnotherTrait::class, - $cache - ); - } - - /** - * @group 62 - */ - public function testIgnoresStaleCacheWithInterfacesThatExtendOtherInterfaces(): void - { - $cache = time() - 10; - - touch(__DIR__ . '/Fixtures/InterfaceThatExtendsAnInterface.php', $cache - 10); - touch(__DIR__ . '/Fixtures/EmptyInterface.php', $cache + 10); - - $this->doTestCacheStale( - Fixtures\InterfaceThatExtendsAnInterface::class, - $cache - ); - } - - /** - * @group 62 - * @group 105 - */ - public function testUsesFreshCacheWithTraitsThatUseOtherTraits(): void - { - $cacheTime = time(); - - touch(__DIR__ . '/Fixtures/ClassThatUsesTraitThatUsesAnotherTrait.php', $cacheTime - 10); - touch(__DIR__ . '/Fixtures/Traits/EmptyTrait.php', $cacheTime - 10); - - $this->doTestCacheFresh( - 'Doctrine\Tests\Common\Annotations\Fixtures\ClassThatUsesTraitThatUsesAnotherTrait', - $cacheTime - ); - } - - /** - * @group 62 - */ - public function testPurgeLoadedAnnotations(): void - { - $cache = time() - 10; - - touch(__DIR__ . '/Fixtures/ClassThatUsesTraitThatUsesAnotherTrait.php', $cache - 10); - touch(__DIR__ . '/Fixtures/Traits/EmptyTrait.php', $cache + 10); - - $reader = $this->doTestCacheStale( - Fixtures\ClassThatUsesTraitThatUsesAnotherTrait::class, - $cache - ); - - $classReader = new ReflectionClass(CachedReader::class); - - $loadedAnnotationsProperty = $classReader->getProperty('loadedAnnotations'); - $loadedAnnotationsProperty->setAccessible(true); - $this->assertCount(1, $loadedAnnotationsProperty->getValue($reader)); - - $loadedFilemtimesProperty = $classReader->getProperty('loadedFilemtimes'); - $loadedFilemtimesProperty->setAccessible(true); - $this->assertCount(3, $loadedFilemtimesProperty->getValue($reader)); - - $reader->clearLoadedAnnotations(); - - $this->assertCount(0, $loadedAnnotationsProperty->getValue($reader)); - $this->assertCount(0, $loadedFilemtimesProperty->getValue($reader)); - } - - /** - * As there is a cache on loadedAnnotations, we need to test two different - * method's annotations of the same file - * - * We test four things - * 1. we load the file (and its filemtime) for method1 annotation with fresh cache - * 2. we load the file for method2 with stale cache => but still no save, because seen as fresh - * 3. we purge loaded annotations and filemtime - * 4. same as 2, but this time without filemtime cache, so file seen as stale and new cache is saved - * - * @group 62 - * @group 105 - */ - public function testAvoidCallingFilemtimeTooMuch(): void - { - $this->markTestSkipped('Skipped until further investigation'); - - $className = ClassThatUsesTraitThatUsesAnotherTraitWithMethods::class; - $cacheKey = $className; - $cacheTime = time() - 10; - - $cacheKeyMethod1 = $cacheKey . '#method1'; - $cacheKeyMethod2 = $cacheKey . '#method2'; - - $route1 = new Route(); - $route1->pattern = '/someprefix'; - $route2 = new Route(); - $route2->pattern = '/someotherprefix'; - - $cache = $this->createMock('Doctrine\Common\Cache\Cache'); - assert($cache instanceof Cache && $cache instanceof MockObject); - - $cache - ->expects($this->exactly(6)) - ->method('fetch') - ->withConsecutive( - // first pass => cache ok for method 1 - // we load annotations AND filemtimes for this file - [$this->equalTo($cacheKeyMethod1)], - [$this->equalTo('[C]' . $cacheKeyMethod1)], - // second pass => cache ok for method 2 - // filemtime is seen as fresh even if it's not - [$this->equalTo($cacheKeyMethod2)], - [$this->equalTo('[C]' . $cacheKeyMethod2)], - // third pass => cache stale for method 2 - // filemtime is seen as not fresh => we save - [$this->equalTo($cacheKeyMethod2)], - [$this->equalTo('[C]' . $cacheKeyMethod2)] - ) - ->willReturnOnConsecutiveCalls( - [$route1], // Result was cached, but there was an annotation; - $cacheTime, - [$route2], // Result was cached, but there was an annotation; - $cacheTime, - [$route2], // Result was cached, but there was an annotation; - $cacheTime - ); - - $cache - ->expects($this->exactly(2)) - ->method('save') - ->withConsecutive( - [$this->equalTo($cacheKeyMethod2)], - [$this->equalTo('[C]' . $cacheKeyMethod2)] - ); - - $reader = new CachedReader(new AnnotationReader(), $cache, true); - - touch(__DIR__ . '/Fixtures/ClassThatUsesTraitThatUsesAnotherTraitWithMethods.php', $cacheTime - 20); - touch(__DIR__ . '/Fixtures/Traits/EmptyTrait.php', $cacheTime - 20); - $this->assertEquals([$route1], $reader->getMethodAnnotations(new ReflectionMethod($className, 'method1'))); - - // only filemtime changes, but not cleared => no change - touch(__DIR__ . '/Fixtures/ClassThatUsesTraitThatUsesAnotherTrait.php', $cacheTime + 5); - touch(__DIR__ . '/Fixtures/Traits/EmptyTrait.php', $cacheTime + 5); - $this->assertEquals([$route2], $reader->getMethodAnnotations(new ReflectionMethod($className, 'method2'))); - - $reader->clearLoadedAnnotations(); - $this->assertEquals([$route2], $reader->getMethodAnnotations(new ReflectionMethod($className, 'method2'))); - } - - protected function doTestCacheStale(string $className, int $lastCacheModification): CachedReader - { - $cacheKey = $className; - - $cache = $this->createMock(Cache::class); - $cache - ->expects($this->exactly(2)) - ->method('fetch') - ->withConsecutive( - [$this->equalTo($cacheKey)], - [$this->equalTo('[C]' . $cacheKey)] - ) - ->willReturnOnConsecutiveCalls( - [], // Result was cached, but there was no annotation - $lastCacheModification - ); - $cache - ->expects($this->exactly(2)) - ->method('save') - ->withConsecutive( - [$this->equalTo($cacheKey)], - [$this->equalTo('[C]' . $cacheKey)] - ); - - $reader = new CachedReader(new AnnotationReader(), $cache, true); - $route = new Route(); - $route->pattern = '/someprefix'; - - self::assertEquals([$route], $reader->getClassAnnotations(new ReflectionClass($className))); - - return $reader; - } - - protected function doTestCacheFresh(string $className, int $lastCacheModification): void - { - $cacheKey = $className; - $route = new Route(); - $route->pattern = '/someprefix'; - - $cache = $this->createMock('Doctrine\Common\Cache\Cache'); - $cache - ->expects($this->exactly(2)) - ->method('fetch') - ->withConsecutive( - [$this->equalTo($cacheKey)], - [$this->equalTo('[C]' . $cacheKey)] - ) - ->willReturnOnConsecutiveCalls( - [$route], - $lastCacheModification - ); - $cache->expects(self::never())->method('save'); - - $reader = new CachedReader(new AnnotationReader(), $cache, true); - - $this->assertEquals([$route], $reader->getClassAnnotations(new ReflectionClass($className))); - } - - protected function getReader(): Reader - { - $this->cache = new ArrayCache(); - - return new CachedReader(new AnnotationReader(), $this->cache); - } -} diff --git a/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php b/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php index 297d7e90c..97196c7b3 100644 --- a/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php +++ b/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php @@ -6,7 +6,6 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; use Doctrine\Common\Annotations\Annotation\Target; use Doctrine\Common\Annotations\AnnotationException; -use Doctrine\Common\Annotations\AnnotationRegistry; use Doctrine\Common\Annotations\DocParser; use Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation; use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll; @@ -1315,11 +1314,6 @@ class_exists('Doctrine\Tests\Common\Annotations\Fixture\Annotation\Autoload', fa $parser = new DocParser(); - AnnotationRegistry::registerAutoloadNamespace( - 'Doctrine\Tests\Common\Annotations\Fixtures\Annotation', - __DIR__ . '/../../../../' - ); - $parser->setImports([ 'autoload' => Fixtures\Annotation\Autoload::class, ]); diff --git a/tests/Doctrine/Tests/Common/Annotations/FileCacheReaderTest.php b/tests/Doctrine/Tests/Common/Annotations/FileCacheReaderTest.php deleted file mode 100644 index a92f245a0..000000000 --- a/tests/Doctrine/Tests/Common/Annotations/FileCacheReaderTest.php +++ /dev/null @@ -1,56 +0,0 @@ -cacheDir = sys_get_temp_dir() . '/annotations_' . uniqid('', true); - @mkdir($this->cacheDir); - - return new FileCacheReader(new AnnotationReader(), $this->cacheDir); - } - - public function tearDown(): void - { - foreach (glob($this->cacheDir . '/*.php') as $file) { - unlink($file); - } - - rmdir($this->cacheDir); - } - - /** - * @group DCOM-81 - */ - public function testAttemptToCreateAnnotationCacheDir(): void - { - $this->cacheDir = sys_get_temp_dir() . '/not_existed_dir_' . uniqid('', true); - - if (method_exists($this, 'assertDirectoryDoesNotExist')) { - self::assertDirectoryDoesNotExist($this->cacheDir); - } else { - self::assertDirectoryNotExists($this->cacheDir); - } - - new FileCacheReader(new AnnotationReader(), $this->cacheDir); - - self::assertDirectoryExists($this->cacheDir); - } -} diff --git a/tests/Doctrine/Tests/TestInit.php b/tests/Doctrine/Tests/TestInit.php index 27b04a1b6..f704c23f5 100644 --- a/tests/Doctrine/Tests/TestInit.php +++ b/tests/Doctrine/Tests/TestInit.php @@ -1,7 +1,5 @@ Date: Sun, 18 Dec 2022 18:18:28 +0100 Subject: [PATCH 21/25] Bump PHP to 7.2 This allows us to simplify some dependencies constraints, and to add parameter type declarations. --- .github/workflows/continuous-integration.yml | 2 +- UPGRADE.md | 6 +- composer.json | 10 +-- .../Common/Annotations/Annotation.php | 9 +-- .../Annotations/AnnotationException.php | 65 ++++++++----------- .../Common/Annotations/AnnotationReader.php | 8 +-- lib/Doctrine/Common/Annotations/DocParser.php | 25 ++----- .../Common/Annotations/IndexedReader.php | 3 +- lib/Doctrine/Common/Annotations/PhpParser.php | 2 +- .../Annotations/SimpleAnnotationReader.php | 4 +- .../Common/Annotations/TokenParser.php | 7 +- 11 files changed, 57 insertions(+), 84 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index f160d3efb..bbd54d485 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -16,4 +16,4 @@ jobs: name: "PHPUnit" uses: "doctrine/.github/.github/workflows/continuous-integration.yml@2.1.0" with: - php-versions: '["7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2"]' + php-versions: '["7.2", "7.3", "7.4", "8.0", "8.1", "8.2"]' diff --git a/UPGRADE.md b/UPGRADE.md index de29a33cd..fcb68b3ab 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -5,5 +5,9 @@ implement `ArrayAccess` as a way for you to still be able to treat them as arrays in some ways. - `CachedReader` and `FileCacheReader` have been removed. -- `AnnotationRegistry` method related to registering annotations instead of +- `AnnotationRegistry` methods related to registering annotations instead of using autoloading have been removed. +- Parameter type declarations have been added to all methods of all classes. If +you have classes inheriting from classes inside this package, you should add +parameter and return type declarations. +- Support for PHP < 7.2 has been removed diff --git a/composer.json b/composer.json index 8dc634be3..a45507696 100644 --- a/composer.json +++ b/composer.json @@ -32,17 +32,17 @@ ], "homepage": "https://www.doctrine-project.org/projects/annotations.html", "require": { - "php": "^7.1 || ^8.0", + "php": "^7.2 || ^8.0", "ext-tokenizer": "*", "doctrine/lexer": "^2", "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.0", + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.0", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/cache": "^5.4 || ^6", "vimeo/psalm": "^4.10" }, "suggest": { diff --git a/lib/Doctrine/Common/Annotations/Annotation.php b/lib/Doctrine/Common/Annotations/Annotation.php index 9cae3dacd..fba23e9f1 100644 --- a/lib/Doctrine/Common/Annotations/Annotation.php +++ b/lib/Doctrine/Common/Annotations/Annotation.php @@ -29,11 +29,9 @@ final public function __construct(array $data) /** * Error handler for unknown property accessor in Annotation class. * - * @param string $name Unknown property name. - * * @throws BadMethodCallException */ - public function __get($name) + public function __get(string $name) { throw new BadMethodCallException( sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class) @@ -43,12 +41,11 @@ public function __get($name) /** * Error handler for unknown property mutator in Annotation class. * - * @param string $name Unknown property name. - * @param mixed $value Property value. + * @param mixed $value Property value. * * @throws BadMethodCallException */ - public function __set($name, $value) + public function __set(string $name, $value) { throw new BadMethodCallException( sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class) diff --git a/lib/Doctrine/Common/Annotations/AnnotationException.php b/lib/Doctrine/Common/Annotations/AnnotationException.php index dcdfe4df6..002ee0491 100644 --- a/lib/Doctrine/Common/Annotations/AnnotationException.php +++ b/lib/Doctrine/Common/Annotations/AnnotationException.php @@ -19,11 +19,9 @@ class AnnotationException extends Exception /** * Creates a new AnnotationException describing a Syntax error. * - * @param string $message Exception message - * * @return AnnotationException */ - public static function syntaxError($message) + public static function syntaxError(string $message) { return new self('[Syntax Error] ' . $message); } @@ -31,11 +29,9 @@ public static function syntaxError($message) /** * Creates a new AnnotationException describing a Semantical error. * - * @param string $message Exception message - * * @return AnnotationException */ - public static function semanticalError($message) + public static function semanticalError(string $message) { return new self('[Semantical Error] ' . $message); } @@ -44,11 +40,9 @@ public static function semanticalError($message) * Creates a new AnnotationException describing an error which occurred during * the creation of the annotation. * - * @param string $message - * * @return AnnotationException */ - public static function creationError($message, ?Throwable $previous = null) + public static function creationError(string $message, ?Throwable $previous = null) { return new self('[Creation Error] ' . $message, 0, $previous); } @@ -56,11 +50,9 @@ public static function creationError($message, ?Throwable $previous = null) /** * Creates a new AnnotationException describing a type error. * - * @param string $message - * * @return AnnotationException */ - public static function typeError($message) + public static function typeError(string $message) { return new self('[Type Error] ' . $message); } @@ -68,12 +60,9 @@ public static function typeError($message) /** * Creates a new AnnotationException describing a constant semantical error. * - * @param string $identifier - * @param string $context - * * @return AnnotationException */ - public static function semanticalErrorConstants($identifier, $context = null) + public static function semanticalErrorConstants(string $identifier, ?string $context = null) { return self::semanticalError(sprintf( "Couldn't find constant %s%s.", @@ -85,16 +74,17 @@ public static function semanticalErrorConstants($identifier, $context = null) /** * Creates a new AnnotationException describing an type error of an attribute. * - * @param string $attributeName - * @param string $annotationName - * @param string $context - * @param string $expected - * @param mixed $actual + * @param mixed $actual * * @return AnnotationException */ - public static function attributeTypeError($attributeName, $annotationName, $context, $expected, $actual) - { + public static function attributeTypeError( + string $attributeName, + string $annotationName, + string $context, + string $expected, + $actual + ) { return self::typeError(sprintf( 'Attribute "%s" of @%s declared on %s expects %s, but got %s.', $attributeName, @@ -108,15 +98,14 @@ public static function attributeTypeError($attributeName, $annotationName, $cont /** * Creates a new AnnotationException describing an required error of an attribute. * - * @param string $attributeName - * @param string $annotationName - * @param string $context - * @param string $expected - * * @return AnnotationException */ - public static function requiredError($attributeName, $annotationName, $context, $expected) - { + public static function requiredError( + string $attributeName, + string $annotationName, + string $context, + string $expected + ) { return self::typeError(sprintf( 'Attribute "%s" of @%s declared on %s expects %s. This value should not be null.', $attributeName, @@ -129,16 +118,18 @@ public static function requiredError($attributeName, $annotationName, $context, /** * Creates a new AnnotationException describing a invalid enummerator. * - * @param string $attributeName - * @param string $annotationName - * @param string $context - * @param mixed $given - * @phpstan-param list $available + * @param mixed $given + * @phpstan-param list $available * * @return AnnotationException */ - public static function enumeratorError($attributeName, $annotationName, $context, $available, $given) - { + public static function enumeratorError( + string $attributeName, + string $annotationName, + string $context, + array $available, + $given + ) { return new self(sprintf( '[Enum Error] Attribute "%s" of @%s declared on %s accepts only [%s], but got %s.', $attributeName, diff --git a/lib/Doctrine/Common/Annotations/AnnotationReader.php b/lib/Doctrine/Common/Annotations/AnnotationReader.php index 1f538ee53..31f3777a8 100644 --- a/lib/Doctrine/Common/Annotations/AnnotationReader.php +++ b/lib/Doctrine/Common/Annotations/AnnotationReader.php @@ -48,20 +48,16 @@ class AnnotationReader implements Reader /** * Add a new annotation to the globally ignored annotation names with regard to exception handling. - * - * @param string $name */ - public static function addGlobalIgnoredName($name) + public static function addGlobalIgnoredName(string $name) { self::$globalIgnoredNames[$name] = true; } /** * Add a new annotation to the globally ignored annotation namespaces with regard to exception handling. - * - * @param string $namespace */ - public static function addGlobalIgnoredNamespace($namespace) + public static function addGlobalIgnoredNamespace(string $namespace) { self::$globalIgnoredNamespaces[$namespace] = true; } diff --git a/lib/Doctrine/Common/Annotations/DocParser.php b/lib/Doctrine/Common/Annotations/DocParser.php index 37c352409..d82119286 100644 --- a/lib/Doctrine/Common/Annotations/DocParser.php +++ b/lib/Doctrine/Common/Annotations/DocParser.php @@ -286,7 +286,7 @@ public function setIgnoredAnnotationNames(array $names) * * @return void */ - public function setIgnoredAnnotationNamespaces($ignoredAnnotationNamespaces) + public function setIgnoredAnnotationNamespaces(array $ignoredAnnotationNamespaces) { $this->ignoredAnnotationNamespaces = $ignoredAnnotationNamespaces; } @@ -294,25 +294,21 @@ public function setIgnoredAnnotationNamespaces($ignoredAnnotationNamespaces) /** * Sets ignore on not-imported annotations. * - * @param bool $bool - * * @return void */ - public function setIgnoreNotImportedAnnotations($bool) + public function setIgnoreNotImportedAnnotations(bool $bool) { - $this->ignoreNotImportedAnnotations = (bool) $bool; + $this->ignoreNotImportedAnnotations = $bool; } /** * Sets the default namespaces. * - * @param string $namespace - * * @return void * * @throws RuntimeException */ - public function addNamespace($namespace) + public function addNamespace(string $namespace) { if ($this->imports) { throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); @@ -342,11 +338,9 @@ public function setImports(array $imports) /** * Sets current target context as bitmask. * - * @param int $target - * * @return void */ - public function setTarget($target) + public function setTarget(int $target) { $this->target = $target; } @@ -354,15 +348,12 @@ public function setTarget($target) /** * Parses the given docblock string for annotations. * - * @param string $input The docblock string to parse. - * @param string $context The parsing context. - * * @phpstan-return list Array of annotations. If no annotations are found, an empty array is returned. * * @throws AnnotationException * @throws ReflectionException */ - public function parse($input, $context = '') + public function parse(string $input, string $context = '') { $pos = $this->findInitialTokenPosition($input); if ($pos === null) { @@ -379,10 +370,8 @@ public function parse($input, $context = '') /** * Finds the first valid annotation - * - * @param string $input The docblock string to parse */ - private function findInitialTokenPosition($input): ?int + private function findInitialTokenPosition(string $input): ?int { $pos = 0; diff --git a/lib/Doctrine/Common/Annotations/IndexedReader.php b/lib/Doctrine/Common/Annotations/IndexedReader.php index 62dcf7487..77b5b9cb2 100644 --- a/lib/Doctrine/Common/Annotations/IndexedReader.php +++ b/lib/Doctrine/Common/Annotations/IndexedReader.php @@ -88,12 +88,11 @@ public function getPropertyAnnotation(ReflectionProperty $property, $annotationN /** * Proxies all methods to the delegate. * - * @param string $method * @param mixed[] $args * * @return mixed */ - public function __call($method, $args) + public function __call(string $method, array $args) { return call_user_func_array([$this->delegate, $method], $args); } diff --git a/lib/Doctrine/Common/Annotations/PhpParser.php b/lib/Doctrine/Common/Annotations/PhpParser.php index d2263c033..2a0970471 100644 --- a/lib/Doctrine/Common/Annotations/PhpParser.php +++ b/lib/Doctrine/Common/Annotations/PhpParser.php @@ -70,7 +70,7 @@ public function parseUseStatements($reflection): array * * @return string|null The content of the file or null if the file does not exist. */ - private function getFileContent($filename, $lineNumber) + private function getFileContent(string $filename, $lineNumber) { if (! is_file($filename)) { return null; diff --git a/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php b/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php index 8a78c119d..cde40dac0 100644 --- a/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php +++ b/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php @@ -31,11 +31,9 @@ public function __construct() /** * Adds a namespace in which we will look for annotations. * - * @param string $namespace - * * @return void */ - public function addNamespace($namespace) + public function addNamespace(string $namespace) { $this->parser->addNamespace($namespace); } diff --git a/lib/Doctrine/Common/Annotations/TokenParser.php b/lib/Doctrine/Common/Annotations/TokenParser.php index 69259fccf..0534fd17c 100644 --- a/lib/Doctrine/Common/Annotations/TokenParser.php +++ b/lib/Doctrine/Common/Annotations/TokenParser.php @@ -46,8 +46,7 @@ class TokenParser */ private $pointer = 0; - /** @param string $contents */ - public function __construct($contents) + public function __construct(string $contents) { $this->tokens = token_get_all($contents); @@ -71,7 +70,7 @@ public function __construct($contents) * * @return mixed[]|string|null The token if exists, null otherwise. */ - public function next($docCommentIsComment = true) + public function next(bool $docCommentIsComment = true) { for ($i = $this->pointer; $i < $this->numTokens; $i++) { $this->pointer++; @@ -149,7 +148,7 @@ public function parseUseStatement() * * @return array A list with all found use statements. */ - public function parseUseStatements($namespaceName) + public function parseUseStatements(string $namespaceName) { $statements = []; while (($token = $this->next())) { From c03bc8bb64ac38379577eb06df568c49be7cdc01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 17 Dec 2022 13:59:47 +0100 Subject: [PATCH 22/25] Allow doctrine/lexer 3 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a45507696..d1d3d8db1 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-tokenizer": "*", - "doctrine/lexer": "^2", + "doctrine/lexer": "^2 || ^3", "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { From 9dc2866182ec5f8fee0bb18093b4c57f469c12cf Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 19 Dec 2022 17:50:58 +0100 Subject: [PATCH 23/25] Remove SimpleAnnotationReader (#469) --- UPGRADE.md | 1 + .../Annotations/SimpleAnnotationReader.php | 112 -------------- .../SimpleAnnotationReaderTest.php | 140 ------------------ .../Common/Annotations/Ticket/DCOM58Test.php | 11 -- 4 files changed, 1 insertion(+), 263 deletions(-) delete mode 100644 lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php delete mode 100644 tests/Doctrine/Tests/Common/Annotations/SimpleAnnotationReaderTest.php diff --git a/UPGRADE.md b/UPGRADE.md index fcb68b3ab..6e88871cd 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,6 @@ # Upgrade from 1.0.x to 2.0.x +- `SimpleAnnotationReader` has been removed. - `DocLexer::peek()` and `DocLexer::glimpse` now return `Doctrine\Common\Lexer\Token` objects. When using `doctrine/lexer` 2, these implement `ArrayAccess` as a way for you to still be able to treat them as diff --git a/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php b/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php deleted file mode 100644 index cde40dac0..000000000 --- a/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php +++ /dev/null @@ -1,112 +0,0 @@ -parser = new DocParser(); - $this->parser->setIgnoreNotImportedAnnotations(true); - } - - /** - * Adds a namespace in which we will look for annotations. - * - * @return void - */ - public function addNamespace(string $namespace) - { - $this->parser->addNamespace($namespace); - } - - /** - * {@inheritDoc} - */ - public function getClassAnnotations(ReflectionClass $class) - { - return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); - } - - /** - * {@inheritDoc} - */ - public function getMethodAnnotations(ReflectionMethod $method) - { - return $this->parser->parse( - $method->getDocComment(), - 'method ' . $method->getDeclaringClass()->name . '::' . $method->getName() . '()' - ); - } - - /** - * {@inheritDoc} - */ - public function getPropertyAnnotations(ReflectionProperty $property) - { - return $this->parser->parse( - $property->getDocComment(), - 'property ' . $property->getDeclaringClass()->name . '::$' . $property->getName() - ); - } - - /** - * {@inheritDoc} - */ - public function getClassAnnotation(ReflectionClass $class, $annotationName) - { - foreach ($this->getClassAnnotations($class) as $annot) { - if ($annot instanceof $annotationName) { - return $annot; - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - public function getMethodAnnotation(ReflectionMethod $method, $annotationName) - { - foreach ($this->getMethodAnnotations($method) as $annot) { - if ($annot instanceof $annotationName) { - return $annot; - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) - { - foreach ($this->getPropertyAnnotations($property) as $annot) { - if ($annot instanceof $annotationName) { - return $annot; - } - } - - return null; - } -} diff --git a/tests/Doctrine/Tests/Common/Annotations/SimpleAnnotationReaderTest.php b/tests/Doctrine/Tests/Common/Annotations/SimpleAnnotationReaderTest.php deleted file mode 100644 index f65861c60..000000000 --- a/tests/Doctrine/Tests/Common/Annotations/SimpleAnnotationReaderTest.php +++ /dev/null @@ -1,140 +0,0 @@ -ignoreIssues(); - parent::testImportDetectsNotImportedAnnotation(); - } - - /** - * Contrary to the behavior of the default annotation reader, we do just ignore - * these in the simple annotation reader (so, no expected exception here). - * - * @doesNotPerformAssertions - */ - public function testImportDetectsNonExistentAnnotation(): void - { - $this->ignoreIssues(); - parent::testImportDetectsNonExistentAnnotation(); - } - - /** - * Contrary to the behavior of the default annotation reader, we do just ignore - * these in the simple annotation reader (so, no expected exception here). - * - * @doesNotPerformAssertions - */ - public function testClassWithInvalidAnnotationTargetAtClassDocBlock(): void - { - $this->ignoreIssues(); - parent::testClassWithInvalidAnnotationTargetAtClassDocBlock(); - } - - /** - * Contrary to the behavior of the default annotation reader, we do just ignore - * these in the simple annotation reader (so, no expected exception here). - * - * @doesNotPerformAssertions - */ - public function testClassWithInvalidAnnotationTargetAtPropertyDocBlock(): void - { - $this->ignoreIssues(); - parent::testClassWithInvalidAnnotationTargetAtPropertyDocBlock(); - } - - /** - * Contrary to the behavior of the default annotation reader, we do just ignore - * these in the simple annotation reader (so, no expected exception here). - * - * @doesNotPerformAssertions - */ - public function testClassWithInvalidNestedAnnotationTargetAtPropertyDocBlock(): void - { - $this->ignoreIssues(); - parent::testClassWithInvalidNestedAnnotationTargetAtPropertyDocBlock(); - } - - /** - * Contrary to the behavior of the default annotation reader, we do just ignore - * these in the simple annotation reader (so, no expected exception here). - * - * @doesNotPerformAssertions - */ - public function testClassWithInvalidAnnotationTargetAtMethodDocBlock(): void - { - $this->ignoreIssues(); - parent::testClassWithInvalidAnnotationTargetAtMethodDocBlock(); - } - - /** - * Contrary to the behavior of the default annotation reader, we do just ignore - * these in the simple annotation reader (so, no expected exception here). - * - * @doesNotPerformAssertions - */ - public function testErrorWhenInvalidAnnotationIsUsed(): void - { - $this->ignoreIssues(); - parent::testErrorWhenInvalidAnnotationIsUsed(); - } - - /** - * The SimpleAnnotationReader doens't include the @IgnoreAnnotation in the results. - */ - public function testInvalidAnnotationUsageButIgnoredClass(): void - { - $reader = $this->getReader(); - $ref = new ReflectionClass(Fixtures\InvalidAnnotationUsageButIgnoredClass::class); - $annots = $reader->getClassAnnotations($ref); - - self::assertCount(1, $annots); - } - - public function testIncludeIgnoreAnnotation(): void - { - $this->markTestSkipped('The simplified annotation reader would always autoload annotations'); - } - - /** - * @group DDC-1660 - * @group regression - * - * Contrary to the behavior of the default annotation reader, @version is not ignored - */ - public function testInvalidAnnotationButIgnored(): void - { - $reader = $this->getReader(); - $class = new ReflectionClass(Fixtures\ClassDDC1660::class); - - self::assertTrue(class_exists(Fixtures\Annotation\Version::class)); - self::assertCount(1, $reader->getClassAnnotations($class)); - self::assertCount(1, $reader->getMethodAnnotations($class->getMethod('bar'))); - self::assertCount(1, $reader->getPropertyAnnotations($class->getProperty('foo'))); - } - - protected function getReader(): Reader - { - $reader = new SimpleAnnotationReader(); - $reader->addNamespace(__NAMESPACE__); - $reader->addNamespace(__NAMESPACE__ . '\Fixtures'); - $reader->addNamespace(__NAMESPACE__ . '\Fixtures\Annotation'); - - return $reader; - } -} diff --git a/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Test.php b/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Test.php index 599b62c72..6ea53287e 100644 --- a/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Test.php +++ b/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Test.php @@ -4,7 +4,6 @@ use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\DocParser; -use Doctrine\Common\Annotations\SimpleAnnotationReader; use PHPUnit\Framework\TestCase; use ReflectionClass; @@ -78,16 +77,6 @@ public function testIssueWithNamespacesOrImports(): void self::assertCount(1, $annots); self::assertInstanceOf(\Entity::class, $annots[0]); } - - public function testIssueSimpleAnnotationReader(): void - { - $reader = new SimpleAnnotationReader(); - $reader->addNamespace('Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping'); - $annots = $reader->getClassAnnotations(new ReflectionClass(__NAMESPACE__ . '\MappedClass')); - - self::assertCount(1, $annots); - self::assertInstanceOf(Doctrine\ORM\Mapping\Entity::class, $annots[0]); - } } /** From 455db7458c6371dda61afd212c9d74c09f534496 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 19 Dec 2022 18:43:32 +0100 Subject: [PATCH 24/25] Remove NamedArgumentConstructorAnnotation interface (#470) --- UPGRADE.md | 2 + lib/Doctrine/Common/Annotations/DocParser.php | 3 +- .../NamedArgumentConstructorAnnotation.php | 14 ----- .../Common/Annotations/DocParserTest.php | 62 ------------------- 4 files changed, 3 insertions(+), 78 deletions(-) delete mode 100644 lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.php diff --git a/UPGRADE.md b/UPGRADE.md index 6e88871cd..df069092c 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,7 @@ # Upgrade from 1.0.x to 2.0.x +- The `NamedArgumentConstructorAnnotation` has been removed. Use the `@NamedArgumentConstructor` + annotation instead. - `SimpleAnnotationReader` has been removed. - `DocLexer::peek()` and `DocLexer::glimpse` now return `Doctrine\Common\Lexer\Token` objects. When using `doctrine/lexer` 2, these diff --git a/lib/Doctrine/Common/Annotations/DocParser.php b/lib/Doctrine/Common/Annotations/DocParser.php index d82119286..dc06bcbf8 100644 --- a/lib/Doctrine/Common/Annotations/DocParser.php +++ b/lib/Doctrine/Common/Annotations/DocParser.php @@ -523,8 +523,7 @@ class_exists(NamedArgumentConstructor::class); 'is_annotation' => strpos($docComment, '@Annotation') !== false, ]; - $metadata['has_named_argument_constructor'] = $metadata['has_constructor'] - && $class->implementsInterface(NamedArgumentConstructorAnnotation::class); + $metadata['has_named_argument_constructor'] = false; // verify that the class is really meant to be an annotation if ($metadata['is_annotation']) { diff --git a/lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.php b/lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.php deleted file mode 100644 index 8af224c0b..000000000 --- a/lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.php +++ /dev/null @@ -1,14 +0,0 @@ -createTestParser() - ->parse('/** @NamedAnnotation(foo="baz", bar=2222) */'); - - self::assertCount(1, $result); - self::assertInstanceOf(NamedAnnotation::class, $result[0]); - self::assertSame('baz', $result[0]->getFoo()); - self::assertSame(2222, $result[0]->getBar()); - } - - public function testNamedReorderedArgumentsConstructorInterface(): void - { - $result = $this - ->createTestParser() - ->parse('/** @NamedAnnotation(bar=2222, foo="baz") */'); - - self::assertCount(1, $result); - self::assertInstanceOf(NamedAnnotation::class, $result[0]); - self::assertSame('baz', $result[0]->getFoo()); - self::assertSame(2222, $result[0]->getBar()); - } - - public function testNamedArgumentsConstructorInterfaceWithDefaultValue(): void - { - $result = $this - ->createTestParser() - ->parse('/** @NamedAnnotation(foo="baz") */'); - - self::assertCount(1, $result); - self::assertInstanceOf(NamedAnnotation::class, $result[0]); - self::assertSame('baz', $result[0]->getFoo()); - self::assertSame(1234, $result[0]->getBar()); - } - public function testNamedArgumentsConstructorAnnotation(): void { $result = $this @@ -1751,31 +1714,6 @@ public function expectExceptionMessageMatches(string $regularExpression): void } } -/** @Annotation */ -class NamedAnnotation implements NamedArgumentConstructorAnnotation -{ - /** @var string */ - private $foo; - /** @var int */ - private $bar; - - public function __construct(string $foo, int $bar = 1234) - { - $this->foo = $foo; - $this->bar = $bar; - } - - public function getFoo(): string - { - return $this->foo; - } - - public function getBar(): int - { - return $this->bar; - } -} - /** * @Annotation * @NamedArgumentConstructor From 7a95b3bfd1d7702b19cadbd26b1593ae0c199179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Mon, 19 Dec 2022 19:06:52 +0100 Subject: [PATCH 25/25] Remove deprecated method --- UPGRADE.md | 2 + lib/Doctrine/Common/Annotations/PhpParser.php | 14 ------- .../PhpParserPerformanceWithShortCutBench.php | 2 +- ...pParserPerformanceWithoutShortCutBench.php | 2 +- .../Common/Annotations/PhpParserTest.php | 38 +++++++++---------- 5 files changed, 23 insertions(+), 35 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index df069092c..4172708f0 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -14,3 +14,5 @@ arrays in some ways. you have classes inheriting from classes inside this package, you should add parameter and return type declarations. - Support for PHP < 7.2 has been removed +- `PhpParser::parseClass()` has been removed. Use + `PhpParser::parseUseStatements()` instead. diff --git a/lib/Doctrine/Common/Annotations/PhpParser.php b/lib/Doctrine/Common/Annotations/PhpParser.php index 2a0970471..312a2ab1f 100644 --- a/lib/Doctrine/Common/Annotations/PhpParser.php +++ b/lib/Doctrine/Common/Annotations/PhpParser.php @@ -16,20 +16,6 @@ */ final class PhpParser { - /** - * Parses a class. - * - * @deprecated use parseUseStatements instead - * - * @param ReflectionClass $class A ReflectionClass object. - * - * @return array A list with use statements in the form (Alias => FQN). - */ - public function parseClass(ReflectionClass $class) - { - return $this->parseUseStatements($class); - } - /** * Parse a class or function for use statements. * diff --git a/tests/Doctrine/Performance/Common/Annotations/PhpParserPerformanceWithShortCutBench.php b/tests/Doctrine/Performance/Common/Annotations/PhpParserPerformanceWithShortCutBench.php index 64c7a70d9..d83d9aba1 100644 --- a/tests/Doctrine/Performance/Common/Annotations/PhpParserPerformanceWithShortCutBench.php +++ b/tests/Doctrine/Performance/Common/Annotations/PhpParserPerformanceWithShortCutBench.php @@ -31,6 +31,6 @@ public function initialize(): void */ public function bench(): void { - $this->parser->parseClass($this->class); + $this->parser->parseUseStatements($this->class); } } diff --git a/tests/Doctrine/Performance/Common/Annotations/PhpParserPerformanceWithoutShortCutBench.php b/tests/Doctrine/Performance/Common/Annotations/PhpParserPerformanceWithoutShortCutBench.php index 41cb419e5..ca68000c0 100644 --- a/tests/Doctrine/Performance/Common/Annotations/PhpParserPerformanceWithoutShortCutBench.php +++ b/tests/Doctrine/Performance/Common/Annotations/PhpParserPerformanceWithoutShortCutBench.php @@ -31,6 +31,6 @@ public function initialize(): void */ public function bench(): void { - $this->parser->parseClass($this->class); + $this->parser->parseUseStatements($this->class); } } diff --git a/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php b/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php index d533bee4e..a97e1515a 100644 --- a/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php +++ b/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php @@ -21,7 +21,7 @@ public function testParseClassWithMultipleClassesInFile(): void self::assertEquals([ 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testParseClassWithMultipleImportsInUseStatement(): void @@ -32,7 +32,7 @@ public function testParseClassWithMultipleImportsInUseStatement(): void self::assertEquals([ 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } /** @@ -47,13 +47,13 @@ public function testParseClassWithGroupUseStatement(): void 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', 'supersecure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testParseClassWhenNotUserDefined(): void { $parser = new PhpParser(); - self::assertEquals([], $parser->parseClass(new ReflectionClass(stdClass::class))); + self::assertEquals([], $parser->parseUseStatements(new ReflectionClass(stdClass::class))); } public function testClassFileDoesNotExist(): void @@ -64,7 +64,7 @@ public function testClassFileDoesNotExist(): void ->will($this->returnValue('/valid/class/Fake.php(35) : eval()d code')); $parser = new PhpParser(); - self::assertEquals([], $parser->parseClass($class)); + self::assertEquals([], $parser->parseUseStatements($class)); } public function testParseClassWhenClassIsNotNamespaced(): void @@ -75,7 +75,7 @@ public function testParseClassWhenClassIsNotNamespaced(): void self::assertEquals([ 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testParseClassWhenClassIsInterface(): void @@ -85,7 +85,7 @@ public function testParseClassWhenClassIsInterface(): void self::assertEquals([ 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testClassWithFullyQualifiedUseStatements(): void @@ -97,7 +97,7 @@ public function testClassWithFullyQualifiedUseStatements(): void 'secure' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Secure', 'route' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Route', 'template' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Template', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testNamespaceAndClassCommentedOut(): void @@ -108,7 +108,7 @@ public function testNamespaceAndClassCommentedOut(): void self::assertEquals([ 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testEqualNamespacesPerFileWithClassAsFirst(): void @@ -119,7 +119,7 @@ public function testEqualNamespacesPerFileWithClassAsFirst(): void self::assertEquals([ 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testEqualNamespacesPerFileWithClassAsLast(): void @@ -130,7 +130,7 @@ public function testEqualNamespacesPerFileWithClassAsLast(): void self::assertEquals([ 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testDifferentNamespacesPerFileWithClassAsFirst(): void @@ -140,7 +140,7 @@ public function testDifferentNamespacesPerFileWithClassAsFirst(): void self::assertEquals([ 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testDifferentNamespacesPerFileWithClassAsLast(): void @@ -150,7 +150,7 @@ public function testDifferentNamespacesPerFileWithClassAsLast(): void self::assertEquals([ 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testGlobalNamespacesPerFileWithClassAsFirst(): void @@ -161,7 +161,7 @@ public function testGlobalNamespacesPerFileWithClassAsFirst(): void self::assertEquals([ 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testGlobalNamespacesPerFileWithClassAsLast(): void @@ -172,7 +172,7 @@ public function testGlobalNamespacesPerFileWithClassAsLast(): void self::assertEquals([ 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testNamespaceWithClosureDeclaration(): void @@ -184,7 +184,7 @@ public function testNamespaceWithClosureDeclaration(): void 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } public function testIfPointerResetsOnMultipleParsingTries(): void @@ -196,13 +196,13 @@ public function testIfPointerResetsOnMultipleParsingTries(): void 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); self::assertEquals([ 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } /** @@ -217,6 +217,6 @@ public function testClassWithClosure(): void self::assertEquals([ 'annotationtargetall' => __NAMESPACE__ . '\Fixtures\AnnotationTargetAll', 'annotationtargetannotation' => __NAMESPACE__ . '\Fixtures\AnnotationTargetAnnotation', - ], $parser->parseClass($class)); + ], $parser->parseUseStatements($class)); } }