Overview

Namespaces

  • None
  • PHP
  • Wei
    • Validator

Classes

  • Apc
  • App
  • ArrayCache
  • Asset
  • Base
  • BaseCache
  • Bicache
  • Cache
  • Config
  • Cookie
  • Couchbase
  • Counter
  • Db
  • DbCache
  • E
  • Env
  • Error
  • FileCache
  • Gravatar
  • Http
  • Lock
  • Logger
  • Memcache
  • Memcached
  • MongoCache
  • Password
  • PhpError
  • Pinyin
  • Record
  • Redis
  • Request
  • Response
  • Router
  • SafeUrl
  • Session
  • Soap
  • T
  • Ua
  • Upload
  • Url
  • Uuid
  • Validate
  • View
  • WeChatApp
  • Wei
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Wei Framework
  4:  *
  5:  * @copyright   Copyright (c) 2008-2013 Twin Huang
  6:  * @license     http://opensource.org/licenses/mit-license.php MIT License
  7:  */
  8: 
  9: namespace Wei;
 10: 
 11: use Wei\Validator\BaseValidator;
 12: 
 13: /**
 14:  * A validator service
 15:  *
 16:  * @author      Twin Huang <twinhuang@qq.com>
 17:  */
 18: class Validate extends Base
 19: {
 20:     /**
 21:      * The validation rules
 22:      *
 23:      * @var array
 24:      */
 25:     protected $rules = array();
 26: 
 27:     /**
 28:      * The data to be validated
 29:      *
 30:      * @var array
 31:      */
 32:     protected $data = array();
 33: 
 34:     /**
 35:      * The invalid messages
 36:      *
 37:      * @var array
 38:      */
 39:     protected $messages = array();
 40: 
 41:     /**
 42:      * The names for messages
 43:      *
 44:      * @var array
 45:      */
 46:     protected $names = array();
 47: 
 48:     /**
 49:      * The callback triggered before validation
 50:      *
 51:      * @var callable
 52:      */
 53:     protected $beforeValidate;
 54: 
 55:     /**
 56:      * The callback triggered when every rule is valid
 57:      *
 58:      * @var callable
 59:      */
 60:     protected $ruleValid;
 61: 
 62:     /**
 63:      * The callback triggered when every rule is invalid
 64:      *
 65:      * @var callable
 66:      */
 67:     protected $ruleInvalid;
 68: 
 69:     /**
 70:      * The callback triggered when every field is valid
 71:      *
 72:      * @var callable
 73:      */
 74:     protected $fieldValid;
 75: 
 76:     /**
 77:      * The callback triggered when every field is invalid
 78:      *
 79:      * @var callable
 80:      */
 81:     protected $fieldInvalid;
 82: 
 83:     /**
 84:      * The callback triggered after all rules are valid
 85:      *
 86:      * @var callable
 87:      */
 88:     protected $success;
 89: 
 90:     /**
 91:      * The callback triggered when the validation is invalid
 92:      *
 93:      * @var callable
 94:      */
 95:     protected $failure;
 96: 
 97:     /**
 98:      * Whether break the validation flow when any field's rule is not valid
 99:      *
100:      * @var bool
101:      */
102:     protected $breakRule = false;
103: 
104:     /**
105:      * Whether break the validation flow when any field is not valid
106:      *
107:      * @var bool
108:      */
109:     protected $breakField = false;
110: 
111:     /**
112:      * Whether skip the current field validation when the filed's rule is not
113:      * valid, so every field contains one invalid rule at most
114:      *
115:      * @var bool
116:      */
117:     protected $skip = false;
118: 
119:     /**
120:      * The valid rules array, which use the field as key, and the rules as value
121:      *
122:      * @var string
123:      */
124:     protected $validRules = array();
125: 
126:     /**
127:      * The invalid rules array, which use the field as key, and the rules as value
128:      *
129:      * @var string
130:      */
131:     protected $invalidRules = array();
132: 
133:     /**
134:      * The validation result
135:      *
136:      * @var bool|null
137:      */
138:     protected $result;
139: 
140:     /**
141:      * The rule validator instances
142:      *
143:      * @var array<Validator\BaseValidator>
144:      */
145:     protected $ruleValidators = array();
146: 
147:     /**
148:      * Create a new validator and validate by specified options
149:      *
150:      * @param array $options
151:      * @return $this
152:      */
153:     public function __invoke(array $options = array())
154:     {
155:         $validator = new self($options + get_object_vars($this));
156: 
157:         $validator->valid($options);
158: 
159:         return $validator;
160:     }
161: 
162:     /**
163:      * Validate the data by the given options
164:      *
165:      * @param array $options The options for validation
166:      * @return bool Whether pass the validation or not
167:      * @throws \InvalidArgumentException  When validation rule is not array, string or instance of BaseValidator
168:      */
169:     public function valid($options = array())
170:     {
171:         $options && $this->setOption($options);
172: 
173:         // Initialize the validation result to be true
174:         $this->result = true;
175: 
176:         $this->beforeValidate && call_user_func($this->beforeValidate, $this, $this->wei);
177: 
178:         foreach ($this->rules as $field => $rules) {
179:             $data = $this->getFieldData($field);
180: 
181:             /**
182:              * Process simple rule
183:              * FROM
184:              * 'username' => 'required'
185:              * TO
186:              * 'username' => array(
187:              *     'required' => true
188:              * )
189:              */
190:             if (is_string($rules)) {
191:                 $rules = array($rules => true);
192:             } elseif ($rules instanceof BaseValidator) {
193:                 $rules = array($rules);
194:             } elseif (!is_array($rules)) {
195:                 throw new \InvalidArgumentException(sprintf(
196:                     'Expected argument of type array, string or instance of Wei\Validator\BaseValidator, "%s" given',
197:                     is_object($rules) ? get_class($rules) : gettype($rules)
198:                 ));
199:             }
200: 
201:             // Make sure the "required" rule at first
202:             if (!isset($rules['required'])) {
203:                 $isRequired = true;
204:             } else {
205:                 $isRequired = (bool) $rules['required'];
206:                 unset($rules['required']);
207:             }
208:             $rules = array('required' => $isRequired) + $rules;
209: 
210:             // Start validation
211:             foreach ($rules as $rule => $params) {
212:                 // Prepare property options for validator
213:                 $props = $this->prepareProps($field, $rule);
214: 
215:                 // The current rule validation result
216:                 /* @var $validator Validator\BaseValidator */
217:                 $validator = null;
218:                 $result = $this->validateOne($rule, $data, $params, $validator, $props);
219: 
220:                 if (is_object($params)) {
221:                     $rule = get_class($params);
222:                 }
223: 
224:                 // Record the rule validators
225:                 $this->ruleValidators[$field][$rule] = $validator;
226: 
227:                 // If any rule is invalid, the result would always be false in the whole validation flow
228:                 if (false === $result) {
229:                     $this->result = false;
230:                 }
231: 
232:                 // Record the valid/invalid rule
233:                 $method = $result ? 'addValidRule' : 'addInvalidRule';
234:                 $this->$method($field, $rule);
235: 
236:                 // Trigger the ruleValid/ruleInvalid callback
237:                 $callback = $result ? 'ruleValid' : 'ruleInvalid';
238:                 if ($this->$callback && false === call_user_func($this->$callback, $rule, $field, $this, $this->wei)) {
239:                     return $this->result;
240:                 }
241: 
242:                 if ($result) {
243:                     // The field data is empty and optional, skip the remaining validation rules
244:                     /** @var $validator Validator\Required */
245:                     if ('required' === $rule && $validator->isInvalid($data)) {
246:                         break;
247:                     }
248:                 } else {
249:                     // Break the validation flow when any field's rule is invalid
250:                     if ('required' === $rule || $this->breakRule || $this->skip) {
251:                         break;
252:                     }
253:                 }
254:             }
255: 
256:             // Trigger the fieldValid/fieldInvalid callback
257:             $callback = $this->isFieldValid($field) ? 'fieldValid' : 'fieldInvalid';
258:             if ($this->$callback && false === call_user_func($this->$callback, $field, $this, $this->wei)) {
259:                 return $this->result;
260:             }
261: 
262:             if (!$this->result && $this->skip) {
263:                 continue;
264:             }
265: 
266:             // Break the validation flow when any field is invalid
267:             if (!$this->result && ($this->breakRule || $this->breakField)) {
268:                 break;
269:             }
270:         }
271: 
272:         // Trigger the success/failure callback
273:         $callback = $this->result ? 'success' : 'failure';
274:         $this->$callback && call_user_func($this->$callback, $this, $this->wei);
275: 
276:         return $this->result;
277:     }
278: 
279:     /**
280:      * Prepare name and messages property option for rule validator
281:      *
282:      * @param string $field
283:      * @param string $rule
284:      * @return array
285:      */
286:     protected function prepareProps($field, $rule)
287:     {
288:         $props = $messages = array();
289: 
290:         $props['validator'] = $this;
291: 
292:         // Prepare name for validator
293:         if (isset($this->names[$field])) {
294:             $props['name'] = $this->names[$field];
295:         }
296: 
297:         /**
298:          * Prepare messages for validator
299:          *
300:          * The messages array may look like below
301:          * array(
302:          *     // Case 1
303:          *     'field' => 'message',
304:          *     // Case 2
305:          *     'field2' => array(
306:          *         'rule' => 'message'
307:          *     ),
308:          *     // Case 2
309:          *     'field3' => array(
310:          *         'rule' => array(
311:          *            'option' => 'message',
312:          *            'option2' => 'message',
313:          *         )
314:          *     )
315:          * )
316:          *
317:          * In case 2, checking non-numeric offsets of strings would return true
318:          * in PHP 5.3, while return false in PHP 5.4, so we do NOT known
319:          * $messages is array or string
320:          * @link http://php.net/manual/en/function.isset.php
321:          *
322:          * In case 1, $messages is string
323:          */
324:         // Case 2
325:         if (isset($this->messages[$field][$rule]) && is_array($this->messages[$field])) {
326:             $messages = $this->messages[$field][$rule];
327:         // Case 1
328:         } elseif (isset($this->messages[$field]) && is_scalar($this->messages[$field])) {
329:             $messages = $this->messages[$field];
330:         }
331: 
332:         // Convert message to array for validator
333:         if (is_scalar($messages)) {
334:             $props['message'] =  $messages;
335:         } elseif (is_array($messages)) {
336:             foreach ($messages as $name => $message) {
337:                 $props[$name . 'Message'] = $message;
338:             }
339:         }
340: 
341:         return $props;
342:     }
343: 
344:     /**
345:      * Add valid rule
346:      *
347:      * @param string $field The field name
348:      * @param string $rule The rule name
349:      * @return $this
350:      */
351:     public function addValidRule($field, $rule)
352:     {
353:         $this->validRules[$field][] = $rule;
354:         return $this;
355:     }
356: 
357:     /**
358:      * Add invalid rule
359:      *
360:      * @param string $field The field name
361:      * @param string $rule The rule name
362:      * @return $this
363:      */
364:     public function addInvalidRule($field, $rule)
365:     {
366:         $this->invalidRules[$field][] = $rule;
367:         return $this;
368:     }
369: 
370:     /**
371:      * Returns the valid fields
372:      *
373:      * @return array
374:      */
375:     public function getValidFields()
376:     {
377:         return array_keys(array_diff_key($this->validRules, $this->invalidRules));
378:     }
379: 
380:     /**
381:      * Returns the invalid fields
382:      *
383:      * @return array
384:      */
385:     public function getInvalidFields()
386:     {
387:         return array_keys($this->invalidRules);
388:     }
389: 
390:     /**
391:      * Check if field is valid
392:      *
393:      * @param string $field
394:      * @return bool
395:      */
396:     public function isFieldValid($field)
397:     {
398:         return !in_array($field, $this->getInvalidFields());
399:     }
400: 
401:     /**
402:      * Check if field is invalid
403:      *
404:      * @param string $field
405:      * @return bool
406:      */
407:     public function isFieldInvalid($field)
408:     {
409:         return in_array($field, $this->getInvalidFields());
410:     }
411: 
412:     /**
413:      * Set field rules
414:      *
415:      * @param array $rules
416:      * @return $this
417:      */
418:     public function setRules(array $rules = null)
419:     {
420:         $this->rules = (array)$rules;
421:         return $this;
422:     }
423: 
424:     /**
425:      * Get validator rules
426:      *
427:      * @return array
428:      */
429:     public function getRules()
430:     {
431:         return $this->rules;
432:     }
433: 
434:     /**
435:      * Get validator rules by specified field
436:      *
437:      * @param string $field
438:      * @return array
439:      */
440:     public function getFieldRules($field)
441:     {
442:         return isset($this->rules[$field]) ? $this->rules[$field] : array();
443:     }
444: 
445:     /**
446:      * Get validation rule parameters
447:      *
448:      * @param string $field The validation field
449:      * @param string $rule The validation rule
450:      * @return array
451:      */
452:     public function getRuleParams($field, $rule)
453:     {
454:         return isset($this->rules[$field][$rule]) ? (array) $this->rules[$field][$rule] : array();
455:     }
456: 
457:     /**
458:      * Get valid rules by field
459:      *
460:      * @param string $field
461:      * @return array
462:      */
463:     public function getValidRules($field)
464:     {
465:         return isset($this->validRules[$field]) ? $this->validRules[$field] : array();
466:     }
467: 
468:     /**
469:      * Get invalid rules by field
470:      *
471:      * @param string $field
472:      * @return array
473:      */
474:     public function getInvalidRules($field = null)
475:     {
476:         return $field ?
477:             isset($this->invalidRules[$field]) ? $this->invalidRules[$field] : array()
478:             : $this->invalidRules;
479:     }
480: 
481:     /**
482:      * Returns the validation result
483:      *
484:      * @return bool
485:      */
486:     public function isValid()
487:     {
488:         return is_null($this->result) ? $this->__invoke() : $this->result;
489:     }
490: 
491:     /**
492:      * Adds rule for specified field
493:      *
494:      * @param string $field The name of field
495:      * @param string $rule The name of rule
496:      * @param mixed $parameters The parameters for rule
497:      */
498:     public function addRule($field, $rule, $parameters)
499:     {
500:         $this->rules[$field][$rule] = $parameters;
501:     }
502: 
503:     /**
504:      * Returns whether the validation rule exists in specified field
505:      *
506:      * @param string $field
507:      * @param string $rule
508:      * @return bool
509:      */
510:     public function hasRule($field, $rule)
511:     {
512:         return isset($this->rules[$field][$rule]);
513:     }
514: 
515:     /**
516:      * Removes the rule in field
517:      *
518:      * @param string $field The name of field
519:      * @param string $rule The name of rule
520:      * @return bool
521:      */
522:     public function removeRule($field, $rule)
523:     {
524:         if (isset($this->rules[$field][$rule])) {
525:             unset($this->rules[$field][$rule]);
526:             return true;
527:         }
528:         return false;
529:     }
530: 
531:     /**
532:      * Removes the validate field
533:      *
534:      * @param string $field
535:      * @return bool
536:      */
537:     public function removeField($field)
538:     {
539:         if (isset($this->rules[$field])) {
540:             unset($this->rules[$field]);
541:             return true;
542:         }
543:         return false;
544:     }
545: 
546:     /**
547:      * Sets data for validation
548:      *
549:      * @param array|object $data
550:      * @throws \InvalidArgumentException when argument type is not array or object
551:      * @return $this
552:      */
553:     public function setData($data)
554:     {
555:         if (!is_array($data) && !is_object($data)) {
556:             throw new \InvalidArgumentException(sprintf(
557:                 'Expected argument of type array or object, "%s" given',
558:                 is_object($data) ? get_class($data) : gettype($data)
559:             ));
560:         }
561:         $this->data = $data;
562:         return $this;
563:     }
564: 
565:     /**
566:      * Returns validation data
567:      *
568:      * @return array
569:      */
570:     public function getData()
571:     {
572:         return $this->data;
573:     }
574: 
575:     /**
576:      * Returns validation field data
577:      *
578:      * @param string $field The name of field
579:      * @return mixed
580:      */
581:     public function getFieldData($field)
582:     {
583:         // $this->data could only be array or object, which has been checked by $this->setData
584:         if ((is_array($this->data) && array_key_exists($field, $this->data))
585:             || ($this->data instanceof \ArrayAccess && $this->data->offsetExists($field))
586:         ) {
587:             return $this->data[$field];
588:         } elseif (isset($this->data->$field)) {
589:             return $this->data->$field;
590:         } elseif (method_exists($this->data, 'get' . $field)) {
591:             return $this->data->{'get' . $field}();
592:         } else {
593:             return null;
594:         }
595:     }
596: 
597:     /**
598:      * Sets data for validation field
599:      *
600:      * @param string $field The name of field
601:      * @param mixed $data The data of field
602:      * @return $this
603:      */
604:     public function setFieldData($field, $data)
605:     {
606:         if (is_array($this->data)) {
607:             $this->data[$field] = $data;
608:         } else {
609:             $this->data->$field = $data;
610:         }
611:         return $this;
612:     }
613: 
614:     /**
615:      * Set custom messages
616:      *
617:      * @param array $messages
618:      * @return $this
619:      */
620:     public function setMessages(array $messages = null)
621:     {
622:         $this->messages = (array)$messages;
623:         return $this;
624:     }
625: 
626:     /**
627:      * Returns custom message
628:      *
629:      * @return array
630:      */
631:     public function getMessages()
632:     {
633:         return $this->messages;
634:     }
635: 
636:     /**
637:      * Returns detail invalid messages
638:      *
639:      * @return array
640:      */
641:     public function getDetailMessages()
642:     {
643:         $messages = array();
644:         foreach ($this->invalidRules as $field => $rules) {
645:             foreach ($rules as $rule) {
646:                 $messages[$field][$rule] = $this->ruleValidators[$field][$rule]->getMessages();
647:             }
648:         }
649:         return $messages;
650:     }
651: 
652:     /**
653:      * Returns summary invalid messages
654:      *
655:      * @return array
656:      */
657:     public function getSummaryMessages()
658:     {
659:         $messages = $this->getDetailMessages();
660:         $summaries = array();
661:         foreach ($messages as $field => $rules) {
662:             foreach ($rules as $options) {
663:                 foreach ($options as $message) {
664:                     $summaries[$field][] = $message;
665:                 }
666:             }
667:         }
668:         return $summaries;
669:     }
670: 
671:     /**
672:      * Returns error message string connected by specified separator
673:      *
674:      * @param string $separator
675:      * @return string
676:      */
677:     public function getJoinedMessage($separator = "\n")
678:     {
679:         $messages = $this->getDetailMessages();
680:         $array = array();
681:         foreach ($messages as $rules) {
682:             foreach ($rules as $options) {
683:                 foreach ($options as $message) {
684:                     $array[] = $message;
685:                 }
686:             }
687:         }
688:         return implode($separator, array_unique($array));
689:     }
690: 
691:     /**
692:      * Returns the first error message string
693:      *
694:      * @return false|string
695:      */
696:     public function getFirstMessage()
697:     {
698:         if ($this->isValid()) {
699:             return false;
700:         }
701:         return current(current(current($this->getDetailMessages())));
702:     }
703: 
704:     /**
705:      * Returns the rule validator object
706:      *
707:      * @param string $field
708:      * @param string $rule
709:      * @return Validator\BaseValidator
710:      */
711:     public function getRuleValidator($field, $rule)
712:     {
713:         return isset($this->ruleValidators[$field][$rule]) ? $this->ruleValidators[$field][$rule] : null;
714:     }
715: 
716:     /**
717:      * Sets field names
718:      *
719:      * @param array $names
720:      */
721:     public function setNames($names)
722:     {
723:         $this->names = (array)$names;
724:     }
725: 
726:     /**
727:      * Returns field names
728:      *
729:      * @return array
730:      */
731:     public function getNames()
732:     {
733:         return $this->names;
734:     }
735: 
736:     /**
737:      * @param string|Validator\BaseValidator|int $rule
738:      * @param array|null $input
739:      * @param mixed $options
740:      * @param null &$validator
741:      * @param array $props
742:      * @return bool
743:      * @internal Do NOT use this method for it may be changed in the future
744:      */
745:     public function validateOne($rule, $input, $options = array(), &$validator = null, $props = array())
746:     {
747:         // Process simple array rules, eg 'username' => ['required', 'email']
748:         if (is_int($rule)) {
749:             $rule = $options;
750:             if (is_string($options)) {
751:                 $options = true;
752:             }
753:         }
754: 
755:         if ($rule instanceof Validator\BaseValidator) {
756:             $validator = $rule;
757:             return $rule($input);
758:         }
759: 
760:         $validator = $this->createRuleValidator($rule, $props);
761: 
762:         if (!is_array($options)) {
763:             $options = array($options);
764:         }
765: 
766:         if (is_int(key($options))) {
767:             array_unshift($options, $input);
768:             $result = call_user_func_array($validator, $options);
769:         } else {
770:             $validator->setOption($options);
771:             $result = $validator($input);
772:         }
773: 
774:         return $result;
775:     }
776: 
777:     /**
778:      * Create a rule validator instance by specified rule name
779:      *
780:      * @param string $rule The name of rule validator
781:      * @param array $options The property options for rule validator
782:      * @return Validator\BaseValidator
783:      * @throws \InvalidArgumentException When validator not found
784:      */
785:     public function createRuleValidator($rule, array $options = array())
786:     {
787:         // Starts with "not", such as notDigit, notEqual
788:         if (0 === stripos($rule, 'not')) {
789:             $options['negative'] = true;
790:             $rule = substr($rule, 3);
791:         }
792: 
793:         $object = 'is' . ucfirst($rule);
794:         $class = $this->wei->getClass($object);
795:         if (!$class || !class_exists($class)) {
796:             throw new \InvalidArgumentException(sprintf('Validator "%s" not found', $rule));
797:         }
798: 
799: 
800:         $options = $options + array('wei' => $this->wei) + (array)$this->wei->getConfig('is' . ucfirst($rule));
801: 
802:         return new $class($options);
803:     }
804: }
805: 
Wei Framework API documentation generated by ApiGen