Test Failed
Push — hotfix/fix-counts ( 1fe4ce...872cd6 )
by Paul
03:14
created

Validator   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 295
Duplicated Lines 0 %

Test Coverage

Coverage 61.29%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 81
c 3
b 1
f 0
dl 0
loc 295
ccs 57
cts 93
cp 0.6129
rs 9.1199
wmc 41

17 Methods

Rating   Name   Duplication   Size   Complexity  
A addError() 0 4 1
A addFailure() 0 4 1
A validateAttribute() 0 10 4
A getValue() 0 4 2
A setRules() 0 8 3
A normalizeData() 0 5 2
A hasRule() 0 3 1
A shouldStopValidating() 0 5 3
A parseRule() 0 9 2
A getAttributeType() 0 5 2
A validate() 0 11 4
A parseParameters() 0 6 2
A getSize() 0 12 5
A translator() 0 7 2
A getRule() 0 8 4
A getSizeMessage() 0 5 1
A getMessage() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like Validator 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.

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 Validator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules;
4
5
use BadMethodCallException;
6
use GeminiLabs\SiteReviews\Defaults\ValidationStringsDefaults;
7
use GeminiLabs\SiteReviews\Helper;
8
use GeminiLabs\SiteReviews\Modules\Validator\ValidationRules;
9
use InvalidArgumentException;
10
11
/**
12
 * @see \Illuminate\Validation\Validator (5.3)
13
 */
