Passed
Push — master ( 779d84...3d5507 )
by Vincent
05:31
created

Registry::transformer()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.0312

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 15
ccs 7
cts 8
cp 0.875
rs 10
cc 4
nc 4
nop 1
crap 4.0312
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\Helper\EmailElement;
27
use Bdf\Form\Leaf\Helper\EmailElementBuilder;
28
use Bdf\Form\Leaf\Helper\UrlElement;
29
use Bdf\Form\Leaf\Helper\UrlElementBuilder;
30
use Bdf\Form\Leaf\IntegerElement;
31
use Bdf\Form\Leaf\IntegerElementBuilder;
32
use Bdf\Form\Leaf\StringElement;
33
use Bdf\Form\Leaf\StringElementBuilder;
34
use Bdf\Form\Phone\FormattedPhoneElement;
35
use Bdf\Form\Phone\FormattedPhoneElementBuilder;
36
use Bdf\Form\Phone\PhoneElement;
37
use Bdf\Form\Phone\PhoneElementBuilder;
38
use Bdf\Form\Transformer\ClosureTransformer;
39
use Bdf\Form\Transformer\DataTransformerAdapter;
40
use Bdf\Form\Transformer\TransformerInterface;
41
use Bdf\Form\Constraint\Closure;
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 string[]|callable[]
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
    /**
75
     * Registry constructor.
76
     */
77 338
    public function __construct()
78
    {
79
        $this->register(CustomForm::class, function (RegistryInterface $registry, string $formClass) {
80
            /** @psalm-suppress ArgumentTypeCoercion */
81 5
            return new CustomFormBuilder($formClass, $this->elementBuilder(Form::class));
82 338
        });
83
84
        $this->register(FormattedPhoneElement::class, function (RegistryInterface $registry) {
85
            /** @psalm-suppress ArgumentTypeCoercion */
86 2
            return new FormattedPhoneElementBuilder($registry->elementBuilder(PhoneElement::class));
87 338
        });
88 338
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 6
    public function filter($filter): FilterInterface
94
    {
95 6
        if ($filter instanceof FilterInterface) {
96 1
            return $filter;
97
        }
98
99 5
        if (is_callable($filter)) {
100 3
            return new ClosureFilter($filter);
101
        }
102
103
        // @todo container ?
104
        /** @var class-string<FilterInterface> $filter */
105 2
        return new $filter();
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111 176
    public function constraint($constraint): Constraint
112
    {
113 176
        if ($constraint instanceof Constraint) {
114 161
            return $constraint;
115
        }
116
117 17
        if (is_callable($constraint)) {
118 14
            return new Closure(['callback' => $constraint]);
119
        }
120
121 3
        if (is_array($constraint)) {
122 2
            $options = $constraint[1];
123 2
            $constraint = $constraint[0];
124
125 2
            if (is_string($options)) {
126 1
                $options = ['message' => $options];
127
            }
128
129
            /** @var class-string<Constraint> $constraint */
130 2
            return new $constraint($options);
131
        }
132
133
        /** @var class-string<Constraint> $constraint */
134 1
        return new $constraint();
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140 33
    public function transformer($transformer): TransformerInterface
141
    {
142 33
        if ($transformer instanceof TransformerInterface) {
143 2
            return $transformer;
144
        }
145
146 31
        if ($transformer instanceof DataTransformerInterface) {
147 1
            return new DataTransformerAdapter($transformer);
148
        }
149
150 30
        if (is_callable($transformer)) {
151 30
            return new ClosureTransformer($transformer);
152
        }
153
154
        throw new LogicException('Invalid view transformer given for input '.var_export($transformer, true));
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160 101
    public function childBuilder(string $element, string $name): ChildBuilderInterface
161
    {
162 101
        return new ChildBuilder($name, $this->elementBuilder($element), $this);
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168 136
    public function elementBuilder(string $element): ElementBuilderInterface
169
    {
170 136
        $builderFactory = null;
171
172 136
        if (isset($this->elementBuilderFactories[$element])) {
173 136
            $builderFactory = $this->elementBuilderFactories[$element];
174
        } else {
175 5
            foreach ($this->elementBuilderFactories as $builderElement => $factory) {
176 5
                if (is_subclass_of($element, $builderElement, true)) {
177 5
                    $builderFactory = $factory;
178 5
                    break;
179
                }
180
            }
181
        }
182
183 136
        if (!$builderFactory) {
184
            throw new InvalidArgumentException('The element '.$element.' is not registered');
185
        }
186
187 136
        if (is_string($builderFactory)) {
188
            /** @var class-string<ElementBuilderInterface> $builderFactory */
189 136
            return new $builderFactory($this, $element);
190
        }
191
192 6
        return ($builderFactory)($this, $element);
193
    }
194
195
    /**
196
     * {@inheritdoc}
197
     */
198 4
    public function buttonBuilder(string $name): ButtonBuilderInterface
199
    {
200 4
        return new SubmitButtonBuilder($name);
201
    }
202
203
    /**
204
     * Register a new element builder
205
     *
206
     * <code>
207
     * // Register MyCustomBuilder as builder for MyCustomElement
208
     * $registry->register(MyCustomElement::class, MyCustomBuilder::class);
209
     *
210
     * // Register a factory builder. The factory takes as parameters the registry, and the element class name
211
     * $registry->register(MyCustomElement::class, function (Registry $registry, string $element) {
212
     *     return new MyCustomBuilder($registry);
213
     * });
214
     * </code>
215
     *
216
     * @param string $elementType The element class name
217
     * @param string|callable $builderFactory The builder factory, or builder class name
218
     *
219
     * @see Registry::elementBuilder()
220
     */
221 338
    public function register(string $elementType, $builderFactory): void
222
    {
223 338
        $this->elementBuilderFactories[$elementType] = $builderFactory;
224 338
    }
225
}
226