Skip to content

[Form][Validator] Fixed generation of HTML5 pattern attribute based on Assert\Regex by removing delimiters or using a new option: html_pattern. #4520

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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->pattern, 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);
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Component/Form/Tests/FormFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -528,23 +528,23 @@ public function testCreateBuilderUsesPatternIfFound()
->method('guessPattern')
->with('Application\Author', 'firstName')
->will($this->returnValue(new ValueGuess(
'/[a-z]/',
'[a-z]',
Guess::MEDIUM_CONFIDENCE
)));

$this->guesser2->expects($this->once())
->method('guessPattern')
->with('Application\Author', 'firstName')
->will($this->returnValue(new ValueGuess(
'/[a-zA-Z]/',
'[a-zA-Z]',
Guess::HIGH_CONFIDENCE
)));

$factory = $this->createMockFactory(array('createNamedBuilder'));

$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(
Expand Down
36 changes: 36 additions & 0 deletions src/Symfony/Component/Validator/Constraints/Regex.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;

/**
* @Annotation
Expand All @@ -22,6 +23,7 @@ class Regex extends Constraint
{
public $message = 'This value is not valid.';
public $pattern;
public $html_pattern = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be $htmlPattern.

public $match = true;

/**
Expand All @@ -39,4 +41,38 @@ public function getRequiredOptions()
{
return array('pattern');
}

/**
* Sometimes, like when converting to HTML5 pattern attribute, the regex is needed without the delimiters
* 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
*/
public function getNonDelimitedPattern() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brace in new line. Same below.

if (preg_match('/^(.)(.*)\1$/', $this->pattern, $matches)) {
$delimiter = $matches[1];
// Unescape the delimiter in pattern
$pattern = str_replace('\\' . $delimiter, $delimiter, $matches[2]);
return $pattern;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing new line before return, also no need to use additional ($pattern) variable above.

} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for else as you have return above.

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)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null !== $this->htmlPattern

return empty($this->html_pattern)
? false
: $this->html_pattern;
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again no need for else here.

try {
return $this->getNonDelimitedPattern();
} catch (ConstraintDefinitionException $e) {
// Pattern cannot be converted
return false;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,64 @@ public function testConstraintGetDefaultOption()

$this->assertEquals('pattern', $constraint->getDefaultOption());
}

public function testNonDelimitedPattern() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the curly brace should be on its own line for all methods

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually fixed in PR #4522, my bad for creating multiple PR, I just found out how to add commits to existing PR.

$constraint = new Regex(array(
'pattern' => '/^[0-9]+$/',
));

$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',
));

$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());
}

}