Skip to content

Commit

Permalink
Merge pull request #442 from doctrine/1.13.x-merge-up-into-1.14.x_GEg…
Browse files Browse the repository at this point in the history
…YUWmi

Merge release 1.13.3 into 1.14.x
  • Loading branch information
greg0ire committed Jul 2, 2022
2 parents 2da982a + 648b034 commit 2273d90
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/coding-standards.yml
Expand Up @@ -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"
18 changes: 18 additions & 0 deletions .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"
2 changes: 1 addition & 1 deletion .github/workflows/continuous-integration.yml
Expand Up @@ -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"]'
3 changes: 1 addition & 2 deletions .github/workflows/release-on-milestone-closed.yml
Expand Up @@ -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 }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/static-analysis.yml
Expand Up @@ -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"
50 changes: 37 additions & 13 deletions 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": "*",
Expand All @@ -21,16 +40,15 @@
"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"
},
"config": {
"sort-packages": true
},
"autoload": {
"psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" }
"psr-4": {
"Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
}
},
"autoload-dev": {
"psr-4": {
Expand All @@ -41,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
}
}
5 changes: 3 additions & 2 deletions lib/Doctrine/Common/Annotations/AnnotationException.php
Expand Up @@ -3,6 +3,7 @@
namespace Doctrine\Common\Annotations;

use Exception;
use Throwable;

use function get_class;
use function gettype;
Expand Down Expand Up @@ -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);
}

/**
Expand Down
36 changes: 32 additions & 4 deletions lib/Doctrine/Common/Annotations/DocParser.php
Expand Up @@ -12,6 +12,7 @@
use ReflectionProperty;
use RuntimeException;
use stdClass;
use Throwable;

use function array_keys;
use function array_map;
Expand Down Expand Up @@ -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 = [];
Expand All @@ -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])) {
Expand Down Expand Up @@ -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<string,mixed> $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
);
}
}
}
Expand Up @@ -147,6 +147,7 @@ final class ImplicitlyIgnoredAnnotationNames
// PHPStan, Psalm
'extends' => true,
'implements' => true,
'readonly' => true,
'template' => true,
'use' => true,

Expand Down
8 changes: 7 additions & 1 deletion phpstan.neon
Expand Up @@ -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
Expand All @@ -13,6 +18,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#'
100 changes: 86 additions & 14 deletions tests/Doctrine/Tests/Common/Annotations/DocParserTest.php
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand All @@ -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;
}
}

/**
Expand Down Expand Up @@ -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
Expand All @@ -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;
}
}

/**
Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit 2273d90

Please sign in to comment.