Completed
Push — master ( 731a89...d11137 )
by Emlyn
04:30
created

Validator::setGlobalMessages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 9
ccs 0
cts 5
cp 0
rs 9.6666
cc 2
eloc 4
nc 2
nop 1
crap 6
1
<?php
2
/**
3
 * @package   Fuel\Validation
4
 * @version   2.0
5
 * @author    Fuel Development Team
6
 * @license   MIT License
7
 * @copyright 2010 - 2013 Fuel Development Team
8
 * @link      http://fuelphp.com
9
 */
10
11
namespace Fuel\Validation;
12
13
use InvalidArgumentException;
14
use LogicException;
15
16
/**
17
 * Main entry point for the validation functionality. Handles registering validation rules and loading validation
18
 * adaptors.
19
 *
20
 * @package Fuel\Validation
21
 * @author  Fuel Development Team
22
 * @since   2.0
23
 *
24
 * @method $this email()
25
 * @method $this ip()
26
 * @method $this matchField(string $matchAgainst)
27
 * @method $this minLength(integer $minLength)
28
 * @method $this maxLength(integer $maxLength)
29
 * @method $this number()
30
 * @method $this numericBetween(integer $min, integer $max)
31
 * @method $this numericMax(integer $max)
32
 * @method $this numericMin(integer $min)
33
 * @method $this regex(string $regex)
34
 * @method $this required()
35
 * @method $this url()
36
 * @method $this date(string $format)
37
 * @method $this type(string $type)
38
 * @method $this enum(array $values)
39
 * @method $this enumMulti(array $values)
40
 * @method $this validator(ValidatableInterface $validator)
41
 */
