8000 Improve templating by greg0ire · Pull Request #9541 · doctrine/orm · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Improve templating #9541

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php
8000
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ class AttributeDriver extends AnnotationDriver
Mapping\MappedSuperclass::class => 2,
];

/**
* The annotation reader.
*
* @var AttributeReader
*/
protected $reader;

/**
* @param array<string> $paths
*/
Expand All @@ -46,7 +53,8 @@ public function __construct(array $paths)
));
}

parent::__construct(new AttributeReader(), $paths);
$this->reader = new AttributeReader();
$this->addPaths($paths);
}

/**
Expand Down Expand Up @@ -271,7 +279,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void
// Check for JoinColumn/JoinColumns annotations
$joinColumns = [];

$joinColumnAttributes = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class);
$joinColumnAttributes = $this->reader->getPropertyAnnotationCollection($property, Mapping\JoinColumn::class);

foreach ($joinColumnAttributes as $joinColumnAttribute) {
$joinColumns[] = $this->joinColumnToArray($joinColumnAttribute);
Expand Down Expand Up @@ -376,11 +384,11 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void
];
}

foreach ($this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class) as $joinColumn) {
foreach ($this->reader->getPropertyAnnotationCollection($property, Mapping\JoinColumn::class) as $joinColumn) {
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
}

foreach ($this->reader->getPropertyAnnotation($property, Mapping\InverseJoinColumn::class) as $joinColumn) {
foreach ($this->reader->getPropertyAnnotationCollection($property, Mapping\InverseJoinColumn::class) as $joinColumn) {
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
}

Expand Down
79 changes: 59 additions & 20 deletions lib/Doctrine/ORM/Mapping/Driver/AttributeReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Attribute;
use Doctrine\ORM\Mapping\Annotation;
use LogicException;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;
Expand All @@ -14,58 +15,93 @@
use function assert;
use function is_string;
use function is_subclass_of;
use function sprintf;

/**
* @internal
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows me to do all sorts of BC-breaks.

*/
final class AttributeReader
{
/** @var array<string,bool> */
/** @var array<class-string<Annotation>,bool> */
private array $isRepeatableAttribute = [];

/** @return array<Annotation|RepeatableAttributeCollection> */
/**
* @psalm-return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
public function getClassAnnotations(ReflectionClass $class): array
{
return $this->convertToAttributeInstances($class->getAttributes());
}

/** @return Annotation|RepeatableAttributeCollection|null */
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
return $this->getClassAnnotations($class)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}

/** @return array<Annotation|RepeatableAttributeCollection> */
/**
* @return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
public function getMethodAnnotations(ReflectionMethod $method): array
{
return $this->convertToAttributeInstances($method->getAttributes());
}

/** @return Annotation|RepeatableAttributeCollection|null */
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
return $this->getMethodAnnotations($method)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}

/** @return array<Annotation|RepeatableAttributeCollection> */
/**
* @return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
public function getPropertyAnnotations(ReflectionProperty $property): array
{
return $this->convertToAttributeInstances($property->getAttributes());
}

/** @return Annotation|RepeatableAttributeCollection|null */
/**
* @param class-string<T> $annotationName The name of the annotation.
*
* @return T|null
*
* @template T of Annotation
*/
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
if ($this->isRepeatable($annotationName)) {
throw new LogicException(sprintf(
'The attribute "%s" is repeatable. Call getPropertyAnnotationCollection() instead.',
$annotationName
));
}

return $this->getPropertyAnnotations($property)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}

/**
* @param class-string<T> $annotationName The name of the annotation.
*
* @return RepeatableAttributeCollection<T>
*
* @template T of Annotation
*/
public function getPropertyAnnotationCollection(
ReflectionProperty $property,
string $annotationName
): RepeatableAttributeCollection {
if (! $this->isRepeatable($annotationName)) {
throw new LogicException(sprintf(
'The attribute "%s" is not repeatable. Call getPropertyAnnotation() instead.',
$annotationName
));
}

return $this->getPropertyAnnotations($property)[$annotationName] ?? new RepeatableAttributeCollection();
}

/**
* @param array<ReflectionAttribute> $attributes
*
* @return array<Annotation|RepeatableAttributeCollection>
* @return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
private function convertToAttributeInstances(array $attributes): array
{
Expand Down Expand Up @@ -98,6 +134,9 @@ private function convertToAttributeInstances(array $attributes): array
return $instances;
}

/**
* @param class-string<Annotation> $attributeClassName
*/
private function isRepeatable(string $attributeClassName): bool
{
if (isset($this->isRepeatableAttribute[$attributeClassName])) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
use Doctrine\ORM\Mapping\Annotation;

/**
* @template-extends ArrayObject<int,Annotation>
* @template-extends ArrayObject<int, T>
* @template T of Annotation
*/
final class RepeatableAttributeCollection extends ArrayObject
{
Expand Down
6 changes: 4 additions & 2 deletions lib/Doctrine/ORM/Repository/RepositoryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ interface RepositoryFactory
* Gets the repository for an entity class.
*
* @param EntityManagerInterface $entityManager The EntityManager instance.
* @param string $entityName The name of the entity.
* @param class-string<T> $entityName The name of the entity.
*
* @return ObjectRepository
* @return ObjectRepository<T>
*
* @template T of object
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName);
}
12 changes: 1 addition & 11 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -261,15 +261,10 @@ parameters:
path: lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php

