EmailValidator.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Validator\Constraints;
  11. use Egulias\EmailValidator\EmailValidator as EguliasEmailValidator;
  12. use Egulias\EmailValidator\Validation\EmailValidation;
  13. use Egulias\EmailValidator\Validation\NoRFCWarningsValidation;
  14. use Symfony\Component\Validator\Constraint;
  15. use Symfony\Component\Validator\ConstraintValidator;
  16. use Symfony\Component\Validator\Exception\UnexpectedTypeException;
  17. use Symfony\Component\Validator\Exception\UnexpectedValueException;
  18. /**
  19. * @author Bernhard Schussek <bschussek@gmail.com>
  20. */
  21. class EmailValidator extends ConstraintValidator
  22. {
  23. private const PATTERN_HTML5 = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/';
  24. private const PATTERN_LOOSE = '/^.+\@\S+\.\S+$/';
  25. private const EMAIL_PATTERNS = [
  26. Email::VALIDATION_MODE_LOOSE => self::PATTERN_LOOSE,
  27. Email::VALIDATION_MODE_HTML5 => self::PATTERN_HTML5,
  28. ];
  29. private $defaultMode;
  30. public function __construct(string $defaultMode = Email::VALIDATION_MODE_LOOSE)
  31. {
  32. if (!\in_array($defaultMode, Email::$validationModes, true)) {
  33. throw new \InvalidArgumentException('The "defaultMode" parameter value is not valid.');
  34. }
  35. $this->defaultMode = $defaultMode;
  36. }
  37. /**
  38. * {@inheritdoc}
  39. */
  40. public function validate($value, Constraint $constraint)
  41. {
  42. if (!$constraint instanceof Email) {
  43. throw new UnexpectedTypeException($constraint, Email::class);
  44. }
  45. if (null === $value || '' === $value) {
  46. return;
  47. }
  48. if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
  49. throw new UnexpectedValueException($value, 'string');
  50. }
  51. $value = (string) $value;
  52. if ('' === $value) {
  53. return;
  54. }
  55. if (null !== $constraint->normalizer) {
  56. $value = ($constraint->normalizer)($value);
  57. }
  58. if (null === $constraint->mode) {
  59. $constraint->mode = $this->defaultMode;
  60. }
  61. if (!\in_array($constraint->mode, Email::$validationModes, true)) {
  62. throw new \InvalidArgumentException(sprintf('The "%s::$mode" parameter value is not valid.', get_debug_type($constraint)));
  63. }
  64. if (Email::VALIDATION_MODE_STRICT === $constraint->mode) {
  65. $strictValidator = new EguliasEmailValidator();
  66. if (interface_exists(EmailValidation::class) && !$strictValidator->isValid($value, new NoRFCWarningsValidation())) {
  67. $this->context->buildViolation($constraint->message)
  68. ->setParameter('{{ value }}', $this->formatValue($value))
  69. ->setCode(Email::INVALID_FORMAT_ERROR)
  70. ->addViolation();
  71. return;
  72. } elseif (!interface_exists(EmailValidation::class) && !$strictValidator->isValid($value, false, true)) {
  73. $this->context->buildViolation($constraint->message)
  74. ->setParameter('{{ value }}', $this->formatValue($value))
  75. ->setCode(Email::INVALID_FORMAT_ERROR)
  76. ->addViolation();
  77. return;
  78. }
  79. } elseif (!preg_match(self::EMAIL_PATTERNS[$constraint->mode], $value)) {
  80. $this->context->buildViolation($constraint->message)
  81. ->setParameter('{{ value }}', $this->formatValue($value))
  82. ->setCode(Email::INVALID_FORMAT_ERROR)
  83. ->addViolation();
  84. return;
  85. }
  86. }
  87. }