42
class Validator implements ValidatableInterface
43
{
44
45
	/**
46
	 * Contains a list of fields to be validated
47
	 *
48
	 * @var FieldInterface[]
49
	 */
50
	protected $fields = array();
51
52
	/**
53
	 * Contains a list of any custom validation rules
54
	 *
55
	 * @var string[]
56
	 */
57
	protected $customRules = array();
58
59
	/**
60
	 * @var string[]
61
	 */
62
	protected $messages = array();
63
64
	/**
65
	 * Keeps track of the last field added for magic method chaining
66
	 *
67
	 * @var FieldInterface
68
	 */
69
	protected $lastAddedField;
70
71
	/**
72
	 * Keeps track of the last rule added for message setting
73
	 *
74
	 * @var RuleInterface
75
	 */
76
	protected $lastAddedRule;
77
78
	/**
79
	 * Default namespace to look for rules in when a rule is not known
80
	 *
81
	 * @var string
82
	 */
83
	protected $ruleNamespace = 'Fuel\Validation\Rule\\';
84
85
	/**
86
	 * Adds a rule that can be used to validate a field
87
	 *
88
	 * @param string|FieldInterface $field
89
	 * @param RuleInterface         $rule
90
	 *
91
	 * @return $this
92
	 *
93
	 * @since 2.0
94
	 */
95 17
	public function addRule($field, RuleInterface $rule)
96
	{
97 17
		if (is_string($field))
98 17
		{
99
			try
100
			{
101 11
				$field = $this->getField($field);
102
			}
103 11
			catch (InvalidFieldException $ife)
104
			{
105
				// The field does not exist so create it
106 5
				$this->addField($field);
107 5
				$field = $this->getField($field);
108
			}
109 11
		}
110
111
		// We have a valid field now so add the rule
112 17
		$field->addRule($rule);
113
114 17
		$this->lastAddedRule = $rule;
115
116 17
		return $this;
117
	}
118
119
	/**
120
	 * Adds a new field to the validation object
121
	 *
122
	 * @param string|FieldInterface $field
123
	 * @param string                $label Field name to use in messages, set to null to use $field
124
	 *
125
	 * @return $this
126
	 *
127
	 * @throws InvalidArgumentException
128
	 *
129
	 * @since 2.0
130
	 */
131 20
	public function addField($field, $label = null)
132
	{
133 20
		if (is_string($field))
134 20
		{
135 18
			$field = new Field($field, $label);
136 18
		}
137
138 20
		if ( ! $field instanceof FieldInterface)
139 20
		{
140 1
			throw new InvalidArgumentException('VAL-007: Only FieldInterfaces can be added as a field.');
141
		}
142
143 19
		$this->fields[$field->getName()] = $field;
144 19
		$this->lastAddedField = $field;
145
146 19
		return $this;
147
	}
148
149
	/**
150
	 * Returns the given field
151
	 *
152
	 * @param $name
153
	 *
154
	 * @return FieldInterface
155
	 *
156
	 * @throws InvalidFieldException
157
	 *
158
	 * @since 2.0
159
	 */
160 21
	public function getField($name)
161
	{
162 21
		if ( ! isset($this->fields[$name]))
163 21
		{
164 7
			throw new InvalidFieldException($name);
165
		}
166
167 19
		return $this->fields[$name];
168
	}
169
170
	/**
171
	 * Takes an array of data and validates that against the assigned rules.
172
	 * The array is expected to have keys named after fields.
173
	 * This function will call reset() before it runs.
174
	 *
175
	 * @param array           $data
176
	 * @param ResultInterface $result
177
	 *
178
	 * @return ResultInterface
179
	 *
180
	 * @since 2.0
181
	 */
182 12
	public function run($data, ResultInterface $result = null)
183
	{
184 12
		if ($result === null)
185 12
		{
186 12
			$result = new Result;
187 12
		}
188
189 12
		$result->setResult(true);
190
191 12
		foreach ($this->fields as $fieldName => $rules)
192
		{
193 12
			$fieldResult = $this->validateField($fieldName, $data, $result);
194
195 12
			if ( ! $fieldResult)
196 12
			{
197
				// There was a failure so log it to the result object
198 5
				$result->setResult(false);
199 5
			}
200 12
		}
201
202 12
		return $result;
203
	}
204
205
	/**
206
	 * Takes a field name and an array of data and validates the field against the assigned rules.
207
	 * The array is expected to have keys named after fields.
208
	 * This function will call reset() before it runs.
209
	 *
210
	 * @param string          $field
211
	 * @param array           $data
212
	 * @param ResultInterface $result
213
	 *
214
	 * @return ResultInterface
215
	 *
216
	 * @since 2.0
217
	 */
218 2
	public function runField($field, array $data, ResultInterface $result = null)
219
	{
220 2
		if ($result === null)
221 2
		{
222 2
			$result = new Result;
223 2
		}
224
225 2
		$fieldResult = false;
226
227 2
		if (isset($data[$field]))
228 2
		{
229 1
			$fieldResult = $this->validateField($field, $data, $result);
230 1
		}
231
232
		// Log the result
233 2
		$result->setResult($fieldResult);
234
235 2
		return $result;
236
	}
237
238
	/**
239
	 * Validates a single field
240
	 *
241
	 * @param string          $field
242
	 * @param mixed[]         $data
243
	 * @param ResultInterface $result
244
	 *
245
	 * @return bool
246
	 *
247
	 * @since 2.0
248
	 */
249 13
	protected function validateField($field, $data, ResultInterface $result)
250
	{
251 13
		$value = null;
252
253
		// If there is data, and the data is not empty and not numeric. This allows for strings such as '0' to be passed
254
		// as valid values.
255 13
		$dataPresent = isset($data[$field]) && ! (empty($data[$field]) && ! is_numeric($data[$field]));
256
257
		if ($dataPresent)
258 13
		{
259 11
			$value = $data[$field];
260 11
		}
261
262 13
		$rules = $this->getFieldRules($field);
263
264 13
		foreach ($rules as $rule)
265
		{
266 13
			if ( ! $dataPresent && ! $rule->canAlwaysRun())
267 13
			{
268 4
				continue;
269
			}
270
271 12
			$validateResult = $rule->validate($value, $field, $data);
272 12
			if ( ! $validateResult)
273 12
			{
274
				// Don't allow any others to run if this one failed
275 6
				$result->setError($field, $this->buildMessage($this->getField($field), $rule, $value), $rule);
0 ignored issues
show
Documentation introduced by
$rule is of type object<Fuel\Validation\RuleInterface>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
276
277 6
				return false;
278
			}
279 10
		}
280
281
		// All is good so make sure the field gets added as one of the validated fields
282 10
		$result->setValidated($field);
283
284 10
		return true;
285
	}
286
287
	/**
288
	 * Gets a Rule's message and processes that with various tokens
289
	 *
290
	 * @param FieldInterface $field
291
	 * @param RuleInterface  $rule
292
	 *
293
	 * @return string
294
	 */
295 6
	protected function buildMessage(FieldInterface $field, RuleInterface $rule, $value)
296
	{
297
		// Build an array with all the token values
298
		$tokens = array(
299 6
			'name' => $field->getName(),
300 6
			'label' => $field->getLabel(),
301 6
			'value' => $value,
302
303 6
		) + $rule->getMessageParameters();
304
305 6
		return $this->processMessageTokens($tokens, $rule->getMessage());
306
	}
307
308
	/**
309
	 * Replaces any {} tokens with the matching value from $tokens.
310
	 *
311
	 * @param array $tokens   Associative array of token names and values
312
	 * @param string $message
313
	 *
314
	 * @return string
315
	 *
316
	 * @since 2.0
317
	 */
318 6
	protected function processMessageTokens(array $tokens, $message)
319
	{
320 6
		foreach ($tokens as $token => $value)
321
		{
322 6
			$message = str_replace('{' . $token . '}', $value, $message);
323 6
		}
324
325 6
		return $message;
326
	}
327
328
	/**
329
	 * @param string $fieldName
330
	 *
331
	 * @return RuleInterface[]
332
	 */
333 16
	public function getFieldRules($fieldName)
334
	{
335
		try
336
		{
337 16
			$field = $this->getField($fieldName);
338
		}
339 16
		catch (InvalidFieldException $ife)
340
		{
341
			// No field found so no rules
342 1
			return array();
343
		}
344
345 15
		return $field->getRules();
346
	}
347
348
	/**
349
	 * Allows validation rules to be dynamically added using method chaining.
350
	 *
351
	 * @param string $name
352
	 * @param array  $arguments
353
	 *
354
	 * @return $this
355
	 * @throws InvalidRuleException
356
	 *
357
	 * @since 2.0
358
	 */
359 7
	public function __call($name, $arguments)
360
	{
361
		// Create and then add the new rule to the last added field
362 7
		$rule = $this->createRuleInstance($name, $arguments);
363
364 6
		$this->addRule($this->lastAddedField, $rule);
365
366 6
		return $this;
367
	}
368
369
	/**
370
	 * Sets the failure message for the last added rule
371
	 *
372
	 * @param string $message
373
	 *
374
	 * @return $this
375
	 *
376
	 * @throws LogicException
377
	 *
378
	 * @since 2.0
379
	 */
380 3
	public function setMessage($message)
381
	{
382 3
		if ( ! $this->lastAddedRule)
383 3
		{
384 1
			throw new LogicException('VAL-006: A rule should be added before setting a message.');
385
		}
386
387 2
		$this->lastAddedRule->setMessage($message);
388
389 2
		return $this;
390
	}
391
392
	/**
393
	 * Creates an instance of the given rule name
394
	 *
395
	 * @param string $name
396
	 * @param mixed  $parameters
397
	 *
398
	 * @return RuleInterface
399
	 *
400
	 * @throws InvalidRuleException
401
	 *
402
	 * @since 2.0
403
	 */
404 11
	public function createRuleInstance($name, $parameters = [])
405
	{
406 11
		$className = $this->getRuleClassName($name);
407
408 11
		if ( ! class_exists($className))
409 11
		{
410 2
			throw new InvalidRuleException($name);
411
		}
412
413
		/* @var RuleInterface $instance */
414 9
		$reflection = new \ReflectionClass($className);
415 9
		$instance = $reflection->newInstanceArgs($parameters);
416
417
		// Check if there is a custom message
418 9
		$message = $this->getGlobalMessage($name);
419
420 9
		if ($message !== null)
421 9
		{
422 1
			$instance->setMessage($message);
423 1
		}
424
425 9
		return $instance;
426
	}
427
428
	/**
429
	 * Returns the full class name for the given validation rule
430
	 *
431
	 * @param string $name
432
	 *
433
	 * @return string
434
	 *
435
	 * @since 2.0
436
	 */
437 11
	protected function getRuleClassName($name)
438
	{
439
		// Check if we have a custom rule registered
440 11
		if (isset($this->customRules[$name]))
441 11
		{
442
			// We do so grab the class name from the store
443 3
			return $this->customRules[$name];
444
		}
445
446 8
		return $this->ruleNamespace . ucfirst($name);
447
	}
448
449
	/**
450
	 * Adds custom validation rules and allows for core rules to be overridden.
451
	 * When wanting to override a core rule just specify the rule name as $name.
452
	 * Eg, 'required', 'minLength'. Note the lowercase first letter.
453
	 *
454
	 * The name of the rule should not contain any whitespace or special characters as the name will be available
455
	 * to use as a function name in the method chaining syntax.
456
	 *
457
	 * @param string $name
458
	 * @param string $class
459
	 *
460
	 * @return $this
461
	 *
462
	 * @since 2.0
463
	 */
464 3
	public function addCustomRule($name, $class)
465
	{
466 3
		$this->customRules[$name] = $class;
467
468 3
		return $this;
469
	}
470
471
	/**
472
	 * Sets a custom message for all fields of the given type that are created after the message has been set.
473
	 *
474
	 * @param string      $ruleName Name of the rule to set a message for, eg, required, number, exactLength
475
	 * @param string|null $message  Set to null to disable the custom message
476
	 *
477
	 * @return $this
478
	 *
479
	 * @since 2.0
480
	 */
481 2
	public function setGlobalMessage($ruleName, $message)
482
	{
483 2
		$this->messages[$ruleName] = $message;
484
485 2
		if ($message === null)
486 2
		{
487 1
			$this->removeGlobalMessage($ruleName);
488 1
		}
489
490 2
		return $this;
491
	}
492
493
	/**
494
	 * Sets custom messages for one or more rules. Setting the value to "null" will remove the message
495
	 *
496
	 * @param string[] $messages
497
	 *
498
	 * @return $this
499
	 *
500
	 * @since 2.0
501
	 */
502
	public function setGlobalMessages($messages)
503
	{
504
		foreach ($messages as $name => $value)
505
		{
506
			$this->setGlobalMessage($name, $value);
507
		}
508
509
		return $this;
510
	}
511
512
	/**
513
	 * Removes a global rule message
514
	 *
515
	 * @param string $ruleName
516
	 *
517
	 * @return $this
518
	 *
519
	 * @since 2.0
520
	 */
521 1
	public function removeGlobalMessage($ruleName)
522
	{
523 1
		unset($this->messages[$ruleName]);
524
525 1
		return $this;
526
	}
527
528
	/**
529
	 * Gets the global message set for a rule
530
	 *
531
	 * @param string $ruleName
532
	 *
533
	 * @return null|string Will be null if there is no message
534
	 */
535 10
	public function getGlobalMessage($ruleName)
536
	{
537 10
		if ( ! isset($this->messages[$ruleName]))
538 10
		{
539 9
			return null;
540
		}
541
542 2
		return $this->messages[$ruleName];
543
	}
544
545
}
546