InputFilter   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 377
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 97.66%

Importance

Changes 7
Bugs 1 Features 1
Metric Value
wmc 49
c 7
b 1
f 1
lcom 1
cbo 9
dl 0
loc 377
ccs 125
cts 128
cp 0.9766
rs 8.5454

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 21 4
A init() 0 9 2
A isInitialized() 0 4 1
A getElementFactory() 0 4 1
A setElementFactory() 0 4 1
A getUploadPrefix() 0 4 1
D prepare() 0 42 10
B prepareFiltration() 0 14 6
A isPrepared() 0 4 1
A getValidator() 0 4 1
A getFiltrator() 0 4 1
A getUploadHandlers() 0 8 2
A setUploadHandler() 0 6 1
A populate() 0 10 2
B processUploads() 0 23 5
A isValid() 0 16 3
A getErrors() 0 4 1
A getValues() 0 4 2
A getValue() 0 4 2
A getRawValue() 0 4 1
A getRawValues() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like InputFilter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use InputFilter, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Sirius\Input;
3
4
use Sirius\Filtration\Filtrator;
5
use Sirius\Filtration\FiltratorInterface;
6
use Sirius\Input\Element\Factory as ElementFactory;
7
use Sirius\Input\Traits\HasChildrenTrait;
8
use Sirius\Input\Traits\HasAttributesTrait;
9
use Sirius\Input\Traits\HasFiltersTrait;
10
use Sirius\Input\Util\Arr;
11
use Sirius\Upload\Handler as UploadHandler;
12
use Sirius\Upload\HandlerAggregate as UploadHandlerAggregate;
13
use Sirius\Validation\Validator;
14
use Sirius\Validation\ValidatorInterface;
15
16
class InputFilter extends \ArrayObject
17
{
18
    use HasChildrenTrait;
19
    use HasAttributesTrait;
20
    use HasFiltersTrait;
21
22
    /**
23
     * Uploaded files will have their names prefixed with this value
24
     * A form element of type "file" with the name "picture" will upload
25
     * the file into $_FILES['__upload_picture'].
26
     *
27
     * This is done to prevent collisions with hidden fields that
28
     * might hold values from AJAX uploads.
29
     */
30
    protected $uploadPrefix = '__upload_';
31
32
    /**
33
     * @var boolean
34
     */
35
    protected $isInitialized = false;
36
37
    /**
38
     * @var boolean
39
     */
40
    protected $isPrepared = false;
41
42
    /**
43
     * Data validation object
44
     *
45
     * @var \Sirius\Validation\Validator
46
     */
47
    protected $validator;
48
49
    /**
50
     * Data filtrator object
51
     *
52
     * @var \Sirius\Filtration\Filtrator
53
     */
54
    protected $filtrator;
55
56
    /**
57
     * The upload handlers that are registered withing this form
58
     *
59
     * @var HandlerAggregate
60
     */
61
    protected $uploadHandlers;
62
63
    protected $rawValues = array();
64
65
    protected $values = array();
66
67 33
    public function __construct(
68
        ElementFactory $elementFactory = null,
69
        ValidatorInterface $validator = null,
70
        FiltratorInterface $filtrator = null
71
    ) {
72 33
        if (!$elementFactory) {
73 33
            $elementFactory = new ElementFactory();
74 33
        }
75 33
        $this->elementFactory = $elementFactory;
76
77 33
        if (!$validator) {
78 17
            $validator = new Validator();
79 17
        }
80 33
        $this->validator = $validator;
81
82 33
        if (!$filtrator) {
83 10
            $filtrator = new Filtrator();
84 10
        }
85 33
        $this->filtrator = $filtrator;
86 33
        $this->setAttribute('method', 'post');
87 33
    }
88
89
    /**
90
     * Initialize the form
91
     * This is the place to put your definition (properties, elements)
92
     *
93
     * @return InputFilter
94
     */
95 11
    public function init()
96
    {
97 11
        if ($this->isInitialized) {
98 1
            return $this;
99
        }
100 11
        $this->isInitialized = true;
101
102 11
        return $this;
103
    }
104
105
    /**
106
     * Returns whether the form was initialized or not
107
     *
108
     * @return boolean
109
     */
110 11
    public function isInitialized()
111
    {
112 11
        return $this->isInitialized;
113
    }
114
115
    /**
116
     * Return the element factory
117
     *
118
     * @return ElementFactory
119
     */
120 5
    public function getElementFactory()
121
    {
122 5
        return $this->elementFactory;
123
    }
124
125 1
    public function setElementFactory()
126
    {
127 1
        throw new \BadMethodCallException('You are not allowed to change the element factory of an \InputFilter object');
128
    }
129
130
    /**
131
     * Get the prefix for elements that are of type upload
132
     *
133
     * @return string
134
     */
135 3
    public function getUploadPrefix()
136
    {
137 3
        return $this->uploadPrefix;
138
    }
139
140
    /**
141
     * Prepare the validator, filtrator and upload handlers objects
142
     *
143
     * @param bool $force force the execution of the preparation code even if already prepared
144
     *
145
     * @throws \LogicException
146
     * @return InputFilter
147
     */
148 11
    public function prepare($force = false)
149
    {
150 11
        if ($this->isPrepared && !$force) {
151 2
            return $this;
152
        }
153 11
        $this->init();
154 11
        if (!$this->isInitialized()) {
155
            throw new \LogicException('Input was not properly initialized');
156
        }
157
158
        // remove validation rules
159 11
        $validator       = $this->getValidator();
160 11
        $validationRules = $validator->getRules();
161 11
        if (is_array($validationRules)) {
162 8
            foreach (array_keys($validationRules) as $selector) {
163 1
                $validator->remove($selector);
164 8
            }
165 8
        }
166
167
        // remove filtration rules
168 11
        $filtrator = $this->getFiltrator();
169 11
        $filters   = $filtrator->getFilters();
170 11
        if (is_array($filters)) {
171 6
            foreach (array_keys($filters) as $selector) {
172
                $filtrator->remove($selector);
173 6
            }
174 6
        }
175
176
        // reset upload handler
177 11
        $this->uploadHandlers = null;
178
179 11
        $this->cleanUpMissingGroups();
180 11
        foreach ($this->getElements() as $element) {
181 9
            if (method_exists($element, 'prepareInputFilter')) {
182 9
                $element->prepareInputFilter($this);
183 9
            }
184 11
        }
185 11
        $this->prepareFiltration();
186 11
        $this->isPrepared = true;
187
188 11
        return $this;
189
    }
190
191
    /**
192
     * Populates the filtrator object with the filtering rules
193
     * associated with the InputFilter instance (not individual elements)
194
     */
195 11
    protected function prepareFiltration()
196
    {
197 11
        $filters = $this->getFilters();
198 11
        if (!is_array($filters) || empty($filters)) {
199 10
            return;
200
        }
201 1
        $filtrator = $this->getFiltrator();
202 1
        foreach ($filters as $filter) {
203 1
            $params = is_array($filter) ? $filter : array($filter);
204 1
            if (isset($params[0])) {
205 1
                $filtrator->add(Filtrator::SELECTOR_ROOT, $params[0], @$params[1], @$params[2], @$params[3]);
206 1
            }
207 1
        }
208 1
    }
209
210
    /**
211
     * Return whether the input is prepared or not
212
     *
213
     * @return boolean
214
     */
215 4
    public function isPrepared()
216
    {
217 4
        return $this->isPrepared;
218
    }
219
220
221
    /**
222
     * Returns the input's validator object
223
     *
224
     * @return \Sirius\Validation\Validator
225
     */
226 11
    public function getValidator()
227
    {
228 11
        return $this->validator;
229
    }
230
231
    /**
232
     * Returns the filtrator object
233
     *
234
     * @return \Sirius\Filtration\Filtrator
235
     */
236 11
    public function getFiltrator()
237
    {
238 11
        return $this->filtrator;
239
    }
240
241
    /**
242
     * Retrieves the upload handlers aggregate object
243
     *
244
     * @return UploadHandlerAggregate
245
     */
246 5
    public function getUploadHandlers()
247
    {
248 5
        if (!$this->uploadHandlers) {
249 5
            $this->uploadHandlers = new UploadHandlerAggregate;
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Sirius\Upload\HandlerAggregate() of type object<Sirius\Upload\HandlerAggregate> is incompatible with the declared type object<Sirius\Input\HandlerAggregate> of property $uploadHandlers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
250 5
        }
251
252 5
        return $this->uploadHandlers;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->uploadHandlers; of type Sirius\Upload\HandlerAgg...\Input\HandlerAggregate adds the type Sirius\Input\HandlerAggregate to the return on line 252 which is incompatible with the return type documented by Sirius\Input\InputFilter::getUploadHandlers of type Sirius\Upload\HandlerAggregate.
Loading history...
253
    }
254
255
    /**
256
     * Sets a upload handler to be executed on files with a specific selector
257
     * @example
258
     *      $form->setUploadHandler('resume', $resumeHandler);
259
     *      $form->setUploadHandler('pictures[*]', $pictureHandler);
260
     *
261
     * @param string $selector
262
     * @param UploadHandler $handler
263
     *
264
     * @return $this
265
     */
266 1
    public function setUploadHandler($selector, UploadHandler $handler)
267
    {
268 1
        $this->getUploadHandlers()->addHandler($selector, $handler);
269
270 1
        return $this;
271
    }
272
273
    /**
274
     * Populates a form with values. If you have uploads you must merge data from POST and FILES.
275
     *
276
     * @param array $values
277
     *
278
     * @throws \LogicException
279
     */
280 4
    public function populate($values = array())
281
    {
282 4
        $this->prepare();
283 4
        if (!$this->isPrepared()) {
284
            throw new \LogicException('Input was not prepared and cannot receive data');
285
        }
286
287
        // set raw values
288 4
        $this->rawValues = $values;
289 4
    }
290
291
    /**
292
     * Processes the uploaded files:
293
     * 1. Validates the files
294
     * 2. Confirms valid files
295
     * 3. Add the upload errors to the validation errors
296
     */
297 4
    protected function processUploads()
298
    {
299 4
        $result = $this->getUploadHandlers()->process($this->values);
300 4
        $errors = array();
301 4
        foreach ($result as $path => $file) {
302
            // remember!; $_FILES keys are prefixed with a value
303 2
            $key = substr($path, strlen($this->getUploadPrefix()));
304
            /* @var $file \Sirius\Upload\Result\File */
305 2
            if ($file->isValid()) {
306 1
                $file->confirm();
307 1
                $this->values = Arr::setBySelector($this->values, $key, $file->name);
308 1
            } else {
309 1
                $errors[$key] = $file->getMessages();
310
            }
311 4
        }
312
        // add upload error messages to the validator
313 4
        $validator = $this->getValidator();
314 4
        foreach ($errors as $key => $messages) {
315 1
            foreach ($messages as $message) {
316 1
                $validator->addMessage($key, $message);
317 1
            }
318 4
        }
319 4
    }
320
321
322
    /**
323
     * Returns whether the data is valid.
324
     * If filters the data, process the uploads and performs the validation
325
     *
326
     * @param bool $skipDataProcessing skip filtration and upload handling
327
     *
328
     * @return bool
329
     */
330 4
    public function isValid($skipDataProcessing = false)
331
    {
332 4
        if (!$skipDataProcessing) {
333 4
            $this->values = $this->getFiltrator()->filter($this->rawValues);
334 4
        }
335
336
        // validate form values
337 4
        $validator = $this->getValidator();
338 4
        $validator->validate($this->values);
339
340 4
        if (!$skipDataProcessing) {
341 4
            $this->processUploads();
342 4
        }
343
344 4
        return count($this->getValidator()->getMessages()) === 0;
345
    }
346
347
    /**
348
     * Get the errors from the validator object
349
     *
350
     * @return array
351
     */
352 1
    public function getErrors()
353
    {
354 1
        return $this->getValidator()->getMessages();
355
    }
356
357
    /**
358
     * @return array
359
     */
360 1
    public function getValues()
361
    {
362 1
        return empty($this->values) ? $this->rawValues : $this->values;
363
    }
364
365
    /**
366
     * @param $name
367
     *
368
     * @return mixed
369
     */
370 3
    public function getValue($name)
371
    {
372 3
        return empty($this->values) ? $this->getRawValue($name) : Arr::getByPath($this->values, $name);
373
    }
374
375
    /**
376
     * @param $name
377
     *
378
     * @return mixed
379
     */
380 1
    public function getRawValue($name)
381
    {
382 1
        return Arr::getByPath($this->rawValues, $name);
383
    }
384
385
    /**
386
     * @return array
387
     */
388 1
    public function getRawValues()
389
    {
390 1
        return $this->rawValues;
391
    }
392
}
393