Passed
Push — master ( 5bdfe8...9d312b )
by Vincent
04:50
created

Registry   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 198
Duplicated Lines 0 %

Test Coverage

Coverage 96.49%

Importance

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