Passed
Branch master (3daac1)
by Vincent
07:53
created

Registry::elementBuilder()   A

Complexity

Conditions 6
Paths 12

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6.0163

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 25
ccs 12
cts 13
cp 0.9231
rs 9.2222
cc 6
nc 12
nop 1
crap 6.0163
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\DateTimeElement;
23
use Bdf\Form\Leaf\Date\DateTimeElementBuilder;
24
use Bdf\Form\Leaf\FloatElement;
25
use Bdf\Form\Leaf\FloatElementBuilder;
26
use Bdf\Form\Leaf\IntegerElement;
27
use Bdf\Form\Leaf\IntegerElementBuilder;
28
use Bdf\Form\Leaf\StringElement;
29
use Bdf\Form\Leaf\StringElementBuilder;
30
use Bdf\Form\Phone\PhoneElement;
31
use Bdf\Form\Phone\PhoneElementBuilder;
32
use Bdf\Form\Transformer\ClosureTransformer;
33
use Bdf\Form\Transformer\DataTransformerAdapter;
34
use Bdf\Form\Transformer\TransformerInterface;
35
use Bdf\Form\Constraint\Closure;
36
use InvalidArgumentException;
37
use LogicException;
38
use Symfony\Component\Form\DataTransformerInterface;
39
use Symfony\Component\Validator\Constraint;
40
41
/**
42
 * Base registry interface
43
 */
44
final class Registry implements RegistryInterface
45
{
46
    /**
47
     * @var string[]|callable[]
48
     */
49
    private $elementBuilderFactories = [
50
        StringElement::class => StringElementBuilder::class,
51
        IntegerElement::class => IntegerElementBuilder::class,
52
        FloatElement::class => FloatElementBuilder::class,
53
        BooleanElement::class => BooleanElementBuilder::class,
54
55
        CsrfElement::class => CsrfElementBuilder::class,
56
        PhoneElement::class => PhoneElementBuilder::class,
57
58
        DateTimeElement::class => DateTimeElementBuilder::class,
59
60
        ArrayElement::class => ArrayElementBuilder::class,
61
        Form::class => FormBuilder::class,
62
    ];
63
64
65
    /**
66
     * Registry constructor.
67
     */
68 297
    public function __construct()
69
    {
70
        $this->register(CustomForm::class, function (RegistryInterface $registry, string $formClass) {
71
            /** @psalm-suppress ArgumentTypeCoercion */
72 5
            return new CustomFormBuilder($formClass, $this->elementBuilder(Form::class));
73 297
        });
74 297
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79 6
    public function filter($filter): FilterInterface
80
    {
81 6
        if ($filter instanceof FilterInterface) {
82 1
            return $filter;
83
        }
84
85 5
        if (is_callable($filter)) {
86 3
            return new ClosureFilter($filter);
87
        }
88
89
        // @todo container ?
90
        /** @var class-string<FilterInterface> $filter */
91 2
        return new $filter();
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97 164
    public function constraint($constraint): Constraint
98
    {
99 164
        if ($constraint instanceof Constraint) {
100 153
            return $constraint;
101
        }
102
103 13
        if (is_callable($constraint)) {
104 10
            return new Closure(['callback' => $constraint]);
105
        }
106
107 3
        if (is_array($constraint)) {
108 2
            $options = $constraint[1];
109 2
            $constraint = $constraint[0];
110
111 2
            if (is_string($options)) {
112 1
                $options = ['message' => $options];
113
            }
114
115
            /** @var class-string<Constraint> $constraint */
116 2
            return new $constraint($options);
117
        }
118
119
        /** @var class-string<Constraint> $constraint */
120 1
        return new $constraint();
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 32
    public function transformer($transformer): TransformerInterface
127
    {
128 32
        if ($transformer instanceof TransformerInterface) {
129 2
            return $transformer;
130
        }
131
132 30
        if ($transformer instanceof DataTransformerInterface) {
133 1
            return new DataTransformerAdapter($transformer);
134
        }
135
136 29
        if (is_callable($transformer)) {
137 29
            return new ClosureTransformer($transformer);
138
        }
139
140
        throw new LogicException('Invalid view transformer given for input '.var_export($transformer, true));
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146 92
    public function childBuilder(string $element, string $name): ChildBuilderInterface
147
    {
148 92
        return new ChildBuilder($name, $this->elementBuilder($element), $this);
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154 127
    public function elementBuilder(string $element): ElementBuilderInterface
155
    {
156 127
        $builderFactory = null;
157
158 127
        if (isset($this->elementBuilderFactories[$element])) {
159 127
            $builderFactory = $this->elementBuilderFactories[$element];
160
        } else {
161 5
            foreach ($this->elementBuilderFactories as $builderElement => $factory) {
162 5
                if (is_subclass_of($element, $builderElement, true)) {
163 5
                    $builderFactory = $factory;
164 5
                    break;
165
                }
166
            }
167
        }
168
169 127
        if (!$builderFactory) {
170
            throw new InvalidArgumentException('The element '.$element.' is not registered');
171
        }
172
173 127
        if (is_string($builderFactory)) {
174
            /** @var class-string<ElementBuilderInterface> $builderFactory */
175 127
            return new $builderFactory($this, $element);
176
        }
177
178 5
        return ($builderFactory)($this, $element);
179
    }
180
181
    /**
182
     * {@inheritdoc}
183
     */
184 4
    public function buttonBuilder(string $name): ButtonBuilderInterface
185
    {
186 4
        return new SubmitButtonBuilder($name);
187
    }
188
189
    /**
190
     * Register a new element builder
191
     *
192
     * <code>
193
     * // Register MyCustomBuilder as builder for MyCustomElement
194
     * $registry->register(MyCustomElement::class, MyCustomBuilder::class);
195
     *
196
     * // Register a factory builder. The factory takes as parameters the registry, and the element class name
197
     * $registry->register(MyCustomElement::class, function (Registry $registry, string $element) {
198
     *     return new MyCustomBuilder($registry);
199
     * });
200
     * </code>
201
     *
202
     * @param string $elementType The element class name
203
     * @param string|callable $builderFactory The builder factory, or builder class name
204
     *
205
     * @see Registry::elementBuilder()
206
     */
207 297
    public function register(string $elementType, $builderFactory): void
208
    {
209 297
        $this->elementBuilderFactories[$elementType] = $builderFactory;
210 297
    }
211
}
212