1: <?php namespace Laravel; use Closure;
2:
3: class Validator {
4:
5: /**
6: * The array being validated.
7: *
8: * @var array
9: */
10: public $attributes;
11:
12: /**
13: * The post-validation error messages.
14: *
15: * @var Messages
16: */
17: public $errors;
18:
19: /**
20: * The validation rules.
21: *
22: * @var array
23: */
24: protected $rules = array();
25:
26: /**
27: * The validation messages.
28: *
29: * @var array
30: */
31: protected $messages = array();
32:
33: /**
34: * The database connection that should be used by the validator.
35: *
36: * @var Database\Connection
37: */
38: protected $db;
39:
40: /**
41: * The bundle for which the validation is being run.
42: *
43: * @var string
44: */
45: protected $bundle = DEFAULT_BUNDLE;
46:
47: /**
48: * The language that should be used when retrieving error messages.
49: *
50: * @var string
51: */
52: protected $language;
53:
54: /**
55: * The size related validation rules.
56: *
57: * @var array
58: */
59: protected $size_rules = array('size', 'between', 'min', 'max');
60:
61: /**
62: * The numeric related validation rules.
63: *
64: * @var array
65: */
66: protected $numeric_rules = array('numeric', 'integer');
67:
68: /**
69: * The registered custom validators.
70: *
71: * @var array
72: */
73: protected static $validators = array();
74:
75: /**
76: * Create a new validator instance.
77: *
78: * @param mixed $attributes
79: * @param array $rules
80: * @param array $messages
81: * @return void
82: */
83: public function __construct($attributes, $rules, $messages = array())
84: {
85: foreach ($rules as $key => &$rule)
86: {
87: $rule = (is_string($rule)) ? explode('|', $rule) : $rule;
88: }
89:
90: $this->rules = $rules;
91: $this->messages = $messages;
92: $this->attributes = (is_object($attributes)) ? get_object_vars($attributes) : $attributes;
93: }
94:
95: /**
96: * Create a new validator instance.
97: *
98: * @param array $attributes
99: * @param array $rules
100: * @param array $messages
101: * @return Validator
102: */
103: public static function make($attributes, $rules, $messages = array())
104: {
105: return new static($attributes, $rules, $messages);
106: }
107:
108: /**
109: * Register a custom validator.
110: *
111: * @param string $name
112: * @param Closure $validator
113: * @return void
114: */
115: public static function register($name, $validator)
116: {
117: static::$validators[$name] = $validator;
118: }
119:
120: /**
121: * Validate the target array using the specified validation rules.
122: *
123: * @return bool
124: */
125: public function passes()
126: {
127: return $this->valid();
128: }
129:
130: /**
131: * Validate the target array using the specified validation rules.
132: *
133: * @return bool
134: */
135: public function fails()
136: {
137: return $this->invalid();
138: }
139:
140: /**
141: * Validate the target array using the specified validation rules.
142: *
143: * @return bool
144: */
145: public function invalid()
146: {
147: return ! $this->valid();
148: }
149:
150: /**
151: * Validate the target array using the specified validation rules.
152: *
153: * @return bool
154: */
155: public function valid()
156: {
157: $this->errors = new Messages;
158:
159: foreach ($this->rules as $attribute => $rules)
160: {
161: foreach ($rules as $rule) $this->check($attribute, $rule);
162: }
163:
164: return count($this->errors->messages) == 0;
165: }
166:
167: /**
168: * Evaluate an attribute against a validation rule.
169: *
170: * @param string $attribute
171: * @param string $rule
172: * @return void
173: */
174: protected function check($attribute, $rule)
175: {
176: list($rule, $parameters) = $this->parse($rule);
177:
178: $value = array_get($this->attributes, $attribute);
179:
180: // Before running the validator, we need to verify that the attribute and rule
181: // combination is actually validatable. Only the "accepted" rule implies that
182: // the attribute is "required", so if the attribute does not exist, the other
183: // rules will not be run for the attribute.
184: $validatable = $this->validatable($rule, $attribute, $value);
185:
186: if ($validatable and ! $this->{'validate_'.$rule}($attribute, $value, $parameters, $this))
187: {
188: $this->error($attribute, $rule, $parameters);
189: }
190: }
191:
192: /**
193: * Determine if an attribute is validatable.
194: *
195: * To be considered validatable, the attribute must either exist, or the rule
196: * being checked must implicitly validate "required", such as the "required"
197: * rule or the "accepted" rule.
198: *
199: * @param string $rule
200: * @param string $attribute
201: * @param mixed $value
202: * @return bool
203: */
204: protected function validatable($rule, $attribute, $value)
205: {
206: return $this->validate_required($attribute, $value) or $this->implicit($rule);
207: }
208:
209: /**
210: * Determine if a given rule implies that the attribute is required.
211: *
212: * @param string $rule
213: * @return bool
214: */
215: protected function implicit($rule)
216: {
217: return $rule == 'required' or $rule == 'accepted' or $rule == 'required_with';
218: }
219:
220: /**
221: * Add an error message to the validator's collection of messages.
222: *
223: * @param string $attribute
224: * @param string $rule
225: * @param array $parameters
226: * @return void
227: */
228: protected function error($attribute, $rule, $parameters)
229: {
230: $message = $this->replace($this->message($attribute, $rule), $attribute, $rule, $parameters);
231:
232: $this->errors->add($attribute, $message);
233: }
234:
235: /**
236: * Validate that a required attribute exists in the attributes array.
237: *
238: * @param string $attribute
239: * @param mixed $value
240: * @return bool
241: */
242: protected function validate_required($attribute, $value)
243: {
244: if (is_null($value))
245: {
246: return false;
247: }
248: elseif (is_string($value) and trim($value) === '')
249: {
250: return false;
251: }
252: elseif ( ! is_null(Input::file($attribute)) and is_array($value) and $value['tmp_name'] == '')
253: {
254: return false;
255: }
256:
257: return true;
258: }
259:
260: /**
261: * Validate that an attribute exists in the attributes array, if another
262: * attribute exists in the attributes array.
263: *
264: * @param string $attribute
265: * @param mixed $value
266: * @param array $parameters
267: * @return bool
268: */
269: protected function validate_required_with($attribute, $value, $parameters)
270: {
271: $other = $parameters[0];
272: $other_value = array_get($this->attributes, $other);
273:
274: if ($this->validate_required($other, $other_value))
275: {
276: return $this->validate_required($attribute, $value);
277: }
278:
279: return true;
280: }
281:
282: /**
283: * Validate that an attribute has a matching confirmation attribute.
284: *
285: * @param string $attribute
286: * @param mixed $value
287: * @return bool
288: */
289: protected function validate_confirmed($attribute, $value)
290: {
291: return $this->validate_same($attribute, $value, array($attribute.'_confirmation'));
292: }
293:
294: /**
295: * Validate that an attribute was "accepted".
296: *
297: * This validation rule implies the attribute is "required".
298: *
299: * @param string $attribute
300: * @param mixed $value
301: * @return bool
302: */
303: protected function validate_accepted($attribute, $value)
304: {
305: return $this->validate_required($attribute, $value) and ($value == 'yes' or $value == '1' or $value == 'on');
306: }
307:
308: /**
309: * Validate that an attribute is the same as another attribute.
310: *
311: * @param string $attribute
312: * @param mixed $value
313: * @param array $parameters
314: * @return bool
315: */
316: protected function validate_same($attribute, $value, $parameters)
317: {
318: $other = $parameters[0];
319:
320: return array_key_exists($other, $this->attributes) and $value == $this->attributes[$other];
321: }
322:
323: /**
324: * Validate that an attribute is different from another attribute.
325: *
326: * @param string $attribute
327: * @param mixed $value
328: * @param array $parameters
329: * @return bool
330: */
331: protected function validate_different($attribute, $value, $parameters)
332: {
333: $other = $parameters[0];
334:
335: return array_key_exists($other, $this->attributes) and $value != $this->attributes[$other];
336: }
337:
338: /**
339: * Validate that an attribute is numeric.
340: *
341: * @param string $attribute
342: * @param mixed $value
343: * @return bool
344: */
345: protected function validate_numeric($attribute, $value)
346: {
347: return is_numeric($value);
348: }
349:
350: /**
351: * Validate that an attribute is an integer.
352: *
353: * @param string $attribute
354: * @param mixed $value
355: * @return bool
356: */
357: protected function validate_integer($attribute, $value)
358: {
359: return filter_var($value, FILTER_VALIDATE_INT) !== false;
360: }
361:
362: /**
363: * Validate the size of an attribute.
364: *
365: * @param string $attribute
366: * @param mixed $value
367: * @param array $parameters
368: * @return bool
369: */
370: protected function validate_size($attribute, $value, $parameters)
371: {
372: return $this->size($attribute, $value) == $parameters[0];
373: }
374:
375: /**
376: * Validate the size of an attribute is between a set of values.
377: *
378: * @param string $attribute
379: * @param mixed $value
380: * @param array $parameters
381: * @return bool
382: */
383: protected function validate_between($attribute, $value, $parameters)
384: {
385: $size = $this->size($attribute, $value);
386:
387: return $size >= $parameters[0] and $size <= $parameters[1];
388: }
389:
390: /**
391: * Validate the size of an attribute is greater than a minimum value.
392: *
393: * @param string $attribute
394: * @param mixed $value
395: * @param array $parameters
396: * @return bool
397: */
398: protected function validate_min($attribute, $value, $parameters)
399: {
400: return $this->size($attribute, $value) >= $parameters[0];
401: }
402:
403: /**
404: * Validate the size of an attribute is less than a maximum value.
405: *
406: * @param string $attribute
407: * @param mixed $value
408: * @param array $parameters
409: * @return bool
410: */
411: protected function validate_max($attribute, $value, $parameters)
412: {
413: return $this->size($attribute, $value) <= $parameters[0];
414: }
415:
416: /**
417: * Get the size of an attribute.
418: *
419: * @param string $attribute
420: * @param mixed $value
421: * @return mixed
422: */
423: protected function size($attribute, $value)
424: {
425: // This method will determine if the attribute is a number, string, or file and
426: // return the proper size accordingly. If it is a number, the number itself is
427: // the size; if it is a file, the kilobytes is the size; if it is a
428: // string, the length is the size.
429: if (is_numeric($value) and $this->has_rule($attribute, $this->numeric_rules))
430: {
431: return $this->attributes[$attribute];
432: }
433: elseif (array_key_exists($attribute, Input::file()))
434: {
435: return $value['size'] / 1024;
436: }
437: else
438: {
439: return Str::length(trim($value));
440: }
441: }
442:
443: /**
444: * Validate an attribute is contained within a list of values.
445: *
446: * @param string $attribute
447: * @param mixed $value
448: * @param array $parameters
449: * @return bool
450: */
451: protected function validate_in($attribute, $value, $parameters)
452: {
453: return in_array($value, $parameters);
454: }
455:
456: /**
457: * Validate an attribute is not contained within a list of values.
458: *
459: * @param string $attribute
460: * @param mixed $value
461: * @param array $parameters
462: * @return bool
463: */
464: protected function validate_not_in($attribute, $value, $parameters)
465: {
466: return ! in_array($value, $parameters);
467: }
468:
469: /**
470: * Validate the uniqueness of an attribute value on a given database table.
471: *
472: * If a database column is not specified, the attribute will be used.
473: *
474: * @param string $attribute
475: * @param mixed $value
476: * @param array $parameters
477: * @return bool
478: */
479: protected function validate_unique($attribute, $value, $parameters)
480: {
481: // We allow the table column to be specified just in case the column does
482: // not have the same name as the attribute. It must be within the second
483: // parameter position, right after the database table name.
484: if (isset($parameters[1]))
485: {
486: $attribute = $parameters[1];
487: }
488:
489: $query = $this->db()->table($parameters[0])->where($attribute, '=', $value);
490:
491: // We also allow an ID to be specified that will not be included in the
492: // uniqueness check. This makes updating columns easier since it is
493: // fine for the given ID to exist in the table.
494: if (isset($parameters[2]))
495: {
496: $id = (isset($parameters[3])) ? $parameters[3] : 'id';
497:
498: $query->where($id, '<>', $parameters[2]);
499: }
500:
501: return $query->count() == 0;
502: }
503:
504: /**
505: * Validate the existence of an attribute value in a database table.
506: *
507: * @param string $attribute
508: * @param mixed $value
509: * @param array $parameters
510: * @return bool
511: */
512: protected function validate_exists($attribute, $value, $parameters)
513: {
514: if (isset($parameters[1])) $attribute = $parameters[1];
515:
516: // Grab the number of elements we are looking for. If the given value is
517: // in array, we'll count all of the values in the array, otherwise we
518: // can just make sure the count is greater or equal to one.
519: $count = (is_array($value)) ? count($value) : 1;
520:
521: $query = $this->db()->table($parameters[0]);
522:
523: // If the given value is an array, we will check for the existence of
524: // all the values in the database, otherwise we'll check for the
525: // presence of the single given value in the database.
526: if (is_array($value))
527: {
528: $query = $query->where_in($attribute, $value);
529: }
530: else
531: {
532: $query = $query->where($attribute, '=', $value);
533: }
534:
535: return $query->count() >= $count;
536: }
537:
538: /**
539: * Validate that an attribute is a valid IP.
540: *
541: * @param string $attribute
542: * @param mixed $value
543: * @return bool
544: */
545: protected function validate_ip($attribute, $value)
546: {
547: return filter_var($value, FILTER_VALIDATE_IP) !== false;
548: }
549:
550: /**
551: * Validate that an attribute is a valid e-mail address.
552: *
553: * @param string $attribute
554: * @param mixed $value
555: * @return bool
556: */
557: protected function validate_email($attribute, $value)
558: {
559: return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
560: }
561:
562: /**
563: * Validate that an attribute is a valid URL.
564: *
565: * @param string $attribute
566: * @param mixed $value
567: * @return bool
568: */
569: protected function validate_url($attribute, $value)
570: {
571: return filter_var($value, FILTER_VALIDATE_URL) !== false;
572: }
573:
574: /**
575: * Validate that an attribute is an active URL.
576: *
577: * @param string $attribute
578: * @param mixed $value
579: * @return bool
580: */
581: protected function validate_active_url($attribute, $value)
582: {
583: $url = str_replace(array('http://', 'https://', 'ftp://'), '', Str::lower($value));
584:
585: return (trim($url) !== '') ? checkdnsrr($url) : false;
586: }
587:
588: /**
589: * Validate the MIME type of a file is an image MIME type.
590: *
591: * @param string $attribute
592: * @param mixed $value
593: * @return bool
594: */
595: protected function validate_image($attribute, $value)
596: {
597: return $this->validate_mimes($attribute, $value, array('jpg', 'png', 'gif', 'bmp'));
598: }
599:
600: /**
601: * Validate that an attribute contains only alphabetic characters.
602: *
603: * @param string $attribute
604: * @param mixed $value
605: * @return bool
606: */
607: protected function validate_alpha($attribute, $value)
608: {
609: return preg_match('/^([a-z])+$/i', $value);
610: }
611:
612: /**
613: * Validate that an attribute contains only alpha-numeric characters.
614: *
615: * @param string $attribute
616: * @param mixed $value
617: * @return bool
618: */
619: protected function validate_alpha_num($attribute, $value)
620: {
621: return preg_match('/^([a-z0-9])+$/i', $value);
622: }
623:
624: /**
625: * Validate that an attribute contains only alpha-numeric characters, dashes, and underscores.
626: *
627: * @param string $attribute
628: * @param mixed $value
629: * @return bool
630: */
631: protected function validate_alpha_dash($attribute, $value)
632: {
633: return preg_match('/^([-a-z0-9_-])+$/i', $value);
634: }
635:
636: /**
637: * Validate that an attribute passes a regular expression check.
638: *
639: * @param string $attribute
640: * @param mixed $value
641: * @param array $parameters
642: * @return bool
643: */
644: protected function validate_match($attribute, $value, $parameters)
645: {
646: return preg_match($parameters[0], $value);
647: }
648:
649: /**
650: * Validate the MIME type of a file upload attribute is in a set of MIME types.
651: *
652: * @param string $attribute
653: * @param array $value
654: * @param array $parameters
655: * @return bool
656: */
657: protected function validate_mimes($attribute, $value, $parameters)
658: {
659: if ( ! is_array($value) or array_get($value, 'tmp_name', '') == '') return true;
660:
661: foreach ($parameters as $extension)
662: {
663: if (File::is($extension, $value['tmp_name']))
664: {
665: return true;
666: }
667: }
668:
669: return false;
670: }
671:
672: /**
673: * Validate that an attribute is an array
674: *
675: * @param string $attribute
676: * @param mixed $value
677: * @return bool
678: */
679: protected function validate_array($attribute, $value)
680: {
681: return is_array($value);
682: }
683:
684: /**
685: * Validate that an attribute of type array has a specific count
686: *
687: * @param string $attribute
688: * @param mixed $value
689: * @param array $parameters
690: * @return bool
691: */
692: protected function validate_count($attribute, $value, $parameters)
693: {
694: return (is_array($value) && count($value) == $parameters[0]);
695: }
696:
697: /**
698: * Validate that an attribute of type array has a minimum of elements.
699: *
700: * @param string $attribute
701: * @param mixed $value
702: * @param array $parameters
703: * @return bool
704: */
705: protected function validate_countmin($attribute, $value, $parameters)
706: {
707: return (is_array($value) && count($value) >= $parameters[0]);
708: }
709:
710: /**
711: * Validate that an attribute of type array has a maximum of elements.
712: *
713: * @param string $attribute
714: * @param mixed $value
715: * @param array $parameters
716: * @return bool
717: */
718: protected function validate_countmax($attribute, $value, $parameters)
719: {
720: return (is_array($value) && count($value) <= $parameters[0]);
721: }
722:
723: /**
724: * Validate that an attribute of type array has elements between max and min.
725: *
726: * @param string $attribute
727: * @param mixed $value
728: * @param array $parameters
729: * @return bool
730: */
731: protected function validate_countbetween($attribute, $value, $parameters)
732: {
733: return (is_array($value) && count($value) >= $parameters[0] && count($value) <= $parameters[1] );
734: }
735:
736: /**
737: * Validate the date is before a given date.
738: *
739: * @param string $attribute
740: * @param mixed $value
741: * @param array $parameters
742: * @return bool
743: */
744: protected function validate_before($attribute, $value, $parameters)
745: {
746: return (strtotime($value) < strtotime($parameters[0]));
747: }
748:
749: /**
750: * Validate the date is after a given date.
751: *
752: * @param string $attribute
753: * @param mixed $value
754: * @param array $parameters
755: * @return bool
756: */
757: protected function validate_after($attribute, $value, $parameters)
758: {
759: return (strtotime($value) > strtotime($parameters[0]));
760: }
761:
762: /**
763: * Validate the date conforms to a given format.
764: *
765: * @param string $attribute
766: * @param mixed $value
767: * @param array $parameters
768: * @return bool
769: */
770: protected function validate_date_format($attribute, $value, $parameters)
771: {
772: return date_create_from_format($parameters[0], $value) !== false;
773: }
774:
775: /**
776: * Get the proper error message for an attribute and rule.
777: *
778: * @param string $attribute
779: * @param string $rule
780: * @return string
781: */
782: protected function message($attribute, $rule)
783: {
784: $bundle = Bundle::prefix($this->bundle);
785:
786: // First we'll check for developer specified, attribute specific messages.
787: // These messages take first priority. They allow the fine-grained tuning
788: // of error messages for each rule.
789: $custom = $attribute.'_'.$rule;
790:
791: if (array_key_exists($custom, $this->messages))
792: {
793: return $this->messages[$custom];
794: }
795: elseif (Lang::has($custom = "{$bundle}validation.custom.{$custom}", $this->language))
796: {
797: return Lang::line($custom)->get($this->language);
798: }
799:
800: // Next we'll check for developer specified, rule specific error messages.
801: // These allow the developer to override the error message for an entire
802: // rule, regardless of the attribute being validated by that rule.
803: elseif (array_key_exists($rule, $this->messages))
804: {
805: return $this->messages[$rule];
806: }
807:
808: // If the rule being validated is a "size" rule, we will need to gather
809: // the specific size message for the type of attribute being validated,
810: // either a number, file, or string.
811: elseif (in_array($rule, $this->size_rules))
812: {
813: return $this->size_message($bundle, $attribute, $rule);
814: }
815:
816: // If no developer specified messages have been set, and no other special
817: // messages apply to the rule, we will just pull the default validation
818: // message from the validation language file.
819: else
820: {
821: $line = "{$bundle}validation.{$rule}";
822:
823: return Lang::line($line)->get($this->language);
824: }
825: }
826:
827: /**
828: * Get the proper error message for an attribute and size rule.
829: *
830: * @param string $bundle
831: * @param string $attribute
832: * @param string $rule
833: * @return string
834: */
835: protected function size_message($bundle, $attribute, $rule)
836: {
837: // There are three different types of size validations. The attribute
838: // may be either a number, file, or a string, so we'll check a few
839: // things to figure out which one it is.
840: if ($this->has_rule($attribute, $this->numeric_rules))
841: {
842: $line = 'numeric';
843: }
844: // We assume that attributes present in the $_FILES array are files,
845: // which makes sense. If the attribute doesn't have numeric rules
846: // and isn't a file, it's a string.
847: elseif (array_key_exists($attribute, Input::file()))
848: {
849: $line = 'file';
850: }
851: else
852: {
853: $line = 'string';
854: }
855:
856: return Lang::line("{$bundle}validation.{$rule}.{$line}")->get($this->language);
857: }
858:
859: /**
860: * Replace all error message place-holders with actual values.
861: *
862: * @param string $message
863: * @param string $attribute
864: * @param string $rule
865: * @param array $parameters
866: * @return string
867: */
868: protected function replace($message, $attribute, $rule, $parameters)
869: {
870: $message = str_replace(':attribute', $this->attribute($attribute), $message);
871:
872: if (method_exists($this, $replacer = 'replace_'.$rule))
873: {
874: $message = $this->$replacer($message, $attribute, $rule, $parameters);
875: }
876:
877: return $message;
878: }
879:
880: /**
881: * Replace all place-holders for the required_with rule.
882: *
883: * @param string $message
884: * @param string $attribute
885: * @param string $rule
886: * @param array $parameters
887: * @return string
888: */
889: protected function replace_required_with($message, $attribute, $rule, $parameters)
890: {
891: return str_replace(':field', $this->attribute($parameters[0]), $message);
892: }
893:
894: /**
895: * Replace all place-holders for the between rule.
896: *
897: * @param string $message
898: * @param string $attribute
899: * @param string $rule
900: * @param array $parameters
901: * @return string
902: */
903: protected function replace_between($message, $attribute, $rule, $parameters)
904: {
905: return str_replace(array(':min', ':max'), $parameters, $message);
906: }
907:
908: /**
909: * Replace all place-holders for the size rule.
910: *
911: * @param string $message
912: * @param string $attribute
913: * @param string $rule
914: * @param array $parameters
915: * @return string
916: */
917: protected function replace_size($message, $attribute, $rule, $parameters)
918: {
919: return str_replace(':size', $parameters[0], $message);
920: }
921:
922: /**
923: * Replace all place-holders for the min rule.
924: *
925: * @param string $message
926: * @param string $attribute
927: * @param string $rule
928: * @param array $parameters
929: * @return string
930: */
931: protected function replace_min($message, $attribute, $rule, $parameters)
932: {
933: return str_replace(':min', $parameters[0], $message);
934: }
935:
936: /**
937: * Replace all place-holders for the max rule.
938: *
939: * @param string $message
940: * @param string $attribute
941: * @param string $rule
942: * @param array $parameters
943: * @return string
944: */
945: protected function replace_max($message, $attribute, $rule, $parameters)
946: {
947: return str_replace(':max', $parameters[0], $message);
948: }
949:
950: /**
951: * Replace all place-holders for the in rule.
952: *
953: * @param string $message
954: * @param string $attribute
955: * @param string $rule
956: * @param array $parameters
957: * @return string
958: */
959: protected function replace_in($message, $attribute, $rule, $parameters)
960: {
961: return str_replace(':values', implode(', ', $parameters), $message);
962: }
963:
964: /**
965: * Replace all place-holders for the not_in rule.
966: *
967: * @param string $message
968: * @param string $attribute
969: * @param string $rule
970: * @param array $parameters
971: * @return string
972: */
973: protected function replace_not_in($message, $attribute, $rule, $parameters)
974: {
975: return str_replace(':values', implode(', ', $parameters), $message);
976: }
977:
978: /**
979: * Replace all place-holders for the mimes rule.
980: *
981: * @param string $message
982: * @param string $attribute
983: * @param string $rule
984: * @param array $parameters
985: * @return string
986: */
987: protected function replace_mimes($message, $attribute, $rule, $parameters)
988: {
989: return str_replace(':values', implode(', ', $parameters), $message);
990: }
991:
992: /**
993: * Replace all place-holders for the same rule.
994: *
995: * @param string $message
996: * @param string $attribute
997: * @param string $rule
998: * @param array $parameters
999: * @return string
1000: */
1001: protected function replace_same($message, $attribute, $rule, $parameters)
1002: {
1003: return str_replace(':other', $this->attribute($parameters[0]), $message);
1004: }
1005:
1006: /**
1007: * Replace all place-holders for the different rule.
1008: *
1009: * @param string $message
1010: * @param string $attribute
1011: * @param string $rule
1012: * @param array $parameters
1013: * @return string
1014: */
1015: protected function replace_different($message, $attribute, $rule, $parameters)
1016: {
1017: return str_replace(':other', $this->attribute($parameters[0]), $message);
1018: }
1019:
1020: /**
1021: * Replace all place-holders for the before rule.
1022: *
1023: * @param string $message
1024: * @param string $attribute
1025: * @param string $rule
1026: * @param array $parameters
1027: * @return string
1028: */
1029: protected function replace_before($message, $attribute, $rule, $parameters)
1030: {
1031: return str_replace(':date', $parameters[0], $message);
1032: }
1033:
1034: /**
1035: * Replace all place-holders for the after rule.
1036: *
1037: * @param string $message
1038: * @param string $attribute
1039: * @param string $rule
1040: * @param array $parameters
1041: * @return string
1042: */
1043: protected function replace_after($message, $attribute, $rule, $parameters)
1044: {
1045: return str_replace(':date', $parameters[0], $message);
1046: }
1047:
1048: /**
1049: * Replace all place-holders for the count rule.
1050: *
1051: * @param string $message
1052: * @param string $attribute
1053: * @param string $rule
1054: * @param array $parameters
1055: * @return string
1056: */
1057: protected function replace_count($message, $attribute, $rule, $parameters)
1058: {
1059: return str_replace(':count', $parameters[0], $message);
1060: }
1061:
1062: /**
1063: * Replace all place-holders for the countmin rule.
1064: *
1065: * @param string $message
1066: * @param string $attribute
1067: * @param string $rule
1068: * @param array $parameters
1069: * @return string
1070: */
1071: protected function replace_countmin($message, $attribute, $rule, $parameters)
1072: {
1073: return str_replace(':min', $parameters[0], $message);
1074: }
1075:
1076: /**
1077: * Replace all place-holders for the countmax rule.
1078: *
1079: * @param string $message
1080: * @param string $attribute
1081: * @param string $rule
1082: * @param array $parameters
1083: * @return string
1084: */
1085: protected function replace_countmax($message, $attribute, $rule, $parameters)
1086: {
1087: return str_replace(':max', $parameters[0], $message);
1088: }
1089:
1090: /**
1091: * Replace all place-holders for the between rule.
1092: *
1093: * @param string $message
1094: * @param string $attribute
1095: * @param string $rule
1096: * @param array $parameters
1097: * @return string
1098: */
1099: protected function replace_countbetween($message, $attribute, $rule, $parameters)
1100: {
1101: return str_replace(array(':min', ':max'), $parameters, $message);
1102: }
1103:
1104: /**
1105: * Get the displayable name for a given attribute.
1106: *
1107: * @param string $attribute
1108: * @return string
1109: */
1110: protected function attribute($attribute)
1111: {
1112: $bundle = Bundle::prefix($this->bundle);
1113:
1114: // More reader friendly versions of the attribute names may be stored
1115: // in the validation language file, allowing a more readable version
1116: // of the attribute name in the message.
1117: $line = "{$bundle}validation.attributes.{$attribute}";
1118:
1119: if (Lang::has($line, $this->language))
1120: {
1121: return Lang::line($line)->get($this->language);
1122: }
1123:
1124: // If no language line has been specified for the attribute, all of
1125: // the underscores are removed from the attribute name and that
1126: // will be used as the attribute name.
1127: else
1128: {
1129: return str_replace('_', ' ', $attribute);
1130: }
1131: }
1132:
1133: /**
1134: * Determine if an attribute has a rule assigned to it.
1135: *
1136: * @param string $attribute
1137: * @param array $rules
1138: * @return bool
1139: */
1140: protected function has_rule($attribute, $rules)
1141: {
1142: foreach ($this->rules[$attribute] as $rule)
1143: {
1144: list($rule, $parameters) = $this->parse($rule);
1145:
1146: if (in_array($rule, $rules)) return true;
1147: }
1148:
1149: return false;
1150: }
1151:
1152: /**
1153: * Extract the rule name and parameters from a rule.
1154: *
1155: * @param string $rule
1156: * @return array
1157: */
1158: protected function parse($rule)
1159: {
1160: $parameters = array();
1161:
1162: // The format for specifying validation rules and parameters follows a
1163: // {rule}:{parameters} formatting convention. For instance, the rule
1164: // "max:3" specifies that the value may only be 3 characters long.
1165: if (($colon = strpos($rule, ':')) !== false)
1166: {
1167: $parameters = str_getcsv(substr($rule, $colon + 1));
1168: }
1169:
1170: return array(is_numeric($colon) ? substr($rule, 0, $colon) : $rule, $parameters);
1171: }
1172:
1173: /**
1174: * Set the bundle that the validator is running for.
1175: *
1176: * The bundle determines which bundle the language lines will be loaded from.
1177: *
1178: * @param string $bundle
1179: * @return Validator
1180: */
1181: public function bundle($bundle)
1182: {
1183: $this->bundle = $bundle;
1184: return $this;
1185: }
1186:
1187: /**
1188: * Set the language that should be used when retrieving error messages.
1189: *
1190: * @param string $language
1191: * @return Validator
1192: */
1193: public function speaks($language)
1194: {
1195: $this->language = $language;
1196: return $this;
1197: }
1198:
1199: /**
1200: * Set the database connection that should be used by the validator.
1201: *
1202: * @param Database\Connection $connection
1203: * @return Validator
1204: */
1205: public function connection(Database\Connection $connection)
1206: {
1207: $this->db = $connection;
1208: return $this;
1209: }
1210:
1211: /**
1212: * Get the database connection for the Validator.
1213: *
1214: * @return Database\Connection
1215: */
1216: protected function db()
1217: {
1218: if ( ! is_null($this->db)) return $this->db;
1219:
1220: return $this->db = Database::connection();
1221: }
1222:
1223: /**
1224: * Dynamically handle calls to custom registered validators.
1225: */
1226: public function __call($method, $parameters)
1227: {
1228: // First we will slice the "validate_" prefix off of the validator since
1229: // custom validators aren't registered with such a prefix, then we can
1230: // just call the method with the given parameters.
1231: if (isset(static::$validators[$method = substr($method, 9)]))
1232: {
1233: return call_user_func_array(static::$validators[$method], $parameters);
1234: }
1235:
1236: throw new \Exception("Method [$method] does not exist.");
1237: }
1238:
1239: }
1240: