Passed
Push — master ( 999d23...fe5b90 )
by Paul
04:24
created

Validator::normalizeData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 5
ccs 3
cts 4
cp 0.75
crap 2.0625
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules;
4
5
use BadMethodCallException;
6
use GeminiLabs\SiteReviews\Defaults\ValidationStrings;
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
		elseif( is_array( $value )) {
185
			return count( $value );
186
		}
187
		return mb_strlen( $value );
188
	}
189
190
	/**
191
	 * Get the proper error message for an attribute and size rule.
192
	 * @param string $attribute
193
	 * @param string $rule
194
	 * @return string|null
195
	 */
196
	protected function getSizeMessage( $attribute, $rule, array $parameters )
197
	{
198
		$type = $this->getAttributeType( $attribute );
199
		$lowerRule = glsr( Helper::class )->snakeCase( $rule.$type );
200
		return $this->translator( $lowerRule, $parameters );
201
	}
202
203
	/**
204
	 * Get the value of a given attribute.
205
	 * @param string $attribute
206
	 * @return mixed
207
	 */
208 1
	protected function getValue( $attribute )
209
	{
210 1
		if( isset( $this->data[$attribute] )) {
211 1
			return $this->data[$attribute];
212
		}
213
	}
214
215
	/**
216
	 * Determine if the given attribute has a rule in the given set.
217
	 * @param string $attribute
218
	 * @param string|array $rules
219
	 * @return bool
220
	 */
221 1
	protected function hasRule( $attribute, $rules )
222
	{
223 1
		return !is_null( $this->getRule( $attribute, $rules ));
224
	}
225
226
	/**
227
	 * Normalize the provided data to an array.
228
	 * @param mixed $data
229
	 * @return void
230
	 */
231 1
	protected function normalizeData( $data )
232
	{
233 1
		$this->data = is_object( $data )
234
			? get_object_vars( $data )
235 1
			: $data;
236 1
	}
237
238
	/**
239
	 * Parse a parameter list.
240
	 * @param string $rule
241
	 * @param string $parameter
242
	 * @return array
243
	 */
244 1
	protected function parseParameters( $rule, $parameter )
245
	{
246 1
		if( strtolower( $rule ) == 'regex' ) {
247
			return [$parameter];
248
		}
249 1
		return str_getcsv( $parameter );
250
	}
251
252
	/**
253
	 * Extract the rule name and parameters from a rule.
254
	 * @param string $rule
255
	 * @return array
256
	 */
257 1
	protected function parseRule( $rule )
258
	{
259 1
		$parameters = [];
260 1
		if( strpos( $rule, ':' ) !== false ) {
261 1
			list( $rule, $parameter ) = explode( ':', $rule, 2 );
262 1
			$parameters = $this->parseParameters( $rule, $parameter );
263
		}
264 1
		$rule = glsr( Helper::class )->camelCase( $rule );
265 1
		return [$rule, $parameters];
266
	}
267
268
	/**
269
	 * Set the validation rules.
270
	 * @return void
271
	 */
272 1
	protected function setRules( array $rules )
273
	{
274 1
		foreach( $rules as $key => $rule ) {
275 1
			$rules[$key] = is_string( $rule )
276 1
				? explode( '|', $rule )
277 1
				: $rule;
278
		}
279 1
		$this->rules = $rules;
280 1
	}
281
282
	/**
283
	 * Check if we should stop further validations on a given attribute.
284
	 * @param string $attribute
285
	 * @return bool
286
	 */
287 1
	protected function shouldStopValidating( $attribute )
288
	{
289 1
		return $this->hasRule( $attribute, $this->implicitRules )
290 1
			&& isset( $this->failedRules[$attribute] )
291 1
			&& array_intersect( array_keys( $this->failedRules[$attribute] ), $this->implicitRules );
292
	}
293
294
	/**
295
	 * Returns a translated message for the attribute
296
	 * @param string $key
297
	 * @param string $attribute
298
	 * @return void|string
299
	 */
300
	protected function translator( $key, array $parameters )
301
	{
302
		$strings = glsr( ValidationStrings::class )->defaults();
303
		if( isset( $strings[$key] )) {
304
			return $this->replace( $strings[$key], $parameters );
305
		}
306
	}
307
}
308