Completed
Push — master ( c61969...df1bf5 )
by Marcus
02:35
created

AbstractSpec::setRule()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
dl 0
loc 11
c 0
b 0
f 0
ccs 5
cts 6
cp 0.8333
rs 9.4285
cc 2
eloc 6
nc 2
nop 1
crap 2.0185
1
<?php
2
3
namespace Mbright\Validation\Spec;
4
5
use Mbright\Validation\Exception\RuleClassNotDefinedException;
6
use Mbright\Validation\Exception\ValidationException;
7
8
abstract class AbstractSpec
9
{
10
    /**
11
     * Failure Mode for a halting failure.
12
     *
13
     * @var string
14
     */
15
    const HALTING_FAILURE = 'HALTING_FAILURE';
16
17
    /**
18
     * Failure Mode for a hard failure.
19
     *
20
     * @var string
21
     */
22
    const HARD_FAILURE = 'HARD_FAILURE';
23
24
    /**
25
     * Failure Mode for a soft failure.
26
     *
27
     * @var string
28
     */
29
    const SOFT_FAILURE = 'SOFT_FAILURE';
30
31
    /**
32
     * Field name for the spec to operate on.
33
     *
34
     * @var string
35
     */
36
    protected $field;
37
38
    /**
39
     * Rule to invoke.
40
     *
41
     * @var callable
42
     */
43
    protected $rule;
44
45
    /**
46
     * Arguments supplied to the rule
47
     *
48
     * @var array
49
     */
50
    protected $args = [];
51
52
    /**
53
     * Failure message to be used instead of the default message.
54
     *
55
     * @var string
56
     */
57
    protected $message;
58
59
    /**
60
     * Name of the rule to execute.
61
     *
62
     * @var string
63
     */
64
    protected $ruleClass;
65
66
    /**
67
     * Flag that determines the allowance of blank values.
68
     *
69
     * @var bool
70
     */
71
    protected $allowBlanks = false;
72
73
    /**
74
     * An array of values to be considered blank.
75
     *
76
     * @var array
77
     */
78
    protected $blankWhiteList = [];
79
80
    /**
81
     * AbstractSpec constructor.
82
     *
83
     * @param string $field
84
     */
85 120
    public function __construct(string $field)
86
    {
87 120
        $this->field = $field;
88 120
    }
89
90
    /**
91
     * Invokes the rule that this spec is configured for.
92
     *
93
     * @param object $subject
94
     *
95
     * @return bool
96
     */
97 33
    public function __invoke($subject): bool
98
    {
99 33
        return ($this->rule)($subject, $this->field, ...array_values($this->args));
100
    }
101
102
    /**
103
     * Set a custom message.
104
     *
105
     * @param string $message
106
     *
107
     * @return self
108
     */
109 3
    public function setMessage(string $message): self
110
    {
111 3
        $this->message = $message;
112
113 3
        return $this;
114
    }
115
116
    /**
117
     * Returns the field this spec applies to.
118
     *
119
     * @return string
120
     */
121 54
    public function getField(): string
122
    {
123 54
        return $this->field;
124
    }
125
126
    /**
127
     * Returns the failure message for this rule specification.
128
     *
129
     * @return string
130
     */
131 27
    public function getMessage(): string
132
    {
133 27
        if (!$this->message) {
134 24
            $this->message = $this->getDefaultMessage();
135
        }
136
137 27
        return $this->message;
138
    }
139
140
    /**
141
     * Returns the args this spec is configured to use.
142
     *
143
     * @return array
144
     */
145 24
    public function getArgs(): array
146
    {
147 24
        return $this->args;
148
    }
149
150
    /**
151
     * Returns the name of the rule that was ran.
152
     *
153
     * @return string
154
     */
155 6
    public function getRuleClass(): string
156
    {
157 6
        return $this->ruleClass;
158
    }
159
160
    /**
161
     * Sets the rule and ruleClass for the spec.
162
     *
163
     * @param string $ruleClass
164
     *
165
     * @throws RuleClassNotDefinedException
166
     *
167
     * @return AbstractSpec
168
     */
169 69
    public function setRule(string $ruleClass): self
170
    {
171 69
        if (!class_exists($ruleClass)) {
172
            throw new RuleClassNotDefinedException("Could not find mapping for [$ruleClass]");
173
        }
174
175 69
        $this->rule = new $ruleClass();
176 69
        $this->ruleClass = $ruleClass;
177
178 69
        return $this;
179
    }
180
181
    /**
182
     * Sets the white list of values that should be considered blank.
183
     *
184
     * @param array $blankWhiteList
185
     *
186
     * @return ValidateSpec
187
     */
188 12
    public function setBlankValues(array $blankWhiteList): self
189
    {
190 12
        $this->blankWhiteList = $blankWhiteList;
191
192 12
        return $this;
193
    }
194
195
    /**
196
     * Sets the rule to halt the entire validation process on the subject.
197
     *
198
     * @return AbstractSpec
199
     */
200 3
    public function asHaltingRule(): self
201
    {
202 3
        $this->failureMode = self::HALTING_FAILURE;
0 ignored issues
show
Bug introduced by
The property failureMode does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
203
204 3
        return $this;
205
    }
206
207
    /**
208
     * Sets the rule to stop further rules from applying to the same field.
209
     *
210
     * @return AbstractSpec
211
     */
212 3
    public function asHardRule(): self
213
    {
214 3
        $this->failureMode = self::HARD_FAILURE;
215
216 3
        return $this;
217
    }
218
219
    /**
220
     * Sets the rule to allow other rules to operate on the same field.
221
     *
222
     * @return AbstractSpec
223
     */
224 3
    public function asSoftRule(): self
225
    {
226 3
        $this->failureMode = self::SOFT_FAILURE;
227
228 3
        return $this;
229
    }
230
231
    /**
232
     * Returns the current failure mode.
233
     *
234
     * @return string
235
     */
236 24
    public function getFailureMode(): string
237
    {
238 24
        return $this->failureMode;
239
    }
240
241
    /**
242
     * Determines if the field is a `valid` blank value.
243
     *
244
     * Values are considered blank if they are, not sent, null, or strings that trim down to nothing. integers, floats,
245
     * arrays, resources, objects, etc., are never considered blank. Even a value of `(int) 0` will *not* evaluate as
246
     * blank.
247
     * The optional second argument is used to supply an array of white listed items that should be considered blank.
248
     *
249
     * @param mixed $subject
250
     * @param array $blankWhiteList
0 ignored issues
show
Bug introduced by
There is no parameter named $blankWhiteList. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
251
     *
252
     * @return bool
253
     */
254 81
    public function subjectFieldIsBlank($subject): bool
255
    {
256 81
        foreach ($this->blankWhiteList as $item) {
257 9
            if ($subject->{$this->field} === $item) {
258 9
                return true;
259
            }
260
        }
261
262
        // not set, or null, means it is blank
263 72
        if (!isset($subject->{$this->field}) || $subject->{$this->field} === null) {
264 33
            return true;
265
        }
266
267
        // non-strings are not blank: int, float, object, array, resource, etc
268 39
        if (!is_string($subject->{$this->field})) {
269 18
            return false;
270
        }
271
272
        // strings that trim down to exactly nothing are blank
273 21
        return trim($subject->{$this->field}) === '';
274
    }
275
276
    /**
277
     * Returns the default failure message for this rule specification.
278
     *
279
     * @return string
280
     */
281 24
    protected function getDefaultMessage(): string
282
    {
283 24
        return $this->ruleClass . $this->argsTostring();
284
    }
285
286
    /**
287
     * Converts the args to a string.
288
     *
289
     * @return string
290
     */
291 24
    protected function argsTostring(): string
292
    {
293 24
        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...
294 21
            return '()';
295
        }
296
297 3
        return '(' . implode(', ', $this->args) . ')';
298
    }
299
}
300