From f81a9afb8fb41b06bc0e450d031209bd90f32eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lavoie?= Date: Fri, 8 Jun 2012 00:32:21 -0400 Subject: [PATCH 1/4] [Form][Validator] Fixed generation of HTML5 pattern attribute based on Assert\Regex to remove delimiters. --- .../Validator/ValidatorTypeGuesser.php | 2 +- .../Component/Form/Tests/FormFactoryTest.php | 6 +++--- .../Component/Validator/Constraints/Regex.php | 16 ++++++++++++++++ .../Tests/Constraints/RegexValidatorTest.php | 17 +++++++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php index d76710fba89cc..b14d73a4a6adc 100755 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php @@ -261,7 +261,7 @@ public function guessPatternForConstraint(Constraint $constraint) return new ValueGuess(sprintf('.{%s,%s}', (string) $constraint->min, (string) $constraint->max), Guess::LOW_CONFIDENCE); case 'Symfony\Component\Validator\Constraints\Regex': - return new ValueGuess($constraint->pattern, Guess::HIGH_CONFIDENCE ); + return new ValueGuess($constraint->getNonDelimitedPattern(), Guess::HIGH_CONFIDENCE ); case 'Symfony\Component\Validator\Constraints\Min': return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->limit)), Guess::LOW_CONFIDENCE); diff --git a/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/src/Symfony/Component/Form/Tests/FormFactoryTest.php index b3dd506495771..189cdeec8cb32 100644 --- a/src/Symfony/Component/Form/Tests/FormFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -528,7 +528,7 @@ public function testCreateBuilderUsesPatternIfFound() ->method('guessPattern') ->with('Application\Author', 'firstName') ->will($this->returnValue(new ValueGuess( - '/[a-z]/', + '[a-z]', Guess::MEDIUM_CONFIDENCE ))); @@ -536,7 +536,7 @@ public function testCreateBuilderUsesPatternIfFound() ->method('guessPattern') ->with('Application\Author', 'firstName') ->will($this->returnValue(new ValueGuess( - '/[a-zA-Z]/', + '[a-zA-Z]', Guess::HIGH_CONFIDENCE ))); @@ -544,7 +544,7 @@ public function testCreateBuilderUsesPatternIfFound() $factory->expects($this->once()) ->method('createNamedBuilder') - ->with('firstName', 'text', null, array('pattern' => '/[a-zA-Z]/')) + ->with('firstName', 'text', null, array('pattern' => '[a-zA-Z]')) ->will($this->returnValue('builderInstance')); $builder = $factory->createBuilderForProperty( diff --git a/src/Symfony/Component/Validator/Constraints/Regex.php b/src/Symfony/Component/Validator/Constraints/Regex.php index 73f8b7b3323df..94a716e80469e 100644 --- a/src/Symfony/Component/Validator/Constraints/Regex.php +++ b/src/Symfony/Component/Validator/Constraints/Regex.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** * @Annotation @@ -39,4 +40,19 @@ public function getRequiredOptions() { return array('pattern'); } + + /** + * Sometimes, like when converting to HTML5 pattern attribute, the regex is needed without the delimiters + * Example: /[a-z]+/i would be converted to [a-z]+ + * However, if options are specified, it cannot be converted and this will throw an Exception + * @return string regex + * @throws Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ + public function getNonDelimitedPattern() { + if (preg_match('/^(.)(.*)\1$/', $this->pattern, $matches)) { + return $matches[2]; + } else { + throw new ConstraintDefinitionException("Cannot remove delimiters from pattern '{$this->pattern}'."); + } + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php index 755f488577851..6ce21e9c9ae63 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php @@ -113,4 +113,21 @@ public function testConstraintGetDefaultOption() $this->assertEquals('pattern', $constraint->getDefaultOption()); } + + public function testNonDelimitedPattern() { + $constraint = new Regex(array( + 'pattern' => '/^[0-9]+$/', + )); + + $this->assertEquals('^[0-9]+$', $constraint->getNonDelimitedPattern()); + } + + public function testNonDelimitedPatternError() { + $constraint = new Regex(array( + 'pattern' => '/^[0-9]+$/i', + )); + + $this->setExpectedException('Symfony\Component\Validator\Exception\ConstraintDefinitionException'); + $constraint->getNonDelimitedPattern(); + } } From d29ac234d6125137f416871b91010578baacd574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lavoie?= Date: Fri, 8 Jun 2012 00:41:54 -0400 Subject: [PATCH 2/4] [Form][Validator] Fixed documentation of commit f81a9af --- src/Symfony/Component/Validator/Constraints/Regex.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Constraints/Regex.php b/src/Symfony/Component/Validator/Constraints/Regex.php index 94a716e80469e..91395d83ee949 100644 --- a/src/Symfony/Component/Validator/Constraints/Regex.php +++ b/src/Symfony/Component/Validator/Constraints/Regex.php @@ -43,7 +43,7 @@ public function getRequiredOptions() /** * Sometimes, like when converting to HTML5 pattern attribute, the regex is needed without the delimiters - * Example: /[a-z]+/i would be converted to [a-z]+ + * Example: /[a-z]+/ would be converted to [a-z]+ * However, if options are specified, it cannot be converted and this will throw an Exception * @return string regex * @throws Symfony\Component\Validator\Exception\ConstraintDefinitionException From d2b28855e5eb3a1d45eb33acb60403517d229323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lavoie?= Date: Fri, 8 Jun 2012 10:25:11 -0400 Subject: [PATCH 3/4] [Validator] Added delimiter escaping to Validator\Constraints\Regex::getNonDelimitedPattern --- .../Component/Validator/Constraints/Regex.php | 5 ++++- .../Tests/Constraints/RegexValidatorTest.php | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Constraints/Regex.php b/src/Symfony/Component/Validator/Constraints/Regex.php index 91395d83ee949..948731ea895e1 100644 --- a/src/Symfony/Component/Validator/Constraints/Regex.php +++ b/src/Symfony/Component/Validator/Constraints/Regex.php @@ -50,7 +50,10 @@ public function getRequiredOptions() */ public function getNonDelimitedPattern() { if (preg_match('/^(.)(.*)\1$/', $this->pattern, $matches)) { - return $matches[2]; + $delimiter = $matches[1]; + // Unescape the delimiter in pattern + $pattern = str_replace('\\' . $delimiter, $delimiter, $matches[2]); + return $pattern; } else { throw new ConstraintDefinitionException("Cannot remove delimiters from pattern '{$this->pattern}'."); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php index 6ce21e9c9ae63..044efb0c855c5 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php @@ -122,6 +122,20 @@ public function testNonDelimitedPattern() { $this->assertEquals('^[0-9]+$', $constraint->getNonDelimitedPattern()); } + public function testNonDelimitedPatternEscaping() { + $constraint = new Regex(array( + 'pattern' => '/^[0-9]+\/$/', + )); + + $this->assertEquals('^[0-9]+/$', $constraint->getNonDelimitedPattern()); + + $constraint = new Regex(array( + 'pattern' => '#^[0-9]+\#$#', + )); + + $this->assertEquals('^[0-9]+#$', $constraint->getNonDelimitedPattern()); + } + public function testNonDelimitedPatternError() { $constraint = new Regex(array( 'pattern' => '/^[0-9]+$/i', From 107751934435d7386aad7118c014b991cf61f205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lavoie?= Date: Fri, 8 Jun 2012 10:55:51 -0400 Subject: [PATCH 4/4] [Form][Validator] Added html_pattern option for Regex Validation. --- .../Validator/ValidatorTypeGuesser.php | 5 +++- .../Component/Validator/Constraints/Regex.php | 17 +++++++++++ .../Tests/Constraints/RegexValidatorTest.php | 29 +++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php index b14d73a4a6adc..28a9e234c8121 100755 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php @@ -261,7 +261,10 @@ public function guessPatternForConstraint(Constraint $constraint) return new ValueGuess(sprintf('.{%s,%s}', (string) $constraint->min, (string) $constraint->max), Guess::LOW_CONFIDENCE); case 'Symfony\Component\Validator\Constraints\Regex': - return new ValueGuess($constraint->getNonDelimitedPattern(), Guess::HIGH_CONFIDENCE ); + $html_pattern = $constraint->getHtmlPattern(); + return $html_pattern + ? new ValueGuess($html_pattern, Guess::HIGH_CONFIDENCE) + : null; case 'Symfony\Component\Validator\Constraints\Min': return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->limit)), Guess::LOW_CONFIDENCE); diff --git a/src/Symfony/Component/Validator/Constraints/Regex.php b/src/Symfony/Component/Validator/Constraints/Regex.php index 948731ea895e1..de05ba712ae24 100644 --- a/src/Symfony/Component/Validator/Constraints/Regex.php +++ b/src/Symfony/Component/Validator/Constraints/Regex.php @@ -23,6 +23,7 @@ class Regex extends Constraint { public $message = 'This value is not valid.'; public $pattern; + public $html_pattern = null; public $match = true; /** @@ -58,4 +59,20 @@ public function getNonDelimitedPattern() { throw new ConstraintDefinitionException("Cannot remove delimiters from pattern '{$this->pattern}'."); } } + + public function getHtmlPattern() { + // If html_pattern is specified, use it + if (!is_null($this->html_pattern)) { + return empty($this->html_pattern) + ? false + : $this->html_pattern; + } else { + try { + return $this->getNonDelimitedPattern(); + } catch (ConstraintDefinitionException $e) { + // Pattern cannot be converted + return false; + } + } + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php index 044efb0c855c5..82e743c3c2c69 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php @@ -144,4 +144,33 @@ public function testNonDelimitedPatternError() { $this->setExpectedException('Symfony\Component\Validator\Exception\ConstraintDefinitionException'); $constraint->getNonDelimitedPattern(); } + + public function testHtmlPattern() { + // Specified html_pattern + $constraint = new Regex(array( + 'pattern' => '/^[a-z]+$/i', + 'html_pattern' => '^[a-zA-Z]+$', + )); + $this->assertEquals('^[a-zA-Z]+$', $constraint->getHtmlPattern()); + + // Disabled html_pattern + $constraint = new Regex(array( + 'pattern' => '/^[a-z]+$/i', + 'html_pattern' => false, + )); + $this->assertFalse($constraint->getHtmlPattern()); + + // Cannot be converted + $constraint = new Regex(array( + 'pattern' => '/^[a-z]+$/i', + )); + $this->assertFalse($constraint->getHtmlPattern()); + + // Automaticaly converted + $constraint = new Regex(array( + 'pattern' => '/^[a-z]+$/', + )); + $this->assertEquals('^[a-z]+$', $constraint->getHtmlPattern()); + } + }