Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature #30900 [Validator] add new
Timezone
validation constraint (…
…phansys) This PR was merged into the 4.3-dev branch. Discussion ---------- [Validator] add new `Timezone` validation constraint | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | License | MIT | Doc PR | symfony/symfony-docs#11317 Rework of #22262. Commits ------- 536e53f [Validator] add new `Timezone` validation constraint.
- Loading branch information
Showing
9 changed files
with
497 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Validator\Constraints; | ||
|
||
use Symfony\Component\Validator\Constraint; | ||
use Symfony\Component\Validator\Exception\ConstraintDefinitionException; | ||
|
||
/** | ||
* @Annotation | ||
* @Target({"PROPERTY", "METHOD", "ANNOTATION"}) | ||
* | ||
* @author Javier Spagnoletti <phansys@gmail.com> | ||
* @author Hugo Hamon <hugohamon@neuf.fr> | ||
*/ | ||
class Timezone extends Constraint | ||
{ | ||
public const TIMEZONE_IDENTIFIER_ERROR = '5ce113e6-5e64-4ea2-90fe-d2233956db13'; | ||
public const TIMEZONE_IDENTIFIER_IN_ZONE_ERROR = 'b57767b1-36c0-40ac-a3d7-629420c775b8'; | ||
public const TIMEZONE_IDENTIFIER_IN_COUNTRY_ERROR = 'c4a22222-dc92-4fc0-abb0-d95b268c7d0b'; | ||
|
||
public $zone = \DateTimeZone::ALL; | ||
public $countryCode; | ||
public $message = 'This value is not a valid timezone.'; | ||
|
||
protected static $errorNames = [ | ||
self::TIMEZONE_IDENTIFIER_ERROR => 'TIMEZONE_IDENTIFIER_ERROR', | ||
self::TIMEZONE_IDENTIFIER_IN_ZONE_ERROR => 'TIMEZONE_IDENTIFIER_IN_ZONE_ERROR', | ||
self::TIMEZONE_IDENTIFIER_IN_COUNTRY_ERROR => 'TIMEZONE_IDENTIFIER_IN_COUNTRY_ERROR', | ||
]; | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function __construct(array $options = null) | ||
{ | ||
parent::__construct($options); | ||
|
||
if ($this->countryCode && \DateTimeZone::PER_COUNTRY !== $this->zone) { | ||
throw new ConstraintDefinitionException('The option "countryCode" can only be used when "zone" option is configured with `\DateTimeZone::PER_COUNTRY`.'); | ||
} | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
src/Symfony/Component/Validator/Constraints/TimezoneValidator.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Validator\Constraints; | ||
|
||
use Symfony\Component\Validator\Constraint; | ||
use Symfony\Component\Validator\ConstraintValidator; | ||
use Symfony\Component\Validator\Exception\UnexpectedTypeException; | ||
use Symfony\Component\Validator\Exception\UnexpectedValueException; | ||
|
||
/** | ||
* Validates whether a value is a valid timezone identifier. | ||
* | ||
* @author Javier Spagnoletti <phansys@gmail.com> | ||
* @author Hugo Hamon <hugohamon@neuf.fr> | ||
*/ | ||
class TimezoneValidator extends ConstraintValidator | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function validate($value, Constraint $constraint) | ||
{ | ||
if (!$constraint instanceof Timezone) { | ||
throw new UnexpectedTypeException($constraint, Timezone::class); | ||
} | ||
|
||
if (null === $value || '' === $value) { | ||
return; | ||
} | ||
|
||
if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) { | ||
throw new UnexpectedValueException($value, 'string'); | ||
} | ||
|
||
$value = (string) $value; | ||
|
||
// @see: https://bugs.php.net/bug.php?id=75928 | ||
if ($constraint->countryCode) { | ||
$timezoneIds = \DateTimeZone::listIdentifiers($constraint->zone, $constraint->countryCode); | ||
} else { | ||
$timezoneIds = \DateTimeZone::listIdentifiers($constraint->zone); | ||
} | ||
|
||
if ($timezoneIds && \in_array($value, $timezoneIds, true)) { | ||
return; | ||
} | ||
|
||
if ($constraint->countryCode) { | ||
$code = Timezone::TIMEZONE_IDENTIFIER_IN_COUNTRY_ERROR; | ||
} elseif (\DateTimeZone::ALL !== $constraint->zone) { | ||
$code = Timezone::TIMEZONE_IDENTIFIER_IN_ZONE_ERROR; | ||
} else { | ||
$code = Timezone::TIMEZONE_IDENTIFIER_ERROR; | ||
} | ||
|
||
$this->context->buildViolation($constraint->message) | ||
->setParameter('{{ value }}', $this->formatValue($value)) | ||
->setCode($code) | ||
->addViolation(); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getDefaultOption() | ||
{ | ||
return 'zone'; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function formatValue($value, $format = 0) | ||
{ | ||
$value = parent::formatValue($value, $format); | ||
|
||
if (!$value || \DateTimeZone::PER_COUNTRY === $value) { | ||
return $value; | ||
} | ||
|
||
return array_search($value, (new \ReflectionClass(\DateTimeZone::class))->getConstants(), true) ?: $value; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
src/Symfony/Component/Validator/Tests/Constraints/TimezoneTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Validator\Tests\Constraints; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Component\Validator\Constraints\Timezone; | ||
|
||
/** | ||
* @author Javier Spagnoletti <phansys@gmail.com> | ||
*/ | ||
class TimezoneTest extends TestCase | ||
{ | ||
public function testValidTimezoneConstraints() | ||
{ | ||
$constraint = new Timezone(); | ||
|
||
$constraint = new Timezone([ | ||
'message' => 'myMessage', | ||
'zone' => \DateTimeZone::PER_COUNTRY, | ||
'countryCode' => 'AR', | ||
]); | ||
|
||
$constraint = new Timezone([ | ||
'message' => 'myMessage', | ||
'zone' => \DateTimeZone::ALL, | ||
]); | ||
|
||
// Make an assertion in order to avoid this test to be marked as risky | ||
$this->assertInstanceOf(Timezone::class, $constraint); | ||
} | ||
|
||
/** | ||
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException | ||
*/ | ||
public function testExceptionForGroupedTimezonesByCountryWithWrongTimezone() | ||
{ | ||
$constraint = new Timezone([ | ||
'message' => 'myMessage', | ||
'zone' => \DateTimeZone::ALL, | ||
'countryCode' => 'AR', | ||
]); | ||
} | ||
|
||
/** | ||
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException | ||
*/ | ||
public function testExceptionForGroupedTimezonesByCountryWithoutTimezone() | ||
{ | ||
$constraint = new Timezone([ | ||
'message' => 'myMessage', | ||
'countryCode' => 'AR', | ||
]); | ||
} | ||
} |
Oops, something went wrong.