验证(Validation)

Phalcon\Validation 对Phalcon来说是一个相对独立的组件,它可以对任意的数据进行验证。 当然也可以用来对非模型内的数据进行验证。

下面的例子展示了一些基本的使用方法:

  1. <?php
  2. use Phalcon\Validation;
  3. use Phalcon\Validation\Validator\Email;
  4. use Phalcon\Validation\Validator\PresenceOf;
  5. $validation = new Validation();
  6. $validation->add(
  7. "name",
  8. new PresenceOf(
  9. [
  10. "message" => "The name is required",
  11. ]
  12. )
  13. );
  14. $validation->add(
  15. "email",
  16. new PresenceOf(
  17. [
  18. "message" => "The e-mail is required",
  19. ]
  20. )
  21. );
  22. $validation->add(
  23. "email",
  24. new Email(
  25. [
  26. "message" => "The e-mail is not valid",
  27. ]
  28. )
  29. );
  30. $messages = $validation->validate($_POST);
  31. if (count($messages)) {
  32. foreach ($messages as $message) {
  33. echo $message, "<br>";
  34. }
  35. }

由于此模型是松耦合设计的,故此我们也可以使用自己书写的验证工具:

初始化验证(Initializing Validation)

我们可以直接在 Phalcon\Validation 初始化时添加验证链。我们可以把验证器放在一个单独的文件中以提高代码的重用率及可组织性:

  1. <?php
  2. use Phalcon\Validation;
  3. use Phalcon\Validation\Validator\Email;
  4. use Phalcon\Validation\Validator\PresenceOf;
  5. class MyValidation extends Validation
  6. {
  7. public function initialize()
  8. {
  9. $this->add(
  10. "name",
  11. new PresenceOf(
  12. [
  13. "message" => "The name is required",
  14. ]
  15. )
  16. );
  17. $this->add(
  18. "email",
  19. new PresenceOf(
  20. [
  21. "message" => "The e-mail is required",
  22. ]
  23. )
  24. );
  25. $this->add(
  26. "email",
  27. new Email(
  28. [
  29. "message" => "The e-mail is not valid",
  30. ]
  31. )
  32. );
  33. }
  34. }

Then initialize and use your own validator:

  1. <?php
  2. $validation = new MyValidation();
  3. $messages = $validation->validate($_POST);
  4. if (count($messages)) {
  5. foreach ($messages as $message) {
  6. echo $message, "<br>";
  7. }
  8. }

验证器(Validators)

Phalcon的验证组件中内置了一些验证器:

下面的例子中展示了如何创建自定义的验证器:

  1. <?php
  2. use Phalcon\Validation;
  3. use Phalcon\Validation\Message;
  4. use Phalcon\Validation\Validator;
  5. class IpValidator extends Validator
  6. {
  7. /**
  8. * 执行验证
  9. *
  10. * @param Phalcon\Validation $validator
  11. * @param string $attribute
  12. * @return boolean
  13. */
  14. public function validate(Validation $validator, $attribute)
  15. {
  16. $value = $validator->getValue($attribute);
  17. if (!filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) {
  18. $message = $this->getOption("message");
  19. if (!$message) {
  20. $message = "The IP is not valid";
  21. }
  22. $validator->appendMessage(
  23. new Message($message, $attribute, "Ip")
  24. );
  25. return false;
  26. }
  27. return true;
  28. }
  29. }

最重要的一点即是难证器要返回一个布尔值以标识验证是否成功:

Callback Validator

