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