AbstractType::create()   B
last analyzed

Complexity

Conditions 9
Paths 16

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 8.0555
c 0
b 0
f 0
cc 9
nc 16
nop 0
1
<?php
2
3
namespace Faulancer\Form\Type;
4
5
use Faulancer\Form\Validator\AbstractValidator;
6
use Faulancer\Http\Request;
7
use Faulancer\Service\Config;
8
use Faulancer\ServiceLocator\ServiceLocator;
9
use Faulancer\Form\Validator\ValidatorChain;
10
use Faulancer\ServiceLocator\ServiceLocatorInterface;
11
use Faulancer\Session\SessionManager;
12
13
/**
14
 * Class AbstractType
15
 *
16
 * @package Faulancer\Form\Type\Base
17
 * @author Florian Knapp <[email protected]>
18
 */
19
abstract class AbstractType
20
{
21
22
    /** @var array */
23
    protected $definition = [];
24
25
    /** @var string */
26
    protected $type = '';
27
28
    /** @var string */
29
    protected $label = '';
30
31
    /** @var string */
32
    protected $value = '';
33
34
    /** @var string */
35
    protected $name = '';
36
37
    /** @var string */
38
    protected $outputPattern = '';
39
40
    /** @var string */
41
    protected $element = '';
42
43
    /** @var array */
44
    protected $errorMessages = [];
45
46
    /** @var string */
47
    protected $formIdentifier = '';
48
49
    /** @var AbstractValidator|null */
50
    protected $defaultValidator = null;
51
52
    /** @var ValidatorChain */
53
    protected $validatorChain = null;
54
55
    /** @var array */
56
    protected $formErrorDecoration = [];
57
58
    /** @var ServiceLocatorInterface */
59
    protected $serviceLocator;
60
61
    /**
62
     * AbstractType constructor.
63
     *
64
     * @param array $definition
65
     * @param array $formErrorDecoration
66
     * @param string $formIdentifier
67
     */
68
    public function __construct(array $definition, array $formErrorDecoration = [], string $formIdentifier = '')
69
    {
70
        $this->definition          = $definition;
71
        $this->formErrorDecoration = $formErrorDecoration;
72
        $this->formIdentifier      = $formIdentifier;
73
    }
74
75
    /**
76
     * Get default validator
77
     *
78
     * @return AbstractValidator|null
79
     */
80
    public function getDefaultValidator()
81
    {
82
        return $this->defaultValidator;
83
    }
84
85
    /**
86
     * Set default validator
87
     *
88
     * @param AbstractValidator $validator
89
     */
90
    public function setDefaultValidator(AbstractValidator $validator)
91
    {
92
        $this->defaultValidator = $validator;
93
    }
94
95
    /**
96
     * Set validator chain
97
     *
98
     * @param ValidatorChain $validatorChain
99
     */
100
    public function setValidatorChain(ValidatorChain $validatorChain)
101
    {
102
        $this->validatorChain = $validatorChain;
103
    }
104
105
    public function getValidatorChain():? ValidatorChain
106
    {
107
        return $this->validatorChain;
108
    }
109
110
    /**
111
     * Validate field by validator chain or default validator
112
     *
113
     * @return bool|null
114
     */
115
    public function isValid()
116
    {
117
        if (!empty($this->validatorChain)) {
118
            return $this->validatorChain->validate();
119
        } elseif (!empty($this->getDefaultValidator())) {
120
            return $this->getDefaultValidator()->validate();
121
        }
122
123
        return null;
124
    }
125
126
    /**
127
     * Remove bound validators
128
     *
129
     * @return void
130
     */
131
    public function removeValidators()
132
    {
133
        $this->setValidatorChain(null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object<Faulancer\Form\Validator\ValidatorChain>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
134
        $this->setDefaultValidator(null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object<Faulancer\Form\Va...ator\AbstractValidator>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
135
    }
136
137
    /**
138
     * Get error messages
139
     *
140
     * @return array|string
141
     */
142
    public function getErrorMessages()
143
    {
144
        if (empty($this->formErrorDecoration)) {
145
            return $this->errorMessages;
146
        }
147
148
        $def = $this->formErrorDecoration;
149
150
        if (empty($this->errorMessages)) {
151
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Faulancer\Form\Type\AbstractType::getErrorMessages of type array|string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
152
        }
153
154
        if (strpos($def['containerPrefix'], '{name}') !== false) {
155
156
            $name = $this->definition['attributes']['name'];
157
            $def['containerPrefix'] = str_replace('{name}', $name, $def['containerPrefix']);
158
159
        }
160
161
        return $def['containerPrefix']
162
            . $def['containerItemPrefix']
163
            . implode(
164
                $def['containerItemSuffix'] . $def['containerItemPrefix'],
165
                $this->errorMessages
166
            )
167
            . $def['containerItemSuffix']
168
            . $def['containerSuffix'];
169
170
    }
171
172
    /**
173
     * Set error messages
174
     *
175
     * @param array $messages
176
     */
177
    public function setErrorMessages(array $messages)
178
    {
179
        $this->errorMessages = $messages;
180
    }
181
182
    /**
183
     * Get field type
184
     *
185
     * @return string
186
     */
187
    public function getType() :string
188
    {
189
        return $this->type;
190
    }
191
192
    /**
193
     * @param string $type
194
     *
195
     * @return self
196
     */
197
    public function setType(string $type)
198
    {
199
        $this->type = $type;
200
        return $this;
201
    }
202
203
    /**
204
     * @return string
205
     */
206
    public function getLabel() :string
207
    {
208
        return $this->label;
209
    }
210
211
    /**
212
     * @param string $label
213
     *
214
     * @return self
215
     */
216
    public function setLabel(string $label)
217
    {
218
        $this->label = $label;
219
        return $this;
220
    }
221
222
    /**
223
     * @return string
224
     */
225
    public function getValue()
226
    {
227
        return $this->value;
228
    }
229
230
    /**
231
     * @param mixed $value
232
     *
233
     * @return self
234
     */
235
    public function setValue($value)
236
    {
237
        $this->value = $value;
238
        return $this;
239
    }
240
241
    /**
242
     * @return string
243
     */
244
    public function getName() :string
245
    {
246
        return $this->name;
247
    }
248
249
    /**
250
     * @param string $name
251
     *
252
     * @return self
253
     */
254
    public function setName(string $name)
255
    {
256
        $this->name = $name;
257
        return $this;
258
    }
259
260
    /**
261
     * Add field attribute
262
     *
263
     * @param string $key
264
     * @param string $value
265
     *
266
     * @return self
267
     */
268
    public function addAttribute(string $key, string $value)
269
    {
270
        if (!empty($this->definition['attributes'][$key])) {
271
            $this->definition['attributes'][$key] = $this->definition['attributes'][$key] . ' ' . $value;
272
        } else {
273
            $this->definition['attributes'][$key] = $value;
274
        }
275
276
        return $this;
277
    }
278
279
    /**
280
     * @return bool
281
     */
282
    protected function isPost() :bool
283
    {
284
        return ServiceLocator::instance()->get(Request::class)->isPost();
285
    }
286
287
    /**
288
     * @return self
289
     */
290
    public function create()
291
    {
292
        if (empty($this->getLabel()) && !empty($this->definition['label'])) {
293
            $this->setLabel($this->definition['label']);
294
        }
295
296
        if (empty($this->getType()) && !empty($this->definition['type'])) {
297
             $this->setType($this->definition['type']);
298
        }
299
300
        if (empty($this->getValue()) && !empty($this->definition['value'])) {
301
            $this->setValue($this->definition['value']);
302
        }
303
304
        if (empty($this->getName()) && !empty($this->definition['name'])) {
305
            $this->setName($this->definition['name']);
306
        }
307
308
        $this->_translateLabelsAndPlaceholders();
309
310
        return $this;
311
    }
312
313
    /**
314
     * @return string
315
     */
316
    public function __toString() :string
317
    {
318
        $this->create();
319
        return str_replace(['  ', ' >'], [' ', '>'], $this->element);
320
    }
321
322
    /**
323
     * @return bool
324
     */
325
    private function _translateLabelsAndPlaceholders() :bool
326
    {
327
        if (!empty($this->definition['attributes']['placeholder'])) {
328
            $this->_translateType('placeholder');
329
        } else if (!empty($this->definition['label'])) {
330
            $this->_translateType('label');
331
        } else {
332
            return false;
333
        }
334
335
        return true;
336
    }
337
338
    /**
339
     * @param string $type
340
     *
341
     * @return bool
342
     */
343
    private function _translateType(string $type) :bool
344
    {
345
        /** @var Config $config */
346
        $config = $this->_getServiceLocator()->get(Config::class);
347
348
        /** @var SessionManager $sessionManager */
349
        $sessionManager = $this->_getServiceLocator()->get(SessionManager::class);
350
        $lang           = $sessionManager->get('language');
351
        $trans          = $config->get('translation:' . $lang);
352
353
        if (!empty($trans['form_' . $this->getName()])) {
354
355
            $transText = $trans['form_' . $this->getName()];
356
357
            switch ($type) {
358
359
                case 'label':
360
                    $this->setLabel($transText);
361
                    break;
362
363
                case 'placeholder':
364
                    $this->definition['attributes']['placeholder'] = $transText;
365
                    break;
366
367
            }
368
369
        }
370
371
        return true;
372
    }
373
374
    public function getFormIdentifier()
375
    {
376
        return $this->formIdentifier;
377
    }
378
379
    /**
380
     * @return ServiceLocatorInterface
381
     */
382
    private function _getServiceLocator()
383
    {
384
        return ServiceLocator::instance();
385
    }
386
387
}