Completed
Pull Request — master (#26)
by Andres
02:23
created

AbstractValidator   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 247
Duplicated Lines 4.05 %

Coupling/Cohesion

Components 3
Dependencies 4

Importance

Changes 17
Bugs 2 Features 12
Metric Value
c 17
b 2
f 12
dl 10
loc 247
rs 10
wmc 28
lcom 3
cbo 4

11 Methods

Rating   Name   Duplication   Size   Complexity  
A toBool() 0 6 1
A defaultValue() 0 6 1
A __construct() 0 5 1
D __invoke() 0 33 9
A __call() 0 4 1
A required() 0 13 2
A optional() 0 6 1
A add() 0 6 1
A anyOf() 10 10 2
A errorMessages() 0 13 2
C createError() 0 39 7

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
namespace Comfort\Validator;
3
4
use Comfort\Comfort;
5
use Comfort\Error;
6
use Comfort\Exception\DiscomfortException;
7
use Comfort\Exception\ValidationException;
8
use Comfort\ValidationError;
9
use Comfort\Validator\Helper\AlternativesTrait;
10
11
/**
12
 * Class AbstractValidator
13
 * @package Comfort\Validator
14
 */
15
abstract class AbstractValidator
16
{
17
    use AlternativesTrait;
18
19
    /**
20
     * @var \Closure[]
21
     */
22
    protected $validationStack = [];
23
    /**
24
     * @var Comfort
25
     */
26
    private $comfort;
27
    /**
28
     * @var boolean
29
     */
30
    private $toBool = false;
31
    /**
32
     * @var bool
33
     */
34
    private $optional = true;
35
    /**
36
     * @var mixed
37
     */
38
    private $defaultValue;
39
    /**
40
     * @var array
41
     */
42
    protected $errorHandlers = [
43
        'default' => [
44
            'message' => 'There was a validation error'
45
        ],
46
        'required' => [
47
            'message' => '%s is required',
48
            'default' => 'value'
49
        ]
50
    ];
51
52
    public function __construct(Comfort $comfort)
53
    {
54
55
        $this->comfort = $comfort;
56
    }
57
58
    /**
59
     * Execute validation stack
60
     *
61
     * @param mixed $value
62
     * @param null|string $key
63
     * @return bool|ValidationError|null
64
     */
65
    public function __invoke($value, $key = null)
66
    {
67
        if (is_null($value) && $this->optional) {
68
            if (is_null($this->defaultValue)) {
69
                return null;
70
            } else {
71
                $value = $this->defaultValue;
72
            }
73
        }
74
75
        try {
76
            reset($this->validationStack);
77
78
            do {
79
                /** @var callable $validator */
80
                $validator = current($this->validationStack);
81
                $retVal = $validator($value, $key);
82
                $value = $retVal === null ? $value : $retVal;
83
            } while (next($this->validationStack));
84
85
            if ($this->toBool) {
86
                return true;
87
            }
88
89
            return $value;
90
        } catch (ValidationException $validationException) {
91
            if ($this->toBool) {
92
                return false;
93
            }
94
95
            return ValidationError::fromException($validationException);
96
        }
97
    }
98
99
    public function __call($name, $arguments)
100
    {
101
        return call_user_func_array([$this->comfort, $name], $arguments);
102
    }
103
104
    /**
105
     * Declare the value as being required
106
     *
107
     * @return $this
108
     */
109
    public function required()
110
    {
111
        $this->optional = false;
112
113
        $this->add(function ($value, $nameKey) {
114
            if (is_null($value)) {
115
                $this->createError('required', $value, $nameKey);
116
            }
117
        });
118
119
120
        return $this;
121
    }
122
123
124
    /**
125
     * Declare data is optional
126
     *
127
     * @return $this
128
     */
129
    public function optional()
130
    {
131
        $this->optional = true;
132
133
        return $this;
134
    }
135
136
    /**
137
     * Add adhoc validator to validation stack
138
     *
139
     * @param callable $validation
140
     * @return $this
141
     */
142
    public function add(callable $validation)
143
    {
144
        $this->validationStack[] = $validation;
145
146
        return $this;
147
    }
148
149
    /**
150
     * Validate given value matches any of the provided strings
151
     *
152
     * @param array $vals
153
     * @return $this
154
     */
155 View Code Duplication
    public function anyOf(array $vals)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
156
    {
157
        $this->add(function ($value, $nameKey) use ($vals) {
158
            if (!in_array($value, $vals)) {
159
                return $this->createError('anyof', $value, $nameKey);
160
            }
161
        });
162
163
        return $this;
164
    }
165
166
    /**
167
     * On validation failure whether to return false or a validation error
168
     *
169
     * @param bool $val
170
     * @return $this
171
     */
172
    public function toBool($val = true)
173
    {
174
        $this->toBool = (boolean)$val;
175
176
        return $this;
177
    }
178
179
    /**
180
     * Set default value
181
     *
182
     * @param $value
183
     * @return $this
184
     */
185
    public function defaultValue($value)
186
    {
187
        $this->defaultValue = $value;
188
189
        return $this;
190
    }
191
192
    /**
193
     * Set error messages
194
     *
195
     * @param array $errorMessages
196
     * @return $this
197
     */
198
    public function errorMessages(array $errorMessages)
199
    {
200
        $errorMessages = array_map(function ($errorMessage) {
201
            if (is_string($errorMessage)) {
202
                $errorMessage = ['message' => $errorMessage];
203
            }
204
205
            return $errorMessage;
206
        }, $errorMessages);
207
208
        $this->errorHandlers = array_merge($this->errorHandlers, $errorMessages);
209
        return $this;
210
    }
211
212
    /**
213
     * Create an error with a formatted message
214
     *
215
     * @param string $key
216
     * @param null|string $value
217
     * @param null|string $valueKey
218
     * @param mixed $validationValue
219
     * @throws DiscomfortException
220
     * @throws ValidationException
221
     */
222
    protected function createError($key, $value = null, $valueKey = null, $validationValue = null)
0 ignored issues
show
Unused Code introduced by
The parameter $validationValue is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
223
    {
224
        if (!array_key_exists($key, $this->errorHandlers)) {
225
            throw new ValidationException(
226
                $key,
227
                $this->errorHandlers['default']['message']
228
            );
229
        }
230
231
        $errorHandler = $this->errorHandlers[$key];
232
        if (!array_key_exists('message_formatter', $errorHandler)) {
233
            $messageFormatter = function ($template, $value, $validationValue = null) {
234
                return sprintf($template, $value, $validationValue);
235
            };
236
        } else {
237
            $messageFormatter = $errorHandler['message_formatter'];
238
            if (!is_callable($messageFormatter)) {
239
                throw new DiscomfortException('"message_formatter" must be callable');
240
            }
241
        }
242
243
        if (!is_null($valueKey)) {
244
            $templateValue = $valueKey;
245
        } else {
246
            if (!is_string($value)) {
247
                $valueType = gettype($value);
248
                $templateValue = "'{$valueType}'";
249
            } else {
250
                $templateValue = "'{$value}'";
251
            }
252
        }
253
254
        $errorMessage = $messageFormatter(
255
            $errorHandler['message'],
256
            $templateValue ?: $errorHandler['value']
257
        );
258
259
        throw new ValidationException($key, $errorMessage);
260
    }
261
}
262