ReflectionProcessor::configureBuilder()   B
last analyzed

Complexity

Conditions 9
Paths 6

Size

Total Lines 35
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 9

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 21
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 35
ccs 21
cts 21
cp 1
crap 9
rs 8.0555
1
<?php
2
3
namespace Bdf\Form\Attribute\Processor;
4
5
use Bdf\Form\Aggregate\FormBuilderInterface;
6
use Bdf\Form\Attribute\AttributeForm;
7
use Bdf\Form\Button\ButtonInterface;
8
use Bdf\Form\ElementInterface;
9
use ReflectionClass;
10
use ReflectionNamedType;
11
12
/**
13
 * Base processor using reflection for extract properties and attributes
14
 *
15
 * The configuration action will be delegated to the ReflectionStrategyInterface
16
 * This implementation is only responsive of iterate over class hierarchy and properties
17
 */
18
final class ReflectionProcessor implements AttributesProcessorInterface
19
{
20
    /**
21
     * Strategy to use on each field / class
22
     *
23
     * @var ReflectionStrategyInterface
24
     */
25
    private ReflectionStrategyInterface $strategy;
26
27
    /**
28
     * @param ReflectionStrategyInterface $strategy
29
     */
30 97
    public function __construct(ReflectionStrategyInterface $strategy)
31
    {
32 97
        $this->strategy = $strategy;
33
    }
34
35
    /**
36
     * {@inheritdoc}
37
     */
38 138
    public function configureBuilder(AttributeForm $form, FormBuilderInterface $builder): ?PostConfigureInterface
39
    {
40 138
        $elementProperties = [];
41 138
        $buttonProperties = [];
42
43 138
        foreach ($this->iterateClassHierarchy($form) as $formClass) {
44 138
            $this->strategy->onFormClass($formClass, $form, $builder);
45
46 138
            foreach ($formClass->getProperties() as $property) {
47
                /** @var non-empty-string $name */
48 124
                $name = $property->getName();
49
50
                if (
51 124
                    !$property->hasType()
52 124
                    || !$property->getType() instanceof ReflectionNamedType
53 124
                    || isset($elementProperties[$name])
54 124
                    || isset($buttonProperties[$name])
55
                ) {
56 3
                    continue;
57
                }
58
59 124
                $elementType = $property->getType()->getName();
60 124
                $property->setAccessible(true);
61
62 124
                if ($elementType === ButtonInterface::class) {
63 13
                    $buttonProperties[$name] = $property;
64 13
                    $this->strategy->onButtonProperty($property, $name, $form, $builder);
65 115
                } elseif (is_subclass_of($elementType, ElementInterface::class)) {
66 115
                    $elementProperties[$name] = $property;
67 115
                    $this->strategy->onElementProperty($property, $name, $elementType, $form, $builder);
68
                }
69
            }
70
        }
71
72 138
        return $this->strategy->onPostConfigure($elementProperties, $buttonProperties, $form);
73
    }
74
75
    /**
76
     * Iterate over the class hierarchy of the annotation form
77
     * The iteration will start with the form class, and end with the AttributeForm class (excluded)
78
     *
79
     * @param AttributeForm $form
80
     *
81
     * @return iterable<ReflectionClass<AttributeForm>>
82
     *
83
     * @psalm-suppress MoreSpecificReturnType
84
     */
85 138
    private function iterateClassHierarchy(AttributeForm $form): iterable
86
    {
87 138
        for ($reflection = new ReflectionClass($form); $reflection->getName() !== AttributeForm::class; $reflection = $reflection->getParentClass()) {
88 138
            yield $reflection;
89
        }
90
    }
91
}
92