Skip to content

Commit

Permalink
feature #30900 [Validator] add new Timezone validation constraint (…
Browse files Browse the repository at this point in the history
…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
fabpot committed Apr 6, 2019
2 parents 58d78ac + 536e53f commit c73fd10
Show file tree
Hide file tree
Showing 9 changed files with 497 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@ CHANGELOG
4.3.0
-----

* added `Timezone` constraint
* added `NotCompromisedPassword` constraint
* added options `iban` and `ibanPropertyPath` to Bic constraint
* added UATP cards support to `CardSchemeValidator`
Expand Down
51 changes: 51 additions & 0 deletions src/Symfony/Component/Validator/Constraints/Timezone.php
@@ -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 src/Symfony/Component/Validator/Constraints/TimezoneValidator.php
@@ -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;
}
}
Expand Up @@ -354,6 +354,10 @@
<source>This collection should contain only unique elements.</source>
<target>Diese Sammlung darf keine doppelten Elemente enthalten.</target>
</trans-unit>
<trans-unit id="92">
<source>This value is not a valid timezone.</source>
<target>Dieser Wert ist keine gültige Zeitzone.</target>
</trans-unit>
</body>
</file>
</xliff>
Expand Up @@ -354,6 +354,10 @@
<source>This collection should contain only unique elements.</source>
<target>This collection should contain only unique elements.</target>
</trans-unit>
<trans-unit id="92">
<source>This value is not a valid timezone.</source>
<target>This value is not a valid timezone.</target>
</trans-unit>
</body>
</file>
</xliff>
Expand Up @@ -330,6 +330,10 @@
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>Este Código de Identificación Bancaria (BIC) no está asociado con el IBAN {{ iban }}.</target>
</trans-unit>
<trans-unit id="92">
<source>This value is not a valid timezone.</source>
<target>Este valor no es una zona horaria válida.</target>
</trans-unit>
</body>
</file>
</xliff>
Expand Up @@ -334,6 +334,10 @@
<source>This value should be valid JSON.</source>
<target>Cette valeur doit être un JSON valide.</target>
</trans-unit>
<trans-unit id="92">
<source>This value is not a valid timezone.</source>
<target>Cette valeur n'est pas un fuseau horaire valide.</target>
</trans-unit>
</body>
</file>
</xliff>
63 changes: 63 additions & 0 deletions src/Symfony/Component/Validator/Tests/Constraints/TimezoneTest.php
@@ -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',
]);
}
}

0 comments on commit c73fd10

Please sign in to comment.