GenerateConfiguratorStrategy   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 58
c 1
b 0
f 0
dl 0
loc 157
ccs 64
cts 64
cp 1
rs 10
wmc 20

7 Methods

Rating   Name   Duplication   Size   Complexity  
A onButtonProperty() 0 9 2
A __construct() 0 9 1
A onFormClass() 0 7 2
A onElementProperty() 0 21 5
A code() 0 3 1
A registerElementAttributeProcessor() 0 3 1
B onPostConfigure() 0 52 8
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\Attribute\Button\ButtonBuilderAttributeInterface;
8
use Bdf\Form\Attribute\ChildBuilderAttributeInterface;
9
use Bdf\Form\Attribute\Form\FormBuilderAttributeInterface;
10
use Bdf\Form\Attribute\Processor\CodeGenerator\AttributesProcessorGenerator;
11
use Bdf\Form\Attribute\Processor\Element\ConstraintAttributeProcessor;
12
use Bdf\Form\Attribute\Processor\Element\ElementAttributeProcessorInterface;
13
use Bdf\Form\Attribute\Processor\Element\ExtractorAttributeProcessor;
14
use Bdf\Form\Attribute\Processor\Element\FilterAttributeProcessor;
15
use Bdf\Form\Attribute\Processor\Element\HydratorAttributeProcessor;
16
use Bdf\Form\Attribute\Processor\Element\TransformerAttributeProcessor;
17
use Nette\PhpGenerator\Closure;
18
use Nette\PhpGenerator\Literal;
19
use ReflectionAttribute;
20
use ReflectionClass;
21
use ReflectionProperty;
22
23
/**
24
 * Strategy for generate the processor class code
25
 */
