SubjectFilter::applyToArray()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 *
4
 * This file is part of Aura for PHP.
5
 *
6
 * @license http://opensource.org/licenses/bsd-license.php BSD
7
 *
8
 */
9
namespace Aura\Filter;
10
11
use Aura\Filter\Exception;
12
use Aura\Filter\Failure\FailureCollection;
13
use Aura\Filter\Spec\SanitizeSpec;
14
use Aura\Filter\Spec\ValidateSpec;
15
use InvalidArgumentException;
16
17
/**
18
 *
19
 * A filter for an entire "subject" (i.e., an array or object).
20
 *
21
 * @package Aura.Filter
22
 *
23
 */
24
class SubjectFilter
25
{
26
    /**
27
     *
28
     * An array of specifications for the filter subject.
29
     *
30
     * @var array
31
     *
32
     */
33
    protected $specs = array();
34
35
    /**
36
     *
37
     * Skip these fields on the filter subject.
38
     *
39
     * @var array
40
     *
41
     */
42
    protected $skip = array();
43
44
    /**
45
     *
46
     * A collection of failure objects.
47
     *
48
     * @var FailureCollection
49
     *
50
     */
51
    protected $failures;
52
53
    /**
54
     *
55
     * Use these field-specific messages when a subject field fails.
56
     *
57
     * @var array
58
     *
59
     */
60
    protected $field_messages = array();
61
62
    /**
63
     *
64
     * A prototype ValidateSpect.
65
     *
66
     * @var ValidateSpec
67
     *
68
     */
69
    protected $validate_spec;
70
71
    /**
72
     *
73
     * A prototype SanitizeSpect.
74
     *
75
     * @var SanitizeSpec
76
     *
77
     */
78
    protected $sanitize_spec;
79
80
    /**
81
     *
82
     * A prototype FailureCollection.
83
     *
84
     * @var FailureCollection
85
     *
86
     */
87
    protected $proto_failures;
88
89
    /**
90
     *
91
     * Constructor.
92
     *
93
     * @param ValidateSpec $validate_spec A prototype ValidateSpec.
94
     *
95
     * @param ValidateSpec $sanitize_spec A prototype SanitizeSpec.
96
     *
97
     * @param FailureCollection $failures A prototype FailureCollection.
98
     *
99
     * @return self
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
100
     *
101
     */
102 11
    public function __construct(
103
        ValidateSpec $validate_spec,
104
        SanitizeSpec $sanitize_spec,
105
        FailureCollection $failures
106
    ) {
107 11
        $this->validate_spec = $validate_spec;
108 11
        $this->sanitize_spec = $sanitize_spec;
109 11
        $this->proto_failures = $failures;
110 11
        $this->init();
111 11
    }
112
113
    /**
114
     *
115
     * Initialization logic for this filter.
116
     *
117
     * @return null
118
     *
119
     */
120 11
    protected function init()
121
    {
122
        // do nothing
123 11
    }
124
125
    /**
126
     *
127
     * Asserts that the subject passes the filter.
128
     *
129
     * @param array|object $subject The subject to be filtered.
130
     *
131
     * @return null
132
     *
133
     * @throws Exception\FilterFailed when the assertion fails.
134
     *
135
     */
136 2
    public function __invoke(&$subject)
137
    {
138 2
        return $this->assert($subject);
139
    }
140
141
    /**
142
     *
143
     * Asserts that the subject passes the filter.
144
     *
145
     * @param array|object $subject The subject to be filtered.
146
     *
147
     * @return null
148
     *
149
     * @throws Exception\FilterFailed when the assertion fails.
150
     *
151
     */
152 2
    public function assert(&$subject)
153
    {
154 2
        if ($this->apply($subject)) {
155 2
            return;
156
        }
157
158 1
        $class = get_class($this);
159 1
        $message = PHP_EOL
160 1
                 . "  Filter: {$class}" . PHP_EOL
161 1
                 . "  Fields:" . PHP_EOL
162 1
                 . $this->failures->getMessagesAsString('    ');
163
164 1
        $e = new Exception\FilterFailed($message);
165 1
        $e->setFilterClass($class);
166 1
        $e->setFailures($this->failures);
167 1
        $e->setSubject($subject);
168 1
        throw $e;
169
    }
170
171
    /**
172
     *
173
     * Adds a "validate" specification for a subject field.
174
     *
175
     * @param string $field The subject field name.
176
     *
177
     * @return ValidateSpec
178
     *
179
     */
180 7
    public function validate($field)
181
    {
182 7
        return $this->addSpec(clone $this->validate_spec, $field);
0 ignored issues
show
Documentation introduced by
clone $this->validate_spec is of type object<Aura\Filter\Spec\ValidateSpec>, but the function expects a object<Aura\Filter\Spec>.

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...
183
    }
184
185
    /**
186
     *
187
     * Adds a "sanitize" specification for a subject field.
188
     *
189
     * @param string $field The subject field name.
190
     *
191
     * @return SanitizeSpec
192
     *
193
     */
194 3
    public function sanitize($field)
195
    {
196 3
        return $this->addSpec(clone $this->sanitize_spec, $field);
0 ignored issues
show
Documentation introduced by
clone $this->sanitize_spec is of type object<Aura\Filter\Spec\SanitizeSpec>, but the function expects a object<Aura\Filter\Spec>.

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...
197
    }
198
199
    /**
200
     *
201
     * Adds a specification for a subject field.
202
     *
203
     * @param Spec $spec The specification object.
204
     *
205
     * @param string $field The subject field name.
206
     *
207
     * @return Spec
208
     *
209
     */
210 9
    protected function addSpec($spec, $field)
211
    {
212 9
        $this->specs[] = $spec;
213 9
        $spec->field($field);
214 9
        return $spec;
215
    }
216
217
    /**
218
     *
219
     * Specifies a custom message to use when a subject field fails.
220
     *
221
     * @param string $field The subject field name.
222
     *
223
     * @param string $message The failure message to use.
224
     *
225
     * @return null
226
     *
227
     */
228 1
    public function useFieldMessage($field, $message)
229
    {
230 1
        $this->field_messages[$field] = $message;
231 1
    }
232
233
    /**
234
     *
235
     * Applies the filter to a subject.
236
     *
237
     * @param array|object $subject The subject to be filtered.
238
     *
239
     * @return bool True on success, false on failure.
240
     *
241
     */
242 10
    public function apply(&$subject)
243
    {
244 10
        if (is_array($subject)) {
245 3
            return $this->applyToArray($subject);
246
        }
247
248 7
        if (! is_object($subject)) {
249 1
            $type = gettype($subject);
250 1
            $message = "Apply the filter to an array or object, not a {$type}.";
251 1
            throw new InvalidArgumentException($message);
252
        }
253
254 6
        return $this->applyToObject($subject);
255
    }
256
257
    /**
258
     *
259
     * Applies the rule specifications to an array.
260
     *
261
     * @param array $array The filter subject.
262
     *
263
     * @return bool True if all rules passed, false if not.
264
     *
265
     */
266 3
    protected function applyToArray(&$array)
267
    {
268 3
        $object = (object) $array;
269 3
        $result = $this->applyToObject($object);
270 3
        $array = (array) $object;
271 3
        return $result;
272
    }
273
274
    /**
275
     *
276
     * Applies the rule specifications to an object.
277
     *
278
     * @param object $object The filter subject.
279
     *
280
     * @return bool True if all rules passed, false if not.
281
     *
282
     */
283 9
    protected function applyToObject($object)
284
    {
285 9
        $this->skip = array();
286 9
        $this->failures = clone $this->proto_failures;
287 9
        foreach ($this->specs as $spec) {
288 9
            $continue = $this->applySpec($spec, $object);
289 9
            if (! $continue) {
290 9
                break;
291
            }
292
        }
293 9
        return $this->failures->isEmpty();
294
    }
295
296
    /**
297
     *
298
     * Apply a rule specification to the subject.
299
     *
300
     * @param Spec $spec The rule specification.
301
     *
302
     * @param object $subject The filter subject.
303
     *
304
     * @return bool True to continue, false to stop.
305
     *
306
     */
307 9
    protected function applySpec($spec, $subject)
308
    {
309 9
        if (isset($this->skip[$spec->getField()])) {
310 2
            return true;
311
        }
312
313 9
        if (call_user_func($spec, $subject)) {
314 4
            return true;
315
        }
316
317 7
        $this->failed($spec);
318 7
        if ($spec->isStopRule()) {
319 1
            return false;
320
        }
321
322 7
        return true;
323
    }
324
325
    /**
326
     *
327
     * Adds a failure.
328
     *
329
     * @param Spec $spec The failed rule specification.
330
     *
331
     * @return Failure
332
     *
333
     */
334 7
    protected function failed($spec)
335
    {
336 7
        $field = $spec->getField();
337
338 7
        if ($spec->isHardRule()) {
339 5
            $this->skip[$field] = true;
340
        }
341
342 7
        if (isset($this->field_messages[$field])) {
343 1
            return $this->failures->set($field, $this->field_messages[$field]);
344
        }
345
346 7
        return $this->failures->add($field, $spec->getMessage(), $spec->getArgs());
347
    }
348
349
    /**
350
     *
351
     * Returns the failures.
352
     *
353
     * @return FailureCollection
354
     *
355
     */
356 6
    public function getFailures()
357
    {
358 6
        return $this->failures;
359
    }
360
}
361