feature #9140 [Validator][Email] - Strict validation and soft dependency (egulias)
This PR was squashed before being merged into the 2.5-dev branch (closes #9140).
Discussion
----------
[Validator][Email] - Strict validation and soft dependency
| Q | A
| ------------- | ---
| Bug fix? | no
| New feature? | yes
| BC breaks? | yes
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #1581, #4930
| License | MIT
| Doc PR | https://github.com/symfony/symfony-docs/pull/3469
TODO
---------
- [x] submit changes to the documentation
- [x] document the BC breaks
- [x] finish the code
- [x] gather feedback for my changes
In #1581 @bschussek suggested to pass the strict_email config to `Validator\EmailValidator::__construct($strict)`, I did not put it there yet since the constraint can receive that configuration each time the constraint is used despite the fact of the global configuration. This could lead to some logic in the constructor and I wanted first to integrate the strict validator.
BC Break
--------------
I'm not sure of this, but as a soft dependency is added and now some emails that where valid before no longer are I thought it is.
Commits
-------
3368630
#1581 - Strict in Email constraint and use of Egulias\EmailValidator
This commit is contained in:
commit
5a4885edb4
@ -44,3 +44,31 @@ Form
|
|||||||
public function getErrors($deep = false, $flatten = true)
|
public function getErrors($deep = false, $flatten = true)
|
||||||
{
|
{
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Validator
|
||||||
|
---------
|
||||||
|
|
||||||
|
* EmailValidator has changed to allow `non-strict` and `strict` email validation
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
Email validation was done with php's `filter_var()`
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
Default email validation is now done via a simple regex which may cause invalid emails (not RFC compilant) to be
|
||||||
|
valid. This is the default behaviour.
|
||||||
|
|
||||||
|
Strict email validation has to be explicitly activated in the configuration file by adding
|
||||||
|
```
|
||||||
|
framework_bundle:
|
||||||
|
//...
|
||||||
|
validation:
|
||||||
|
strict_email: true
|
||||||
|
//...
|
||||||
|
|
||||||
|
```
|
||||||
|
Also you have to add to your composer.json:
|
||||||
|
```
|
||||||
|
"egulias/email-validator": "1.1.*"
|
||||||
|
```
|
||||||
|
@ -73,7 +73,8 @@
|
|||||||
"monolog/monolog": "~1.3",
|
"monolog/monolog": "~1.3",
|
||||||
"propel/propel1": "1.6.*",
|
"propel/propel1": "1.6.*",
|
||||||
"ircmaxell/password-compat": "1.0.*",
|
"ircmaxell/password-compat": "1.0.*",
|
||||||
"ocramius/proxy-manager": ">=0.3.1,<0.6-dev"
|
"ocramius/proxy-manager": ">=0.3.1,<0.6-dev",
|
||||||
|
"egulias/email-validator": "1.1.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": { "Symfony\\": "src/" },
|
"psr-0": { "Symfony\\": "src/" },
|
||||||
|
@ -445,6 +445,7 @@ class Configuration implements ConfigurationInterface
|
|||||||
->scalarNode('cache')->end()
|
->scalarNode('cache')->end()
|
||||||
->booleanNode('enable_annotations')->defaultFalse()->end()
|
->booleanNode('enable_annotations')->defaultFalse()->end()
|
||||||
->scalarNode('translation_domain')->defaultValue('validators')->end()
|
->scalarNode('translation_domain')->defaultValue('validators')->end()
|
||||||
|
->booleanNode('strict_email')->defaultFalse()->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
@ -678,6 +678,9 @@ class FrameworkExtension extends Extension
|
|||||||
$container->setParameter('validator.mapping.loader.xml_files_loader.mapping_files', $this->getValidatorXmlMappingFiles($container));
|
$container->setParameter('validator.mapping.loader.xml_files_loader.mapping_files', $this->getValidatorXmlMappingFiles($container));
|
||||||
$container->setParameter('validator.mapping.loader.yaml_files_loader.mapping_files', $this->getValidatorYamlMappingFiles($container));
|
$container->setParameter('validator.mapping.loader.yaml_files_loader.mapping_files', $this->getValidatorYamlMappingFiles($container));
|
||||||
|
|
||||||
|
$definition = $container->findDefinition('validator.email');
|
||||||
|
$definition->replaceArgument(0, $config['strict_email']);
|
||||||
|
|
||||||
if (array_key_exists('enable_annotations', $config) && $config['enable_annotations']) {
|
if (array_key_exists('enable_annotations', $config) && $config['enable_annotations']) {
|
||||||
$loaderChain = $container->getDefinition('validator.mapping.loader.loader_chain');
|
$loaderChain = $container->getDefinition('validator.mapping.loader.loader_chain');
|
||||||
$arguments = $loaderChain->getArguments();
|
$arguments = $loaderChain->getArguments();
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
<parameter key="validator.mapping.loader.xml_files_loader.mapping_files" type="collection" />
|
<parameter key="validator.mapping.loader.xml_files_loader.mapping_files" type="collection" />
|
||||||
<parameter key="validator.mapping.loader.yaml_files_loader.mapping_files" type="collection" />
|
<parameter key="validator.mapping.loader.yaml_files_loader.mapping_files" type="collection" />
|
||||||
<parameter key="validator.expression.class">Symfony\Component\Validator\Constraints\ExpressionValidator</parameter>
|
<parameter key="validator.expression.class">Symfony\Component\Validator\Constraints\ExpressionValidator</parameter>
|
||||||
|
<parameter key="validator.email.class">Symfony\Component\Validator\Constraints\EmailValidator</parameter>
|
||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
@ -69,5 +70,10 @@
|
|||||||
<argument type="service" id="property_accessor" />
|
<argument type="service" id="property_accessor" />
|
||||||
<tag name="validator.constraint_validator" alias="validator.expression" />
|
<tag name="validator.constraint_validator" alias="validator.expression" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="validator.email" class="%validator.email.class%">
|
||||||
|
<argument></argument>
|
||||||
|
<tag name="validator.constraint_validator" alias="validator.email" />
|
||||||
|
</service>
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
@ -127,6 +127,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||||||
'enabled' => false,
|
'enabled' => false,
|
||||||
'enable_annotations' => false,
|
'enable_annotations' => false,
|
||||||
'translation_domain' => 'validators',
|
'translation_domain' => 'validators',
|
||||||
|
'strict_email' => false,
|
||||||
),
|
),
|
||||||
'annotations' => array(
|
'annotations' => array(
|
||||||
'cache' => 'file',
|
'cache' => 'file',
|
||||||
|
@ -25,4 +25,13 @@ class Email extends Constraint
|
|||||||
public $message = 'This value is not a valid email address.';
|
public $message = 'This value is not a valid email address.';
|
||||||
public $checkMX = false;
|
public $checkMX = false;
|
||||||
public $checkHost = false;
|
public $checkHost = false;
|
||||||
|
public $strict = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function validatedBy()
|
||||||
|
{
|
||||||
|
return 'validator.email';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Validator\Constraints;
|
|||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
use Symfony\Component\Validator\ConstraintValidator;
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||||
|
use Egulias\EmailValidator\EmailValidator as StrictEmailValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
@ -22,6 +23,18 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
|||||||
*/
|
*/
|
||||||
class EmailValidator extends ConstraintValidator
|
class EmailValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* isStrict
|
||||||
|
*
|
||||||
|
* @var Boolean
|
||||||
|
*/
|
||||||
|
private $isStrict;
|
||||||
|
|
||||||
|
public function __construct($strict = false)
|
||||||
|
{
|
||||||
|
$this->isStrict = $strict;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@ -40,12 +53,23 @@ class EmailValidator extends ConstraintValidator
|
|||||||
}
|
}
|
||||||
|
|
||||||
$value = (string) $value;
|
$value = (string) $value;
|
||||||
$valid = filter_var($value, FILTER_VALIDATE_EMAIL);
|
if (null === $constraint->strict) {
|
||||||
|
$constraint->strict = $this->isStrict;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($constraint->strict && class_exists('\Egulias\EmailValidator\EmailValidator')) {
|
||||||
|
$strictValidator = new StrictEmailValidator();
|
||||||
|
$valid = $strictValidator->isValid($value, false);
|
||||||
|
} elseif ($constraint->strict === true) {
|
||||||
|
throw new \RuntimeException('Strict email validation requires egulias/email-validator');
|
||||||
|
} else {
|
||||||
|
$valid = preg_match('/.+\@.+\..+/', $value);
|
||||||
|
}
|
||||||
|
|
||||||
if ($valid) {
|
if ($valid) {
|
||||||
$host = substr($value, strpos($value, '@') + 1);
|
$host = substr($value, strpos($value, '@') + 1);
|
||||||
|
|
||||||
// Check for host DNS resource records
|
// Check for host DNS resource records
|
||||||
|
|
||||||
if ($valid && $constraint->checkMX) {
|
if ($valid && $constraint->checkMX) {
|
||||||
$valid = $this->checkMX($host);
|
$valid = $this->checkMX($host);
|
||||||
} elseif ($valid && $constraint->checkHost) {
|
} elseif ($valid && $constraint->checkHost) {
|
||||||
|
@ -22,7 +22,7 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
$this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false);
|
$this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false);
|
||||||
$this->validator = new EmailValidator();
|
$this->validator = new EmailValidator(false);
|
||||||
$this->validator->initialize($this->context);
|
$this->validator->initialize($this->context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +100,14 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
array('example'),
|
array('example'),
|
||||||
array('example@'),
|
array('example@'),
|
||||||
array('example@localhost'),
|
array('example@localhost'),
|
||||||
array('example@example.com@example.com'),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testStrict()
|
||||||
|
{
|
||||||
|
$this->context->expects($this->never())
|
||||||
|
->method('addViolation');
|
||||||
|
|
||||||
|
$this->validator->validate('example@localhost', new Email(array('strict' => true)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@
|
|||||||
"symfony/yaml": "~2.0",
|
"symfony/yaml": "~2.0",
|
||||||
"symfony/config": "~2.2",
|
"symfony/config": "~2.2",
|
||||||
"doctrine/annotations": "~1.0",
|
"doctrine/annotations": "~1.0",
|
||||||
"doctrine/cache": "~1.0"
|
"doctrine/cache": "~1.0",
|
||||||
|
"egulias/email-validator": "~1.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",
|
"doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",
|
||||||
@ -34,7 +35,8 @@
|
|||||||
"symfony/http-foundation": "",
|
"symfony/http-foundation": "",
|
||||||
"symfony/intl": "",
|
"symfony/intl": "",
|
||||||
"symfony/yaml": "",
|
"symfony/yaml": "",
|
||||||
"symfony/config": ""
|
"symfony/config": "",
|
||||||
|
"egulias/email-validator": "Strict (RFC compliant) email validation"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": { "Symfony\\Component\\Validator\\": "" }
|
"psr-0": { "Symfony\\Component\\Validator\\": "" }
|
||||||
|
Reference in New Issue
Block a user