26
final class GenerateConfiguratorStrategy implements ReflectionStrategyInterface
27
{
28
    private AttributesProcessorGenerator $generator;
29
30
    /**
31
     * @var list<ElementAttributeProcessorInterface>
0 ignored issues
show
Bug introduced by
The type Bdf\Form\Attribute\Processor\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
32
     */
33
    private array $elementProcessors = [];
34
35
    /**
36
     * @param non-empty-string $className The class name to generate. Must have a namespace
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
37
     * @throws \InvalidArgumentException If a namespace is not provided, or if the class name is not valid
38
     */
39 84
    public function __construct(string $className)
40
    {
41 84
        $this->generator = new AttributesProcessorGenerator($className);
42
43 84
        $this->registerElementAttributeProcessor(new ConstraintAttributeProcessor());
44 84
        $this->registerElementAttributeProcessor(new FilterAttributeProcessor());
45 84
        $this->registerElementAttributeProcessor(new TransformerAttributeProcessor());
46 84
        $this->registerElementAttributeProcessor(new HydratorAttributeProcessor());
47 84
        $this->registerElementAttributeProcessor(new ExtractorAttributeProcessor());
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53 84
    public function onFormClass(ReflectionClass $formClass, AttributeForm $form, FormBuilderInterface $builder): void
54
    {
55 84
        foreach ($formClass->getAttributes(FormBuilderAttributeInterface::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
56 13
            $attribute->newInstance()->generateCodeForFormBuilder($this->generator, $form);
57
        }
58
59 84
        $this->generator->line();
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65 7
    public function onButtonProperty(ReflectionProperty $property, string $name, AttributeForm $form, FormBuilderInterface $builder): void
66
    {
67 7
        $this->generator->line('$builder->submit(?)', [$name]);
68
69 7
        foreach ($property->getAttributes(ButtonBuilderAttributeInterface::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
70 5
            $attribute->newInstance()->generateCodeForButtonBuilder($this->generator, $form);
71
        }
72
73 7
        $this->generator->line(";\n");
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79 69
    public function onElementProperty(ReflectionProperty $property, string $name, string $elementType, AttributeForm $form, FormBuilderInterface $builder): void
80
    {
81 69
        $elementType = $this->generator->useAndSimplifyType($elementType);
82 69
        $this->generator->line('$? = $builder->add(?, ?::class);', [$name, $name, new Literal($elementType)]);
83
84 69
        foreach ($property->getAttributes() as $attribute) {
85 68
            if (is_subclass_of($attribute->getName(), ChildBuilderAttributeInterface::class)) {
86
                /** @var ChildBuilderAttributeInterface $attributeInstance */
87 50
                $attributeInstance = $attribute->newInstance();
88 50
                $attributeInstance->generateCodeForChildBuilder($name, $this->generator, $form);
89 50
                continue;
90
            }
91
92 28
            foreach ($this->elementProcessors as $configurator) {
93 28
                if (is_subclass_of($attribute->getName(), $configurator->type())) {
94 28
                    $configurator->generateCode($name, $this->generator, $attribute);
95
                }
96
            }
97
        }
98
99 69
        $this->generator->line(); // Add empty line
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105 84
    public function onPostConfigure(array $elementProperties, array $buttonProperties, AttributeForm $form): ?PostConfigureInterface
106
    {
107 84
        $this->generator->line('return $this;');
108
109 84
        $method = $this->generator
110 84
            ->implements(PostConfigureInterface::class)
111 84
            ->implementsMethod(PostConfigureInterface::class, 'postConfigure')
112 84
        ;
113
114 84
        if (!empty($buttonProperties)) {
115 7
            $method->addBody('$root = $form->root();');
116
        }
117
118 84
        $scopedProperties = [];
119
120 84
        foreach ($elementProperties as $name => $property) {
121 69
            if ($property->isPublic()) {
122 64
                $method->addBody('$form->? = $inner[?]->element();', [$name, $name]);
123
            } else {
124 5
                $scopedProperties[$property->getDeclaringClass()->getName()][$name] = ['$form->? = $inner[?]->element();', [$name, $name]];
125
            }
126
        }
127
128 84
        foreach ($buttonProperties as $name => $property) {
129 7
            if ($property->isPublic()) {
130 6
                $method->addBody('$form->? = $root->button(?);', [$name, $name]);
131
            } else {
132 1
                $scopedProperties[$property->getDeclaringClass()->getName()][$name] = ['$form->? = $root->button(?);', [$name, $name]];
133
            }
134
        }
135
136 84
        foreach ($scopedProperties as $className => $lines) {
137 5
            $closure = new Closure();
138 5
            $closure->addUse('inner');
139 5
            $closure->addUse('form');
140
141 5
            if (!empty($buttonProperties)) {
142 1
                $closure->addUse('root');
143
            }
144
145 5
            array_map(fn ($line) => $closure->addBody(...$line), $lines);
0 ignored issues
show
Bug introduced by
$line is expanded, but the parameter $code of Nette\PhpGenerator\Closure::addBody() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

145
            array_map(fn ($line) => $closure->addBody(/** @scrutinizer ignore-type */ ...$line), $lines);
Loading history...
146
147 5
            $method->addBody(
148 5
                '(\Closure::bind(?, null, ?::class))();',
149 5
                [
150 5
                    new Literal($this->generator->printer()->printClosure($closure)),
151 5
                    new Literal($this->generator->useAndSimplifyType($className)),
152 5
                ]
153 5
            );
154
        }
155
156 84
        return null;
157
    }
158
159
    /**
160
     * Print the generated class code
161
     *
162
     * @return string
163
     *
164
     * @see AttributesProcessorGenerator::print()
165
     */
166 84
    public function code(): string
167
    {
168 84
        return $this->generator->print();
169
    }
170
171
    /**
172
     * Register a new processor for element attributes
173
     *
174
     * @param ElementAttributeProcessorInterface<T> $processor
175
     *
176
     * @return void
177
     *
178
     * @template T as object
179
     */
180 84
    private function registerElementAttributeProcessor(ElementAttributeProcessorInterface $processor): void
181
    {
182 84
        $this->elementProcessors[] = $processor;
183
    }
184
}
185