Skip to content

Commit 8624e1d

Browse files
authored
Support FromReflection*() on sub-attributes (#14)
* Support reflection on sub-attributes on properties. * Minor refactor. * Expand subattribute reflection to all component types. * Optimize imports.
1 parent f97bdcf commit 8624e1d

26 files changed

Lines changed: 431 additions & 60 deletions

src/ReflectionDefinitionBuilder.php

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,7 @@ public function loadSubAttributes(?object $attribute, \Reflector $reflection): v
130130
if ($this->isMultivalueAttribute($type)) {
131131
$subs = $this->parser->getInheritedAttributes($reflection, $type);
132132
foreach ($subs as $sub) {
133-
if ($sub instanceof Finalizable) {
134-
$sub->finalize();
135-
}
136-
$this->loadSubAttributes($sub, $reflection);
133+
$this->applySubattributeFeatures($sub, $reflection);
137134
}
138135
if ($callback instanceof \Closure) {
139136
$callback($subs);
@@ -142,10 +139,9 @@ public function loadSubAttributes(?object $attribute, \Reflector $reflection): v
142139
}
143140
} else {
144141
$sub = $this->parser->getInheritedAttribute($reflection, $type);
145-
if ($sub instanceof Finalizable) {
146-
$sub->finalize();
142+
if ($sub) {
143+
$this->applySubattributeFeatures($sub, $reflection);
147144
}
148-
$this->loadSubAttributes($sub, $reflection);
149145
if ($callback instanceof \Closure) {
150146
$callback($sub);
151147
} else {
@@ -156,6 +152,39 @@ public function loadSubAttributes(?object $attribute, \Reflector $reflection): v
156152
}
157153
}
158154

155+
protected function applySubattributeFeatures(object $attribute, \Reflector $reflection): void
156+
{
157+
// For each possible type, check for a FromReflection interface.
158+
if ($reflection instanceof \ReflectionClass && ! $reflection instanceof \ReflectionEnum && $attribute instanceof FromReflectionClass) {
159+
/** @var \ReflectionClass<object> $reflection */
160+
$attribute->fromReflection($reflection);
161+
}
162+
if ($reflection instanceof \ReflectionEnum && $attribute instanceof FromReflectionEnum) {
163+
$attribute->fromReflection($reflection);
164+
}
165+
if ($reflection instanceof \ReflectionFunction && $attribute instanceof FromReflectionFunction) {
166+
$attribute->fromReflection($reflection);
167+
}
168+
if ($reflection instanceof \ReflectionProperty && $attribute instanceof FromReflectionProperty) {
169+
$attribute->fromReflection($reflection);
170+
}
171+
if ($reflection instanceof \ReflectionMethod && $attribute instanceof FromReflectionMethod) {
172+
$attribute->fromReflection($reflection);
173+
}
174+
if ($reflection instanceof \ReflectionClassConstant && $attribute instanceof FromReflectionClassConstant) {
175+
$attribute->fromReflection($reflection);
176+
}
177+
if ($reflection instanceof \ReflectionParameter && $attribute instanceof FromReflectionParameter) {
178+
$attribute->fromReflection($reflection);
179+
}
180+
181+
if ($attribute instanceof Finalizable) {
182+
$attribute->finalize();
183+
}
184+
// Call recursively to allow sub-attributes on sub-attributes. (Yo Dawg.)
185+
$this->loadSubAttributes($attribute, $reflection);
186+
}
187+
159188
/**
160189
* Determines if a given attribute class allows repeating.
161190
*

tests/Attributes/ClassWithOwnSubAttributes.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
use Attribute;
88
use Crell\AttributeUtils\HasSubAttributes;
9-
use Crell\AttributeUtils\ParseProperties;
109

1110
#[Attribute(Attribute::TARGET_CLASS)]
1211
class ClassWithOwnSubAttributes implements HasSubAttributes

tests/Attributes/ClassWithPropertiesWithSubAttributes.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function includePropertiesByDefault(): bool
3131

3232
public function propertyAttribute(): string
3333
{
34-
return PropertyWithSubAttributes::class;
34+
return ConfigurablePropertyWithSubAttributes::class;
3535
}
3636

3737
public function subAttributes(): array

tests/Attributes/ClassWithSubSubAttributeLevelOne.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
namespace Crell\AttributeUtils\Attributes;
66

7-
use \Attribute;
7+
use Attribute;
88
use Crell\AttributeUtils\HasSubAttributes;
9+
910
use function Crell\fp\amap;
1011
use function Crell\fp\prop;
1112

tests/Attributes/ClassWithSubSubAttributeLevelTwo.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Crell\AttributeUtils\Attributes;
66

77
use Attribute;
8-
use Crell\AttributeUtils\HasSubAttributes;
98

109
#[Attribute(Attribute::TARGET_CLASS)]
1110
class ClassWithSubSubAttributeLevelTwo

tests/Attributes/ClassWithSubSubAttributeLevelTwoMulti.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Crell\AttributeUtils\Attributes;
66

77
use Attribute;
8-
use Crell\AttributeUtils\HasSubAttributes;
98
use Crell\AttributeUtils\Multivalue;
109

1110
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]

tests/Attributes/ClassWithSubSubAttributes.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace Crell\AttributeUtils\Attributes;
66

7-
use \Attribute;
7+
use Attribute;
88
use Crell\AttributeUtils\HasSubAttributes;
99

1010
#[Attribute(Attribute::TARGET_CLASS)]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Crell\AttributeUtils\Attributes;
6+
7+
use Attribute;
8+
use Crell\AttributeUtils\ParseProperties;
9+
10+
#[Attribute(Attribute::TARGET_CLASS)]
11+
class ConfigurableClassWithProperties implements ParseProperties
12+
{
13+
/**
14+
* @var array<string, ?object>
15+
*/
16+
public array $properties = [];
17+
18+
public function __construct(
19+
public string $propertyAttribute,
20+
public bool $includeByDefault = false,
21+
public string $a = 'A',
22+
) {}
23+
24+
public function setProperties(array $properties): void
25+
{
26+
$this->properties = $properties;
27+
}
28+
29+
public function includePropertiesByDefault(): bool
30+
{
31+
return $this->includeByDefault;
32+
}
33+
34+
public function propertyAttribute(): string
35+
{
36+
return $this->propertyAttribute;
37+
}
38+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Crell\AttributeUtils\Attributes;
6+
7+
use Attribute;
8+
use Crell\AttributeUtils\HasSubAttributes;
9+
10+
#[Attribute(Attribute::TARGET_PROPERTY)]
11+
class ConfigurablePropertyWithSubAttributes implements HasSubAttributes
12+
{
13+
public ?object $subattrib;
14+
15+
public function __construct(
16+
public string $subattribute = PropertySubAttribute::class,
17+
public string $a = 'A',
18+
) {}
19+
20+
public function subAttributes(): array
21+
{
22+
return [$this->subattribute => 'fromSubAttribute'];
23+
}
24+
25+
public function fromSubAttribute(?object $sub): void
26+
{
27+
$sub ??= new ($this->subattribute)();
28+
$this->subattrib = $sub;
29+
}
30+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Crell\AttributeUtils\Attributes;
6+
7+
use Crell\AttributeUtils\FromReflectionProperty;
8+
9+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
10+
class PropertySubAttributeWithReflection implements FromReflectionProperty
11+
{
12+
public string $name;
13+
14+
public function fromReflection(\ReflectionProperty $subject): void
15+
{
16+
$this->name = $subject->getName();
17+
}
18+
}

0 commit comments

Comments
 (0)