Passed
Push — master ( 9d312b...da6698 )
by Vincent
04:40
created

Registry   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 194
Duplicated Lines 0 %

Test Coverage

Coverage 96.36%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 24
eloc 63
c 1
b 0
f 0
dl 0
loc 194
ccs 53
cts 55
cp 0.9636
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A filter() 0 13 3
A childBuilder() 0 12 2
A constraint() 0 24 5
A __construct() 0 5 1
A transformer() 0 15 4
A register() 0 6 2
A buttonBuilder() 0 3 1
A elementBuilder() 0 25 6
1
<?php
2
3
namespace Bdf\Form\Registry;
4
5
use Bdf\Form\Aggregate\ArrayElement;
6
use Bdf\Form\Aggregate\ArrayElementBuilder;
7
use Bdf\Form\Aggregate\Form;
8
use Bdf\Form\Aggregate\FormBuilder;
9
use Bdf\Form\Button\ButtonBuilderInterface;
10
use Bdf\Form\Button\SubmitButtonBuilder;
11
use Bdf\Form\Child\ChildBuilder;
12
use Bdf\Form\Child\ChildBuilderInterface;
13
use Bdf\Form\Constraint\Closure;
14
use Bdf\Form\Csrf\CsrfElement;
15
use Bdf\Form\Csrf\CsrfElementBuilder;
16
use Bdf\Form\Custom\CustomForm;
17
use Bdf\Form\Custom\CustomFormBuilder;
18
use Bdf\Form\ElementBuilderInterface;
19
use Bdf\Form\Filter\ClosureFilter;
20
use Bdf\Form\Filter\FilterInterface;
21
use Bdf\Form\Leaf\BooleanElement;
22
use Bdf\Form\Leaf\BooleanElementBuilder;
23
use Bdf\Form\Leaf\Date\DateTimeChildBuilder;
24
use Bdf\Form\Leaf\Date\DateTimeElement;
25
use Bdf\Form\Leaf\Date\DateTimeElementBuilder;
26
use Bdf\Form\Leaf\FloatElement;
27
use Bdf\Form\Leaf\FloatElementBuilder;
28
use Bdf\Form\Leaf\Helper\EmailElement;
29
use Bdf\Form\Leaf\Helper\EmailElementBuilder;
30
use Bdf\Form\Leaf\Helper\UrlElement;
31
use Bdf\Form\Leaf\Helper\UrlElementBuilder;
32
use Bdf\Form\Leaf\IntegerElement;
33
use Bdf\Form\Leaf\IntegerElementBuilder;
34
use Bdf\Form\Leaf\StringElement;
35
use Bdf\Form\Leaf\StringElementBuilder;
36
use Bdf\Form\Phone\PhoneChildBuilder;
37
use Bdf\Form\Phone\PhoneElement;
38
use Bdf\Form\Phone\PhoneElementBuilder;
39
use Bdf\Form\Transformer\ClosureTransformer;
40
use Bdf\Form\Transformer\DataTransformerAdapter;
41
use Bdf\Form\Transformer\TransformerInterface;
42
use InvalidArgumentException;
43
use LogicException;
44
use Symfony\Component\Form\DataTransformerInterface;
45
use Symfony\Component\Validator\Constraint;
46
47
/**
48
 * Base registry interface
49
 */