14
class Validator
15
{
16
	use ValidationRules;
17
18
	/**
19
	 * @var array
20
	 */
21
	public $errors = [];
22
23
	/**
24
	 * The data under validation.
25
	 * @var array
26
	 */
27
	protected $data = [];
28
29
	/**
30
	 * The failed validation rules.
31
	 * @var array
32
	 */
33
	protected $failedRules = [];
34
35
	/**
36
	 * The rules to be applied to the data.
37
	 * @var array
38
	 */
39
	protected $rules = [];
40
41
	/**
42
	 * The size related validation rules.
43
	 * @var array
44
	 */
45
	protected $sizeRules = [
46
		'Between', 'Max', 'Min',
47
	];
48
49
	/**
50
	 * The validation rules that imply the field is required.
51
	 * @var array
52
	 */
53
	protected $implicitRules = [
54
		'Required',
55
	];
56
57
	/**
58
	 * The numeric related validation rules.
59
	 * @var array
60
	 */
61
	protected $numericRules = [
62
		'Number',
63
	];
64
65
	/**
66
	 * Run the validator's rules against its data.
67
	 * @param mixed $data
68
	 * @return array
69
	 */
70 1
	public function validate( $data, array $rules = [] )
71
	{
72 1
		$this->normalizeData( $data );
73 1
		$this->setRules( $rules );
74 1
		foreach( $this->rules as $attribute => $rules ) {
75 1
			foreach( $rules as $rule ) {
76 1
				$this->validateAttribute( $attribute, $rule );
77 1
				if( $this->shouldStopValidating( $attribute ))break;
78
			}
79
		}
80 1
		return $this->errors;
81
	}
82
83
	/**
84
	 * Validate a given attribute against a rule.
85
	 * @param string $attribute
86
	 * @param string $rule
87
	 * @return void
88
	 * @throws BadMethodCallException
89
	 */
90 1
	public function validateAttribute( $attribute, $rule )
91
	{
92 1
		list( $rule, $parameters ) = $this->parseRule( $rule );
93 1
		if( $rule == '' )return;
94 1
		$value = $this->getValue( $attribute );
95 1
		if( !method_exists( $this, $method = 'validate'.$rule )) {
96
			throw new BadMethodCallException( "Method [$method] does not exist." );
97
		}
98 1
		if( !$this->$method( $value, $attribute, $parameters )) {
99
			$this->addFailure( $attribute, $rule, $parameters );
100
		}
101 1
	}
102
103
	/**
104
	 * Add an error message to the validator's collection of errors.
105
	 * @param string $attribute
106
	 * @param string $rule
107
	 * @return void
108
	 */
109
	protected function addError( $attribute, $rule, array $parameters )
110
	{
111
		$message = $this->getMessage( $attribute, $rule, $parameters );
112
		$this->errors[$attribute][] = $message;
113
	}
114
115
	/**
116
	 * Add a failed rule and error message to the collection.
117
	 * @param string $attribute
118
	 * @param string $rule
119
	 * @return void
120
	 */
121
	protected function addFailure( $attribute, $rule, array $parameters )
122
	{
123
		$this->addError( $attribute, $rule, $parameters );
124
		$this->failedRules[$attribute][$rule] = $parameters;
125
	}
126
127
	/**
128
	 * Get the data type of the given attribute.
129
	 * @param string $attribute
130
	 * @return string
131
	 */
132
	protected function getAttributeType( $attribute )
133
	{
134
		return !$this->hasRule( $attribute, $this->numericRules )
135
			? 'length'
136
			: '';
137
	}
138
139
	/**
140
	 * Get the validation message for an attribute and rule.
141
	 * @param string $attribute
142
	 * @param string $rule
143
	 * @return string|null
144
	 */
145
	protected function getMessage( $attribute, $rule, array $parameters )
146
	{
147
		if( in_array( $rule, $this->sizeRules )) {
148
			return $this->getSizeMessage( $attribute, $rule, $parameters );
149
		}
150
		$lowerRule = glsr( Helper::class )->snakeCase( $rule );
151
		return $this->translator( $lowerRule, $parameters );
152
	}
153
154
	/**
155
	 * Get a rule and its parameters for a given attribute.
156
	 * @param string $attribute
157
	 * @param string|array $rules
158
	 * @return array|null
159
	 */
160 1
	protected function getRule( $attribute, $rules )
161
	{
162 1
		if( !array_key_exists( $attribute, $this->rules ))return;
163 1
		$rules = (array)$rules;
164 1
		foreach( $this->rules[$attribute] as $rule ) {
165 1
			list( $rule, $parameters ) = $this->parseRule( $rule );
166 1
			if( in_array( $rule, $rules )) {
167 1
				return [$rule, $parameters];
168
			}
169
		}
170 1
	}
171
172
	/**
173
	 * Get the size of an attribute.
174
	 * @param string $attribute
175
	 * @param mixed $value
176
	 * @return mixed
177
	 */
178 1
	protected function getSize( $attribute, $value )
179
	{
180 1
		$hasNumeric = $this->hasRule( $attribute, $this->numericRules );
181 1
		if( is_numeric( $value ) && $hasNumeric ) {
182 1
			return $value;
183
		}
184
		else if( is_array( $value )) {
185
			return count( $value );
186
		}
187
		return function_exists( 'mb_strlen' )
188
			? mb_strlen( $value )
189
			: strlen( $value );
190
	}
191
192
	/**
193
	 * Get the proper error message for an attribute and size rule.
194
	 * @param string $attribute
195
	 * @param string $rule
196
	 * @return string|null
197
	 */
198
	protected function getSizeMessage( $attribute, $rule, array $parameters )
199
	{
200
		$type = $this->getAttributeType( $attribute );
201
		$lowerRule = glsr( Helper::class )->snakeCase( $rule.$type );
202
		return $this->translator( $lowerRule, $parameters );
203
	}
204
205
	/**
206
	 * Get the value of a given attribute.
207
	 * @param string $attribute
208
	 * @return mixed
209
	 */
210 1
	protected function getValue( $attribute )
211
	{
212 1
		if( isset( $this->data[$attribute] )) {
213 1
			return $this->data[$attribute];
214
		}
215
	}
216
217
	/**
218
	 * Determine if the given attribute has a rule in the given set.
219
	 * @param string $attribute
220
	 * @param string|array $rules
221
	 * @return bool
222
	 */
223 1
	protected function hasRule( $attribute, $rules )
224
	{
225 1
		return !is_null( $this->getRule( $attribute, $rules ));
226
	}
227
228
	/**
229
	 * Normalize the provided data to an array.
230
	 * @param mixed $data
231
	 * @return void
232
	 */
233 1
	protected function normalizeData( $data )
234
	{
235 1
		$this->data = is_object( $data )
236
			? get_object_vars( $data )
237 1
			: $data;
238 1
	}
239
240
	/**
241
	 * Parse a parameter list.
242
	 * @param string $rule
243
	 * @param string $parameter
244
	 * @return array
245
	 */
246 1
	protected function parseParameters( $rule, $parameter )
247
	{
248 1
		if( strtolower( $rule ) == 'regex' ) {
249
			return [$parameter];
250
		}
251 1
		return str_getcsv( $parameter );
252
	}
253
254
	/**
255
	 * Extract the rule name and parameters from a rule.
256
	 * @param string $rule
257
	 * @return array
258
	 */
259 1
	protected function parseRule( $rule )
260
	{
261 1
		$parameters = [];
262 1
		if( strpos( $rule, ':' ) !== false ) {
263 1
			list( $rule, $parameter ) = explode( ':', $rule, 2 );
264 1
			$parameters = $this->parseParameters( $rule, $parameter );
265
		}
266 1
		$rule = glsr( Helper::class )->camelCase( $rule );
267 1
		return [$rule, $parameters];
268
	}
269
270
	/**
271
	 * Set the validation rules.
272
	 * @return void
273
	 */
274 1
	protected function setRules( array $rules )
275
	{
276 1
		foreach( $rules as $key => $rule ) {
277 1
			$rules[$key] = is_string( $rule )
278 1
				? explode( '|', $rule )
279 1
				: $rule;
280
		}
281 1
		$this->rules = $rules;
282 1
	}
283
284
	/**
285
	 * Check if we should stop further validations on a given attribute.
286
	 * @param string $attribute
287
	 * @return bool
288
	 */
289 1
	protected function shouldStopValidating( $attribute )
290
	{
291 1
		return $this->hasRule( $attribute, $this->implicitRules )
292 1
			&& isset( $this->failedRules[$attribute] )
293 1
			&& array_intersect( array_keys( $this->failedRules[$attribute] ), $this->implicitRules );
294
	}
295
296
	/**
297
	 * Returns a translated message for the attribute
298
	 * @param string $key
299
	 * @param string $attribute
300
	 * @return void|string
301
	 */
302
	protected function translator( $key, array $parameters )
303
	{
304
		$strings = glsr( ValidationStringsDefaults::class )->defaults();
305
		if( isset( $strings[$key] )) {
306
			return $this->replace( $strings[$key], $parameters );
307
		}
308
		return "error";
309
	}
310
}
311