From 78a914c68b50535b5db2f782dad6e7d8237bb0fb Mon Sep 17 00:00:00 2001 From: Seifane Idouchach Date: Thu, 10 Mar 2022 19:35:18 +0100 Subject: [PATCH 1/6] Updated to attributes --- composer.json | 2 +- src/Mappings/BelongsToOrganisation.php | 2 ++ src/Mappings/BelongsToOrganisations.php | 2 ++ src/Mappings/HasPermissions.php | 2 ++ src/Mappings/HasRoles.php | 2 ++ 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3ad1468..d94bbc9 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ } ], "require": { - "php": "^7.2.5|^8.0", + "php": "^8", "illuminate/auth": "^6.0|^7.0|^8.0|^9.0", "illuminate/config": "^6.0|^7.0|^8.0|^9.0", "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0", diff --git a/src/Mappings/BelongsToOrganisation.php b/src/Mappings/BelongsToOrganisation.php index d61c341..ce376b6 100644 --- a/src/Mappings/BelongsToOrganisation.php +++ b/src/Mappings/BelongsToOrganisation.php @@ -2,6 +2,7 @@ namespace LaravelDoctrine\ACL\Mappings; +use Attribute; use Doctrine\Common\Annotations\Annotation; use Illuminate\Contracts\Config\Repository; @@ -9,6 +10,7 @@ * @Annotation * @Target("PROPERTY") */ +#[Attribute(Attribute::TARGET_PROPERTY)] final class BelongsToOrganisation extends RelationAnnotation { /** diff --git a/src/Mappings/BelongsToOrganisations.php b/src/Mappings/BelongsToOrganisations.php index 20e114e..832c0ec 100644 --- a/src/Mappings/BelongsToOrganisations.php +++ b/src/Mappings/BelongsToOrganisations.php @@ -2,6 +2,7 @@ namespace LaravelDoctrine\ACL\Mappings; +use Attribute; use Doctrine\Common\Annotations\Annotation; use Illuminate\Contracts\Config\Repository; @@ -9,6 +10,7 @@ * @Annotation * @Target("PROPERTY") */ +#[Attribute(Attribute::TARGET_PROPERTY)] final class BelongsToOrganisations extends RelationAnnotation { /** diff --git a/src/Mappings/HasPermissions.php b/src/Mappings/HasPermissions.php index 542656a..d4b7e45 100644 --- a/src/Mappings/HasPermissions.php +++ b/src/Mappings/HasPermissions.php @@ -2,6 +2,7 @@ namespace LaravelDoctrine\ACL\Mappings; +use Attribute; use Doctrine\Common\Annotations\Annotation; use Illuminate\Contracts\Config\Repository; @@ -9,6 +10,7 @@ * @Annotation * @Target("PROPERTY") */ +#[Attribute(Attribute::TARGET_PROPERTY)] final class HasPermissions extends RelationAnnotation { /** diff --git a/src/Mappings/HasRoles.php b/src/Mappings/HasRoles.php index 1120cd8..deec68f 100644 --- a/src/Mappings/HasRoles.php +++ b/src/Mappings/HasRoles.php @@ -2,6 +2,7 @@ namespace LaravelDoctrine\ACL\Mappings; +use Attribute; use Doctrine\Common\Annotations\Annotation; use Illuminate\Contracts\Config\Repository; @@ -9,6 +10,7 @@ * @Annotation * @Target("PROPERTY") */ +#[Attribute(Attribute::TARGET_PROPERTY)] final class HasRoles extends RelationAnnotation { /** From 9b82a5b0bc8a44e63442d2816c51462c7f4aee01 Mon Sep 17 00:00:00 2001 From: Seifane Idouchach Date: Thu, 10 Mar 2022 19:45:27 +0100 Subject: [PATCH 2/6] attributes --- src/Mappings/RelationAnnotation.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mappings/RelationAnnotation.php b/src/Mappings/RelationAnnotation.php index 6482399..9a417e8 100644 --- a/src/Mappings/RelationAnnotation.php +++ b/src/Mappings/RelationAnnotation.php @@ -2,9 +2,9 @@ namespace LaravelDoctrine\ACL\Mappings; -use Doctrine\Common\Annotations\Annotation; +use Doctrine\ORM\Mapping\Annotation; -abstract class RelationAnnotation extends Annotation implements ConfigAnnotation +abstract class RelationAnnotation implements ConfigAnnotation, Annotation { /** * @var string From 58ec67379958537332eae9b650a074b4759f6d06 Mon Sep 17 00:00:00 2001 From: Seifane Idouchach Date: Thu, 10 Mar 2022 20:24:02 +0100 Subject: [PATCH 3/6] attributes --- src/Mappings/HasRoles.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Mappings/HasRoles.php b/src/Mappings/HasRoles.php index deec68f..3a0c39b 100644 --- a/src/Mappings/HasRoles.php +++ b/src/Mappings/HasRoles.php @@ -16,7 +16,29 @@ final class HasRoles extends RelationAnnotation /** * @var string */ - public $inversedBy = 'users'; + public $inversedBy; + + /** + * @param string $inversedBy + */ + public function __construct( + ?string $targetEntity = null, + ?array $cascade = null, + string $fetch = 'LAZY', + bool $orphanRemoval = false, + ?string $indexBy = null, + ?string $mappedBy = null, + string $inversedBy = 'users' + ) { + $this->targetEntity = $targetEntity; + $this->cascade = $cascade; + $this->fetch = $fetch; + $this->orphanRemoval = $orphanRemoval; + $this->indexBy = $indexBy; + $this->mappedBy = $mappedBy; + $this->inversedBy = $inversedBy; + } + /** * @param Repository $config From ad58845e540ce2e6d3355ae44ed849e11090bec6 Mon Sep 17 00:00:00 2001 From: Seifane Idouchach Date: Thu, 10 Mar 2022 22:08:51 +0100 Subject: [PATCH 4/6] Added attribute reader implementations --- .../Readers/AttributeAnnotationReader.php | 103 ++++++++++++++++++ src/Mappings/Readers/AttributeReader.php | 95 ++++++++++++++++ .../Subscribers/MappedEventSubscriber.php | 8 +- 3 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 src/Mappings/Readers/AttributeAnnotationReader.php create mode 100644 src/Mappings/Readers/AttributeReader.php diff --git a/src/Mappings/Readers/AttributeAnnotationReader.php b/src/Mappings/Readers/AttributeAnnotationReader.php new file mode 100644 index 0000000..1f91888 --- /dev/null +++ b/src/Mappings/Readers/AttributeAnnotationReader.php @@ -0,0 +1,103 @@ +attributeReader = $attributeReader; + $this->annotationReader = $annotationReader; + } + + /** + * @return Annotation[] + */ + public function getClassAnnotations(ReflectionClass $class): array + { + $annotations = $this->attributeReader->getClassAnnotations($class); + + if ([] !== $annotations) { + return $annotations; + } + + return $this->annotationReader->getClassAnnotations($class); + } + + /** + * @param class-string $annotationName the name of the annotation + * + * @return T|null the Annotation or NULL, if the requested annotation does not exist + * + * @template T + */ + public function getClassAnnotation(ReflectionClass $class, $annotationName) + { + $annotation = $this->attributeReader->getClassAnnotation($class, $annotationName); + + if (null !== $annotation) { + return $annotation; + } + + return $this->annotationReader->getClassAnnotation($class, $annotationName); + } + + /** + * @return Annotation[] + */ + public function getPropertyAnnotations(\ReflectionProperty $property): array + { + $propertyAnnotations = $this->attributeReader->getPropertyAnnotations($property); + + if ([] !== $propertyAnnotations) { + return $propertyAnnotations; + } + + return $this->annotationReader->getPropertyAnnotations($property); + } + + /** + * @param class-string $annotationName the name of the annotation + * + * @return T|null the Annotation or NULL, if the requested annotation does not exist + * + * @template T + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + $annotation = $this->attributeReader->getPropertyAnnotation($property, $annotationName); + + if (null !== $annotation) { + return $annotation; + } + + return $this->annotationReader->getPropertyAnnotation($property, $annotationName); + } + + public function getMethodAnnotations(ReflectionMethod $method): array + { + throw new \BadMethodCallException('Not implemented'); + } + + /** + * @return mixed + */ + public function getMethodAnnotation(ReflectionMethod $method, $annotationName) + { + throw new \BadMethodCallException('Not implemented'); + } +} diff --git a/src/Mappings/Readers/AttributeReader.php b/src/Mappings/Readers/AttributeReader.php new file mode 100644 index 0000000..cc7c7bd --- /dev/null +++ b/src/Mappings/Readers/AttributeReader.php @@ -0,0 +1,95 @@ + */ + private $isRepeatableAttribute = []; + + /** + * @return array + */ + public function getClassAnnotations(ReflectionClass $class): array + { + return $this->convertToAttributeInstances($class->getAttributes()); + } + + /** + * @phpstan-param class-string $annotationName + * + * @return Annotation|Annotation[]|null + */ + public function getClassAnnotation(ReflectionClass $class, string $annotationName) + { + return $this->getClassAnnotations($class)[$annotationName] ?? null; + } + + /** + * @return array + */ + public function getPropertyAnnotations(\ReflectionProperty $property): array + { + return $this->convertToAttributeInstances($property->getAttributes()); + } + + /** + * @phpstan-param class-string $annotationName + * + * @return Annotation|Annotation[]|null + */ + public function getPropertyAnnotation(\ReflectionProperty $property, string $annotationName) + { + return $this->getPropertyAnnotations($property)[$annotationName] ?? null; + } + + /** + * @param array<\ReflectionAttribute> $attributes + * + * @return array + */ + private function convertToAttributeInstances(array $attributes): array + { + $instances = []; + + foreach ($attributes as $attribute) { + $attributeName = $attribute->getName(); + assert(is_string($attributeName)); + // Make sure we only get Gedmo Annotations + if (!is_subclass_of($attributeName, Annotation::class)) { + continue; + } + + $instance = $attribute->newInstance(); + assert($instance instanceof Annotation); + + if ($this->isRepeatable($attributeName)) { + if (!isset($instances[$attributeName])) { + $instances[$attributeName] = []; + } + + $instances[$attributeName][] = $instance; + } else { + $instances[$attributeName] = $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]->newInstance(); + + return $this->isRepeatableAttribute[$attributeClassName] = ($attribute->flags & Attribute::IS_REPEATABLE) > 0; + } +} diff --git a/src/Mappings/Subscribers/MappedEventSubscriber.php b/src/Mappings/Subscribers/MappedEventSubscriber.php index 40ea1d1..b93a5cb 100644 --- a/src/Mappings/Subscribers/MappedEventSubscriber.php +++ b/src/Mappings/Subscribers/MappedEventSubscriber.php @@ -2,6 +2,7 @@ namespace LaravelDoctrine\ACL\Mappings\Subscribers; +use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\Reader; use Doctrine\Common\EventSubscriber; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; @@ -9,6 +10,8 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Illuminate\Contracts\Config\Repository; use LaravelDoctrine\ACL\Mappings\ConfigAnnotation; +use LaravelDoctrine\ACL\Mappings\Readers\AttributeAnnotationReader; +use LaravelDoctrine\ACL\Mappings\Readers\AttributeReader; use ReflectionClass; use ReflectionProperty; @@ -52,7 +55,10 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) $metadata = $eventArgs->getClassMetadata(); if (! $this->reader) { - return; + $this->reader = new AttributeAnnotationReader( + new AttributeReader(), + new AnnotationReader() + ); } if ($this->isInstantiable($metadata) && $this->shouldBeMapped($metadata)) { From 0d551d1427308a4766cf7eb1799d12988a1ea379 Mon Sep 17 00:00:00 2001 From: Seifane Idouchach Date: Thu, 10 Mar 2022 22:21:02 +0100 Subject: [PATCH 5/6] reverting wrong changes --- src/Mappings/HasRoles.php | 24 +----------------------- src/Mappings/RelationAnnotation.php | 4 +--- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/Mappings/HasRoles.php b/src/Mappings/HasRoles.php index 3a0c39b..deec68f 100644 --- a/src/Mappings/HasRoles.php +++ b/src/Mappings/HasRoles.php @@ -16,29 +16,7 @@ final class HasRoles extends RelationAnnotation /** * @var string */ - public $inversedBy; - - /** - * @param string $inversedBy - */ - public function __construct( - ?string $targetEntity = null, - ?array $cascade = null, - string $fetch = 'LAZY', - bool $orphanRemoval = false, - ?string $indexBy = null, - ?string $mappedBy = null, - string $inversedBy = 'users' - ) { - $this->targetEntity = $targetEntity; - $this->cascade = $cascade; - $this->fetch = $fetch; - $this->orphanRemoval = $orphanRemoval; - $this->indexBy = $indexBy; - $this->mappedBy = $mappedBy; - $this->inversedBy = $inversedBy; - } - + public $inversedBy = 'users'; /** * @param Repository $config diff --git a/src/Mappings/RelationAnnotation.php b/src/Mappings/RelationAnnotation.php index 9a417e8..78905f0 100644 --- a/src/Mappings/RelationAnnotation.php +++ b/src/Mappings/RelationAnnotation.php @@ -2,9 +2,7 @@ namespace LaravelDoctrine\ACL\Mappings; -use Doctrine\ORM\Mapping\Annotation; - -abstract class RelationAnnotation implements ConfigAnnotation, Annotation +abstract class RelationAnnotation implements ConfigAnnotation { /** * @var string From b5ee5f7fe10a4d055efd03585bb57728adca15d8 Mon Sep 17 00:00:00 2001 From: Seifane Idouchach Date: Thu, 10 Mar 2022 23:46:37 +0100 Subject: [PATCH 6/6] allow php 7.2+ --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d94bbc9..c8f772f 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ } ], "require": { - "php": "^8", + "php": "^7.2.5|^8", "illuminate/auth": "^6.0|^7.0|^8.0|^9.0", "illuminate/config": "^6.0|^7.0|^8.0|^9.0", "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0",