50
final class Registry implements RegistryInterface
51
{
52
    /**
53
     * @var class-string<ElementBuilderInterface>[]|callable[]
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<ElementBuil...Interface>[]|callable[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<ElementBuilderInterface>[]|callable[].
Loading history...
54
     */
55
    private $elementBuilderFactories = [
56
        StringElement::class => StringElementBuilder::class,
57
        IntegerElement::class => IntegerElementBuilder::class,
58
        FloatElement::class => FloatElementBuilder::class,
59
        BooleanElement::class => BooleanElementBuilder::class,
60
61
        EmailElement::class => EmailElementBuilder::class,
62
        UrlElement::class => UrlElementBuilder::class,
63
64
        CsrfElement::class => CsrfElementBuilder::class,
65
        PhoneElement::class => PhoneElementBuilder::class,
66
67
        DateTimeElement::class => DateTimeElementBuilder::class,
68
69
        ArrayElement::class => ArrayElementBuilder::class,
70
        Form::class => FormBuilder::class,
71
    ];
72
73
    /**
74
     * @var class-string<ChildBuilderInterface>[]|callable[]
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<ChildBuilderInterface>[]|callable[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<ChildBuilderInterface>[]|callable[].
Loading history...
75
     */
76
    private $childBuilderFactories = [
77
        DateTimeElement::class => DateTimeChildBuilder::class,
78
        PhoneElement::class => PhoneChildBuilder::class,
79
    ];
80
81
    /**
82
     * Registry constructor.
83
     */
84 336
    public function __construct()
85
    {
86
        $this->register(CustomForm::class, function (RegistryInterface $registry, string $formClass) {
87
            /** @psalm-suppress ArgumentTypeCoercion */
88 5
            return new CustomFormBuilder($formClass, $this->elementBuilder(Form::class));
89 336
        });
90 336
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95 6
    public function filter($filter): FilterInterface
96
    {
97 6
        if ($filter instanceof FilterInterface) {
98 1
            return $filter;
99
        }
100
101 5
        if (is_callable($filter)) {
102 3
            return new ClosureFilter($filter);
103
        }
104
105
        // @todo container ?
106
        /** @var class-string<FilterInterface> $filter */
107 2
        return new $filter();
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113 173
    public function constraint($constraint): Constraint
114
    {
115 173
        if ($constraint instanceof Constraint) {
116 158
            return $constraint;
117
        }
118
119 17
        if (is_callable($constraint)) {
120 14
            return new Closure(['callback' => $constraint]);
121
        }
122
123 3
        if (is_array($constraint)) {
124 2
            $options = $constraint[1];
125 2
            $constraint = $constraint[0];
126
127 2
            if (is_string($options)) {
128 1
                $options = ['message' => $options];
129
            }
130
131
            /** @var class-string<Constraint> $constraint */
132 2
            return new $constraint($options);
133
        }
134
135
        /** @var class-string<Constraint> $constraint */
136 1
        return new $constraint();
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142 39
    public function transformer($transformer): TransformerInterface
143
    {
144 39
        if ($transformer instanceof TransformerInterface) {
145 8
            return $transformer;
146
        }
147
148 31
        if ($transformer instanceof DataTransformerInterface) {
149 1
            return new DataTransformerAdapter($transformer);
150
        }
151
152 30
        if (is_callable($transformer)) {
153 30
            return new ClosureTransformer($transformer);
154
        }
155
156
        throw new LogicException('Invalid view transformer given for input '.var_export($transformer, true));
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162 105
    public function childBuilder(string $element, string $name): ChildBuilderInterface
163
    {
164 105
        $elementBuilder = $this->elementBuilder($element);
165
166 105
        $builderFactory = $this->childBuilderFactories[$element] ?? ChildBuilder::class;
167
168 105
        if (is_string($builderFactory)) {
169
            /** @var class-string<ChildBuilderInterface> $builderFactory */
170 104
            return new $builderFactory($name, $elementBuilder, $this);
171
        }
172
173 1
        return $builderFactory($name, $elementBuilder, $this);
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 139
    public function elementBuilder(string $element): ElementBuilderInterface
180
    {
181 139
        $builderFactory = null;
182
183 139
        if (isset($this->elementBuilderFactories[$element])) {
184 139
            $builderFactory = $this->elementBuilderFactories[$element];
185
        } else {
186 5
            foreach ($this->elementBuilderFactories as $builderElement => $factory) {
187 5
                if (is_subclass_of($element, $builderElement, true)) {
188 5
                    $builderFactory = $factory;
189 5
                    break;
190
                }
191
            }
192
        }
193
194 139
        if (!$builderFactory) {
195
            throw new InvalidArgumentException('The element '.$element.' is not registered');
196
        }
197
198 139
        if (is_string($builderFactory)) {
199
            /** @var class-string<ElementBuilderInterface> $builderFactory */
200 139
            return new $builderFactory($this, $element);
201
        }
202
203 5
        return ($builderFactory)($this, $element);
204
    }
205
206
    /**
207
     * {@inheritdoc}
208
     */
209 4
    public function buttonBuilder(string $name): ButtonBuilderInterface
210
    {
211 4
        return new SubmitButtonBuilder($name);
212
    }
213
214
    /**
215
     * Register a new element builder
216
     *
217
     * <code>
218
     * // Register MyCustomBuilder as builder for MyCustomElement
219
     * $registry->register(MyCustomElement::class, MyCustomBuilder::class);
220
     *
221
     * // Register a factory builder. The factory takes as parameters the registry, and the element class name
222
     * $registry->register(MyCustomElement::class, function (Registry $registry, string $element) {
223
     *     return new MyCustomBuilder($registry);
224
     * });
225
     *
226
     * // Register with a custom child builder
227
     * $registry->register(MyCustomElement::class, MyCustomBuilder::class, function (string $name, ElementBuilderInterface $builder, Registry $registry) {
228
     *     return new MyCustomChildBuilder($registry, new ChildBuilder($name, $builder, $registry));
229
     * });
230
     * </code>
231
     *
232
     * @param string $elementType The element class name
233
     * @param class-string<ElementBuilderInterface>|callable $builderFactory The builder factory, or builder class name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<ElementBuilderInterface>|callable at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<ElementBuilderInterface>|callable.
Loading history...
234
     * @param class-string<ChildBuilderInterface>|callable|null $childBuilderFactory The builder factory for child, or builder class name. If null, use default child builder
235
     *
236
     * @see Registry::elementBuilder()
237
     */
238 336
    public function register(string $elementType, $builderFactory, $childBuilderFactory = null): void
239
    {
240 336
        $this->elementBuilderFactories[$elementType] = $builderFactory;
241
242 336
        if ($childBuilderFactory !== null) {
243 1
            $this->childBuilderFactories[$elementType] = $childBuilderFactory;
244
        }
245 336
    }
246
}
247