Completed
Push — 2.x ( f3ac2a...cca255 )
by Paul
06:37
created

Spec::field()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
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\Spec;
10
11
use Aura\Filter\Locator\Locator;
12
use Exception;
13
14
/**
15
 *
16
 * A generic rule specification.
17
 *
18
 * @package Aura.Filter
19
 *
20
 */
21
class Spec
22
{
23
    /**
24
     * Stop filtering on a field when a rule for that field fails.
25
     */
26
    const HARD_RULE = 'HARD_RULE';
27
28
    /**
29
     * Continue filtering on a field even when a rule for that field fails.
30
     */
31
    const SOFT_RULE = 'SOFT_RULE';
32
33
    /**
34
     * Stop filtering on all fields when a rule fails.
35
     */
36
    const STOP_RULE = 'STOP_RULE';
37
38
    /**
39
     *
40
     * The field name to be filtered.
41
     *
42
     * @var string
43
     *
44
     */
45
    protected $field;
46
47
    /**
48
     *
49
     * The rule name to be applied.
50
     *
51
     * @var string
52
     *
53
     */
54
    protected $rule;
55
56
    /**
57
     *
58
     * Arguments to pass to the rule.
59
     *
60
     * @var array
61
     *
62
     */
63
    protected $args = array();
64
65
    /**
66
     *
67
     * The message to use on failure.
68
     *
69
     * @var string
70
     *
71
     */
72
    protected $message;
73
74
    /**
75
     *
76
     * Allow the field to be blank?
77
     *
78
     * @var bool
79
     *
80
     */
81
    protected $allow_blank = false;
82
83
    /**
84
     *
85
     * The failure mode to use.
86
     *
87
     * @var string
88
     *
89
     */
90
    protected $failure_mode = self::HARD_RULE;
91
92
    /**
93
     *
94
     * The rule locator to use.
95
     *
96
     * @var Locator
97
     *
98
     */
99
    protected $locator;
100
101
    /**
102
     *
103
     * Constructor.
104
     *
105
     * @param Locator $locator The "sanitize" rules.
106
     *
107
     * @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...
108
     *
109
     */
110 17
    public function __construct(Locator $locator)
111
    {
112 17
        $this->locator = $locator;
113
    }
114
115
    /**
116
     *
117
     * Applies the rule specification to a subject.
118
     *
119
     * @param mixed $subject The filter subject.
120
     *
121
     * @return bool True on success, false on failure.
122
     *
123
     */
124 1
    public function __invoke($subject)
125
    {
126
        return $this->applyBlank($subject)
127 1
            || $this->applyRule($subject);
128
    }
129
130
    /**
131
     *
132
     * Sets the subject field name.
133
     *
134
     * @param string $field The subject field name.
135
     *
136
     * @return self
137
     *
138
     */
139 15
    public function field($field)
140
    {
141 15
        $this->field = $field;
142 15
        return $this;
143
    }
144
145
    /**
146
     *
147
     * Sets this specification as a "soft" rule.
148
     *
149
     * @param string $message The failure message.
150
     *
151
     * @return self
152
     *
153
     */
154 3
    public function asSoftRule($message = null)
155
    {
156
        return $this->setFailureMode(self::SOFT_RULE, $message);
157 3
    }
158
159
    /**
160
     *
161
     * Sets this specification as a "hard" rule.
162
     *
163
     * @param string $message The failure message.
164
     *
165
     * @return self
166
     *
167
     */
168 2
    public function asHardRule($message = null)
169
    {
170
        return $this->setFailureMode(self::HARD_RULE, $message);
171 2
    }
172
173
    /**
174
     *
175
     * Sets this specification as a "stop" rule.
176
     *
177
     * @param string $message The failure message.
178
     *
179
     * @return self
180
     *
181
     */
182 1
    public function asStopRule($message = null)
183
    {
184
        return $this->setFailureMode(self::STOP_RULE, $message);
185 1
    }
186
187
    /**
188
     *
189
     * Sets the failure mode for this rule specification.
190
     *
191
     * @param string $failure_mode The failure mode.
192
     *
193
     * @param string $message The failure message.
194
     *
195
     * @return self
196
     *
197
     */
198
    protected function setFailureMode($failure_mode, $message)
199
    {
200
        $this->failure_mode = $failure_mode;
201
        if ($message) {
202
            $this->setMessage($message);
203
        }
204
        return $this;
205
    }
206
207
    /**
208
     *
209
     * Sets the failure message for this rule specification.
210
     *
211
     * @param string $message The failure message.
212
     *
213
     * @return self
214
     *
215
     */
216
    public function setMessage($message)
217
    {
218
        $this->message = $message;
219
        return $this;
220
    }
221
222
    /**
223
     *
224
     * On failure, should the subject filter stop processing all fields?
225
     *
226
     * @return bool
227
     *
228
     */
229 4
    public function isStopRule()
230
    {
231 4
        return $this->failure_mode === self::STOP_RULE;
232
    }
233
234
    /**
235
     *
236
     * On failure, should the subject filter stop processing the current field?
237
     *
238
     * @return bool
239
     *
240
     */
241 2
    public function isHardRule()
242
    {
243 2
        return $this->failure_mode === self::HARD_RULE;
244
    }
245
246
    /**
247
     *
248
     * On failure, should the subject filter keep processing the current field?
249
     *
250
     * @return bool
251
     *
252
     */
253 2
    public function isSoftRule()
254
    {
255 2
        return $this->failure_mode === self::SOFT_RULE;
256
    }
257
258
    /**
259
     *
260
     * Returns the field name for this rule specification.
261
     *
262
     * @return string
263
     *
264
     */
265 3
    public function getField()
266
    {
267 3
        return $this->field;
268
    }
269
270
    /**
271
     *
272
     * Returns the failure message for this rule specification.
273
     *
274
     * @return string
275
     *
276
     */
277 7
    public function getMessage()
278
    {
279 7
        if (! $this->message) {
280
            $this->message = $this->getDefaultMessage();
281
        }
282 7
        return $this->message;
283
    }
284
285
    /**
286
     *
287
     * Returns the arguments for this rule specification.
288
     *
289
     * @return array
290
     *
291
     */
292 2
    public function getArgs()
293
    {
294 2
        return $this->args;
295
    }
296
297
    /**
298
     *
299
     * Initializes this specification.
300
     *
301
     * @param array $args Arguments for the rule.
302
     *
303
     * @return self
304
     *
305
     */
306 13
    protected function init($args)
307
    {
308 13
        $this->args = $args;
309
        $this->rule = array_shift($this->args);
310 13
        return $this;
311
    }
312
313
    /**
314
     *
315
     * Returns the default failure message for this rule specification.
316
     *
317
     * @return string
318
     *
319
     */
320 11
    protected function getDefaultMessage()
321
    {
322 7
        $message = $this->rule;
323 7
        if (! $this->args) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->args of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
324 5
            return $message;
325
        };
326
327
        $message .= $this->argsToString($this->args);
328 11
        return $message;
329
    }