-
message: "#^Argument of an invalid type Doctrine\\\\ORM\\\\Mapping\\\\InverseJoinColumn supplied for foreach, only iterables are supported\\.$#"
message: "#^PHPDoc type Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\AttributeReader of property Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\AttributeDriver\\:\\:\\$reader is not covariant with PHPDoc type Doctrine\\\\Common\\\\Annotations\\\\Reader of overridden property Doctrine\\\\Persistence\\\\Mapping\\\\Driver\\\\AnnotationDriver\\:\\:\\$reader\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php

-
message: "#^Argument of an invalid type Doctrine\\\\ORM\\\\Mapping\\\\JoinColumn supplied for foreach, only iterables are supported\\.$#"
count: 2
path: lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php

-
message: "#^PHPDoc type array\\<string, int\\> of property Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\AttributeDriver\\:\\:\\$entityAnnotationClasses is not covariant with PHPDoc type array\\<class\\-string, bool\\|int\\> of overridden property Doctrine\\\\Persistence\\\\Mapping\\\\Driver\\\\AnnotationDriver\\:\\:\\$entityAnnotationClasses\\.$#"
count: 1
Expand All @@ -280,11 +275,6 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php

-
message: "#^Parameter \\#1 \\$reader of method Doctrine\\\\Persistence\\\\Mapping\\\\Driver\\\\AnnotationDriver\\:\\:__construct\\(\\) expects Doctrine\\\\Common\\\\Annotations\\\\Reader, Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\AttributeReader given\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php

-
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:\\$name\\.$#"
count: 1
Expand Down
11 changes: 2 additions & 9 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -784,9 +784,6 @@
<ArgumentTypeCoercion occurrences="1">
<code>$metadata</code>
</ArgumentTypeCoercion>
<InvalidArgument occurrences="1">
<code>new AttributeReader()</code>
</InvalidArgument>
<InvalidArrayAccess occurrences="4">
<code>$value[0]</code>
<code>$value[0]</code>
Expand All @@ -797,17 +794,13 @@
<code>$mapping</code>
</LessSpecificReturnStatement>
<MoreSpecificReturnType occurrences="1"/>
<NonInvariantDocblockPropertyType occurrences="1">
<NonInvariantDocblockPropertyType occurrences="2">
<code>$entityAnnotationClasses</code>
<code>$reader</code>
</NonInvariantDocblockPropertyType>
<PossiblyNullArgument occurrences="1">
<code>$listenerClassName</code>
</PossiblyNullArgument>
<PossiblyNullIterator occurrences="3">
<code>$joinColumnAttributes</code>
<code>$this-&gt;reader-&gt;getPropertyAnnotation($property, Mapping\InverseJoinColumn::class)</code>
<code>$this-&gt;reader-&gt;getPropertyAnnotation($property, Mapping\JoinColumn::class)</code>
</PossiblyNullIterator>
<RedundantCondition occurrences="3">
<code>assert($method instanceof ReflectionMethod)</code>
<code>assert($method instanceof ReflectionMethod)</code>
Expand Down
3 changes: 3 additions & 0 deletions tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php
CD27
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ protected function loadDriverForCMSModels(): AnnotationDriver
return $annotationDriver;
}

/**
* @return AnnotationDriver
*/
protected function loadDriver(): MappingDriver
{
return $this->createAnnotationDriver();
Expand Down
48 changes: 48 additions & 0 deletions tests/Doctrine/Tests/ORM/Mapping/AttributeReaderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Mapping;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Driver\AttributeReader;
use LogicException;
use PHPUnit\Framework\TestCase;
use ReflectionProperty;

/**
* @requires PHP 8.0
*/
class AttributeReaderTest extends TestCase
{
public function testItThrowsWhenGettingRepeatableAnnotationWithTheWrongMethod(): void
{
$reader = new AttributeReader();
$property = new ReflectionProperty(TestEntity::class, 'id');
$this->expectException(LogicException::class);
$this->expectExceptionMessage(
'The attribute "Doctrine\ORM\Mapping\Index" is repeatable. Call getPropertyAnnotationCollection() instead.'
);
$reader->getPropertyAnnotation($property, ORM\Index::class);
}

public function testItThrowsWhenGettingNonRepeatableAnnotationWithTheWrongMethod(): void
{
$reader = new AttributeReader();
$property = new ReflectionProperty(TestEntity::class, 'id');
$this->expectException(LogicException::class);
$this->expectExceptionMessage(
'The attribute "Doctrine\ORM\Mapping\Id" is not repeatable. Call getPropertyAnnotation() instead.'
);
$reader->getPropertyAnnotationCollection($property, ORM\Id::class);
}
}

#[ORM\Entity]
#[ORM\Index(name: 'bar', columns: ['id'])]
class TestEntity
{
#[ORM\Id, ORM\Column(type: 'integer'), ORM\GeneratedValue]
/** @var int */
public $id;
}
0