By using Phalcon\Validation\Validator\Callback you can execute custom function which must return boolean or new validator class which will be used to validate the same field. By returning true validation will be successful, returning false will mean validation failed. When executing this validator Phalcon will pass data depending what it is - if it’s an entity then entity will be passed, otherwise data. There is example:

  1. <?php
  2. use \Phalcon\Validation;
  3. use \Phalcon\Validation\Validator\Callback;
  4. use \Phalcon\Validation\Validator\PresenceOf;
  5. $validation = new Validation();
  6. $validation->add(
  7. "amount",
  8. new Callback(
  9. [
  10. "callback" => function($data) {
  11. return $data["amount"] % 2 == 0;
  12. },
  13. "message" => "Only even number of products are accepted"
  14. ]
  15. )
  16. );
  17. $validation->add(
  18. "amount",
  19. new Callback(
  20. [
  21. "callback" => function($data) {
  22. if($data["amount"] % 2 == 0) {
  23. return $data["amount"] != 2;
  24. }
  25. return true;
  26. },
  27. "message" => "You can't buy 2 products"
  28. ]
  29. )
  30. );
  31. $validation->add(
  32. "description",
  33. new Callback(
  34. [
  35. "callback" => function($data) {
  36. if($data["amount"] >= 10) {
  37. return new PresenceOf(
  38. [
  39. "message" => "You must write why you need so big amount."
  40. ]
  41. );
  42. }
  43. return true;
  44. }
  45. ]
  46. )
  47. );
  48. $messages = $validation->validate(["amount" => 1]); // will return message from first validator
  49. $messages = $validation->validate(["amount" => 2]); // will return message from second validator
  50. $messages = $validation->validate(["amount" => 10]); // will return message from validator returned by third validator

验证信息(Validation Messages)

Phalcon\Validation 内置了一个消息子系统,这提供了一个非常好的验证消息回传机制,以便在验证结束后取得验证信息,比如失败原因等。

每个消息由一个 Phalcon\Validation\Message 类的实例构成。 验证过程产生的消息可以使用:code:`getMessages()`方法取得。 每条消息都有一些扩展的信息组成比如产生错误的属性或消息的类型等:

  1. <?php
  2. $messages = $validation->validate();
  3. if (count($messages)) {
  4. foreach ($messages as $message) {
  5. echo "Message: ", $message->getMessage(), "\n";
  6. echo "Field: ", $message->getField(), "\n";
  7. echo "Type: ", $message->getType(), "\n";
  8. }
  9. }

我们也可以传送一个message参数以覆盖验证器中默认的信息:

  1. <?php
  2. use Phalcon\Validation\Validator\Email;
  3. $validation->add(
  4. "email",
  5. new Email(
  6. [
  7. "message" => "The e-mail is not valid",
  8. ]
  9. )
  10. );

默认,:code:`getMessages()`方法会返回在验证过程中所产生的信息。 我们可以使用:code:`filter()`方法来过滤我们感兴趣的消息:

  1. <?php
  2. $messages = $validation->validate();
  3. if (count($messages)) {
  4. // Filter only the messages generated for the field 'name'
  5. $filteredMessages = $messages->filter("name");
  6. foreach ($filteredMessages as $message) {
  7. echo $message;
  8. }
  9. }

过滤数据(Filtering of Data)

我们可以在数据被验证之前对其先进行过滤,以确保那些恶意的或不正确的数据不被验证。

  1. <?php
  2. use Phalcon\Validation;
  3. $validation = new Validation();
  4. $validation->add(
  5. "name",
  6. new PresenceOf(
  7. [
  8. "message" => "The name is required",
  9. ]
  10. )
  11. );
  12. $validation->add(
  13. "email",
  14. new PresenceOf(
  15. [
  16. "message" => "The email is required",
  17. ]
  18. )
  19. );
  20. // Filter any extra space
  21. $validation->setFilters("name", "trim");
  22. $validation->setFilters("email", "trim");

这里我们使用 filter 组件进行过滤。 我们还可以使用自定义的或内置的过滤器。

验证事件(Validation Events)

