Skip to content

Commit

Permalink
[GH-8265] Prototype for Attribute Metadata Driver
Browse files Browse the repository at this point in the history
  • Loading branch information
beberlei committed Nov 15, 2020
1 parent 3d46e07 commit 22f0353
Show file tree
Hide file tree
Showing 40 changed files with 794 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/Doctrine/ORM/Mapping/AttributeOverride.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

namespace Doctrine\ORM\Mapping;

use Attribute;

/**
* This annotation is used to override the mapping of a entity property.
*
Expand All @@ -28,6 +30,7 @@
* @Annotation
* @Target("ANNOTATION")
*/
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
final class AttributeOverride implements Annotation
{
/**
Expand Down
3 changes: 3 additions & 0 deletions lib/Doctrine/ORM/Mapping/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

namespace Doctrine\ORM\Mapping;

use Attribute;

/**
* Caching to an entity or a collection.
*
Expand All @@ -28,6 +30,7 @@
* @Annotation
* @Target({"CLASS","PROPERTY"})
*/
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
final class Cache implements Annotation
{
/**
Expand Down
3 changes: 3 additions & 0 deletions lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@

namespace Doctrine\ORM\Mapping;

use Attribute;

/**
* @Annotation
* @Target("CLASS")
*/
#[Attribute(Attribute::TARGET_CLASS)]
final class ChangeTrackingPolicy implements Annotation
{
/**
Expand Down
3 changes: 3 additions & 0 deletions lib/Doctrine/ORM/Mapping/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@

namespace Doctrine\ORM\Mapping;

use Attribute;

/**
* @Annotation
* @Target({"PROPERTY","ANNOTATION"})
*/
#[Attribute(Attribute::TARGET_PROPERTY)]
final class Column implements Annotation
{
/**
Expand Down
3 changes: 3 additions & 0 deletions lib/Doctrine/ORM/Mapping/CustomIdGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@

namespace Doctrine\ORM\Mapping;

use Attribute;

/**
* @Annotation
* @Target("PROPERTY")
*/
#[Attribute(Attribute::TARGET_PROPERTY)]
final class CustomIdGenerator implements Annotation
{
/**
Expand Down
3 changes: 3 additions & 0 deletions lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@

namespace Doctrine\ORM\Mapping;

use Attribute;

/**
* @Annotation
* @Target("CLASS")
*/
#[Attribute(Attribute::TARGET_CLASS)]
final class DiscriminatorColumn implements Annotation
{
/**
Expand Down
3 changes: 3 additions & 0 deletions lib/Doctrine/ORM/Mapping/DiscriminatorMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@

namespace Doctrine\ORM\Mapping;

use Attribute;

/**
* @Annotation
* @Target("CLASS")
*/
#[Attribute(Attribute::TARGET_CLASS)]
final class DiscriminatorMap implements Annotation
{
/**
Expand Down
87 changes: 87 additions & 0 deletions lib/Doctrine/ORM/Mapping/Driver/AttributeReader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace Doctrine\ORM\Mapping\Driver;

use Doctrine\Common\Annotations\Reader;
use Doctrine\ORM\Mapping\Annotation;

// TODO: Should we move this to doctrine/annotations?
class AttributeReader implements Reader
{
/** @var array<string,bool> */
private $isRepeatableAttribute = [];

function getClassAnnotations(\ReflectionClass $class)
{
return $this->convertToAttributeInstances($class->getAttributes());
}

function getClassAnnotation(\ReflectionClass $class, $annotationName)
{
return $this->getClassAnnotations($class)[$annotationName] ?? ($this->isRepeatable($annotationName) ? [] : null);
}

function getMethodAnnotations(\ReflectionMethod $method)
{
return $this->convertToAttributeInstances($method->getAttributes());
}

function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
{
return $this->getMethodAnnotations($method)[$annotationName] ?? ($this->isRepeatable($annotationName) ? [] : null);
}

function getPropertyAnnotations(\ReflectionProperty $property)
{
return $this->convertToAttributeInstances($property->getAttributes());
}

function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
{
return $this->getPropertyAnnotations($property)[$annotationName] ?? ($this->isRepeatable($annotationName) ? [] : null);
}

private function convertToAttributeInstances(array $attributes)
{
$instances = [];

foreach ($attributes as $attribute) {
// Make sure we only get Doctrine Annotations
if (is_subclass_of($attribute->getName(), Annotation::class)) {
$instance = new $attribute->getName();
$arguments = $attribute->getArguments();

// unnamed argument is automatically "value" in Doctrine Annotations
if (count($arguments) >= 1 && isset($arguments[0])) {
$arguments['value'] = $arguments[0];
unset($arguments[0]);
}

// This works using the old Annotation, but will probably break Attribute IDE autocomplete support
foreach ($arguments as $name => $value) {
$instance->$name = $value;
}

if ($this->isRepeatable($attribute->getName())) {
$instances[$attribute->getName()][] = $instance;
} else {
$instances[$attribute->getName()] = $instance;
}
}
}

return $instances;
}

private function isRepeatable(string $attributeClassName) : bool
{
if (isset($this->isRepeatableAttribute[$attributeClassName])) {
return $this->isRepeatableAttribute[$attributeClassName];
}

$reflectionClass = new \ReflectionClass($attributeClassName);
$attribute = $reflectionClass->getAttributes()[0];

return $this->isRepeatableAttribute[$attributeClassName] = $attribute->flags && Attribute::IS_REPEATABLE;
}
}
Loading

0 comments on commit 22f0353

Please sign in to comment.