330
331 11
    protected function argsToString($args)
332
    {
333 11
        $vals = array();
334
        foreach ($args as $arg) {
335
            $vals[] = $this->argToString($arg);
336
        }
337
        return '(' . implode(', ', $vals) . ')';
338
    }
339
340 11
    protected function argToString($arg)
341
    {
342 11
        switch (true) {
343 11
            case is_object($arg):
344
                return '*' . get_class($arg) . '*';
345 10
            case is_array($arg):
346
                return '*array*';
347 10
            case is_resource($arg):
348
                return '*resource*';
349 10
            case is_null($arg):
350
                return '*null*';
351
            default:
352 10
                return $arg;
353
        }
354
    }
355
356
    /**
357
     *
358
     * Check if the field is allowed to be, and actually is, blank.
359
     *
360
     * @param mixed $subject The filter subject.
361
     *
362
     * @return bool
363
     *
364
     */
365 4
    protected function applyBlank($subject)
366
    {
367 4
        if (! $this->allow_blank) {
368 3
            return false;
369
        }
370
371
        // the field name
372 1
        $field = $this->field;
373
374
        // not set, or null, means it is blank
375 1
        if (! isset($subject->$field) || $subject->$field === null) {
376 1
            return true;
377
        }
378
379
        // non-strings are not blank: int, float, object, array, resource, etc
380
        if (! is_string($subject->$field)) {
381 4
            return false;
382
        }
383
384
        // strings that trim down to exactly nothing are blank
385
        return trim($subject->$field) === '';
386
    }
387
388
    /**
389
     *
390
     * Check if the subject field passes the rule specification.
391
     *
392
     * @param mixed $subject The filter subject.
393
     *
394
     * @return bool
395
     *
396
     */
397 2
    protected function applyRule($subject)
398
    {
399
        $rule = $this->locator->get($this->rule);
400 2
        $args = $this->args;
401
        array_unshift($args, $this->field);
402
        array_unshift($args, $subject);
403
        return call_user_func_array($rule, $args);
404
    }
405
}
406