当在类中执行验证时, 我们可以在:code:`beforeValidation()`或:code:`afterValidation()`方法(事件)中执行额外的检查,过滤,清理等工作。 如果:code:`beforeValidation()`方法返回了false 则验证会被中止:

  1. <?php
  2. use Phalcon\Validation;
  3. class LoginValidation extends Validation
  4. {
  5. public function initialize()
  6. {
  7. // ...
  8. }
  9. /**
  10. * 验证执行之前执行
  11. *
  12. * @param array $data
  13. * @param object $entity
  14. * @param Phalcon\Validation\Message\Group $messages
  15. * @return bool
  16. */
  17. public function beforeValidation($data, $entity, $messages)
  18. {
  19. if ($this->request->getHttpHost() !== "admin.mydomain.com") {
  20. $messages->appendMessage(
  21. new Message("Only users can log on in the administration domain")
  22. );
  23. return false;
  24. }
  25. return true;
  26. }
  27. /**
  28. * 验证之后执行
  29. *
  30. * @param array $data
  31. * @param object $entity
  32. * @param Phalcon\Validation\Message\Group $messages
  33. */
  34. public function afterValidation($data, $entity, $messages)
  35. {
  36. // ... Add additional messages or perform more validations
  37. }
  38. }

取消验证(Cancelling Validations)

默认所有的验证器都会被执行,不管验证成功与否。 我们可以通过设置 cancelOnFail 参数为 true 来指定某个验证器验证失败时中止以后的所有验证:

  1. <?php
  2. use Phalcon\Validation;
  3. use Phalcon\Validation\Validator\Regex;
  4. use Phalcon\Validation\Validator\PresenceOf;
  5. $validation = new Validation();
  6. $validation->add(
  7. "telephone",
  8. new PresenceOf(
  9. [
  10. "message" => "The telephone is required",
  11. "cancelOnFail" => true,
  12. ]
  13. )
  14. );
  15. $validation->add(
  16. "telephone",
  17. new Regex(
  18. [
  19. "message" => "The telephone is required",
  20. "pattern" => "/\+44 [0-9]+/",
  21. ]
  22. )
  23. );
  24. $validation->add(
  25. "telephone",
  26. new StringLength(
  27. [
  28. "messageMinimum" => "The telephone is too short",
  29. "min" => 2,
  30. ]
  31. )
  32. );

第一个验证器中 cancelOnFail 参数设置为 true 则表示如果此验证器验证失败则验证链中接下的验证不会被执行。

我们可以在自定义的验证器中设置 cancelOnFail 为 true 来停止验证链:

  1. <?php
  2. use Phalcon\Validation;
  3. use Phalcon\Validation\Message;
  4. use Phalcon\Validation\Validator;
  5. class MyValidator extends Validator
  6. {
  7. /**
  8. * 执行验证
  9. *
  10. * @param Phalcon\Validation $validator
  11. * @param string $attribute
  12. * @return boolean
  13. */
  14. public function validate(Validation $validator, $attribute)
  15. {
  16. // If the attribute value is name we must stop the chain
  17. if ($attribute === "name") {
  18. $validator->setOption("cancelOnFail", true);
  19. }
  20. // ...
  21. }
  22. }

避免验证空值(Avoid validate empty values)

我们可以向所有内建的验证器传入选项 ‘allowEmpty’ 以避免在传入的值为空时执行验证。

  1. <?php
  2. use Phalcon\Validation;
  3. use Phalcon\Validation\Validator\Regex;
  4. $validation = new Validation();
  5. $validation->add(
  6. "telephone",
  7. new Regex(
  8. [
  9. "message" => "The telephone is required",
  10. "pattern" => "/\+44 [0-9]+/",
  11. "allowEmpty" => true,
  12. ]
  13. )
  14. );

递归验证(Recursive Validation)

我们可以通过 afterValidation() 方法,在一个验证器中运行另一个验证器。在本例中,CompanyValidation 验证实例会同时执行 PhoneValidation 验证器。

  1. <?php
  2. use Phalcon\Validation;
  3. class CompanyValidation extends Validation
  4. {
  5. /**
  6. * @var PhoneValidation
  7. */
  8. protected $phoneValidation;
  9. public function initialize()
  10. {
  11. $this->phoneValidation = new PhoneValidation();
  12. }
  13. public function afterValidation($data, $entity, $messages)
  14. {
  15. $phoneValidationMessages = $this->phoneValidation->validate(
  16. $data["phone"]
  17. );
  18. $messages->appendMessages(
  19. $phoneValidationMessages
  20. );
  21. }
  22. }