From 98bb6439fb03f60c0ad33450d6c3692e60d78761 Mon Sep 17 00:00:00 2001 From: Sylvain Fabre Date: Sun, 16 Sep 2018 11:22:15 +0200 Subject: [PATCH] Fix #28166 --- src/Symfony/Component/Validator/CHANGELOG.md | 1 + .../Component/Validator/Constraints/Bic.php | 19 +++ .../Validator/Constraints/BicValidator.php | 43 +++++- .../Tests/Constraints/BicValidatorTest.php | 128 ++++++++++++++++++ 4 files changed, 190 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index a1f068f3145a1..80786d1ce2ff2 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * made `ValidatorBuilder` final * marked `format` the default option in `DateTime` constraint * deprecated validating instances of `\DateTimeInterface` in `DateTimeValidator`, `DateValidator` and `TimeValidator`. + * validating a BIC based on a given IBAN 4.1.0 ----- diff --git a/src/Symfony/Component/Validator/Constraints/Bic.php b/src/Symfony/Component/Validator/Constraints/Bic.php index dee5d526938e9..d5f493a3fced7 100644 --- a/src/Symfony/Component/Validator/Constraints/Bic.php +++ b/src/Symfony/Component/Validator/Constraints/Bic.php @@ -11,7 +11,9 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** * @Annotation @@ -26,6 +28,7 @@ class Bic extends Constraint const INVALID_BANK_CODE_ERROR = '00559357-6170-4f29-aebd-d19330aa19cf'; const INVALID_COUNTRY_CODE_ERROR = '1ce76f8d-3c1f-451c-9e62-fe9c3ed486ae'; const INVALID_CASE_ERROR = '11884038-3312-4ae5-9d04-699f782130c7'; + const INVALID_IBAN_COUNTRY_CODE_ERROR = '29a2c3bb-587b-4996-b6f5-53081364cea5'; protected static $errorNames = array( self::INVALID_LENGTH_ERROR => 'INVALID_LENGTH_ERROR', @@ -36,4 +39,20 @@ class Bic extends Constraint ); public $message = 'This is not a valid Business Identifier Code (BIC).'; + public $ibanMessage = 'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.'; + public $iban; + public $ibanPropertyPath; + + public function __construct(array $options = null) + { + if (isset($options['iban']) && isset($options['ibanPropertyPath'])) { + throw new ConstraintDefinitionException(sprintf('The "%s" constraint requires only one of the "iban" or "ibanPropertyPath" options to be set, not both.', \get_class($this))); + } + + if (isset($options['propertyPath']) && !class_exists(PropertyAccess::class)) { + throw new ConstraintDefinitionException(sprintf('The "%s" constraint requires the Symfony PropertyAccess component to use the "ibanPropertyPath" option.', \get_class($this))); + } + + parent::__construct($options); + } } diff --git a/src/Symfony/Component/Validator/Constraints/BicValidator.php b/src/Symfony/Component/Validator/Constraints/BicValidator.php index da2ac5fbc3826..1a0dacbbe154c 100644 --- a/src/Symfony/Component/Validator/Constraints/BicValidator.php +++ b/src/Symfony/Component/Validator/Constraints/BicValidator.php @@ -12,9 +12,12 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\Intl\Intl; +use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; -use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** * @author Michael Hirschler @@ -23,6 +26,13 @@ */ class BicValidator extends ConstraintValidator { + private $propertyAccessor; + + public function __construct(PropertyAccessor $propertyAccessor = null) + { + $this->propertyAccessor = $propertyAccessor; + } + /** * {@inheritdoc} */ @@ -91,5 +101,36 @@ public function validate($value, Constraint $constraint) return; } + + // check against an IBAN + $iban = $constraint->iban; + $path = $constraint->ibanPropertyPath; + if ($path && null !== $object = $this->context->getObject()) { + try { + $iban = $this->getPropertyAccessor()->getValue($object, $path); + } catch (NoSuchPropertyException $e) { + throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: %s', $path, \get_class($constraint), $e->getMessage()), 0, $e); + } + } + if (!$iban) { + return; + } + $ibanCountryCode = substr($iban, 0, 2); + if (ctype_alpha($ibanCountryCode) && substr($canonicalize, 4, 2) !== $ibanCountryCode) { + $this->context->buildViolation($constraint->ibanMessage) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ iban }}', $iban) + ->setCode(Bic::INVALID_IBAN_COUNTRY_CODE_ERROR) + ->addViolation(); + } + } + + private function getPropertyAccessor() + { + if (null === $this->propertyAccessor) { + $this->propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + + return $this->propertyAccessor; } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/BicValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/BicValidatorTest.php index d135923f647ab..27dfe3d909d5b 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/BicValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/BicValidatorTest.php @@ -13,8 +13,29 @@ use Symfony\Component\Validator\Constraints\Bic; use Symfony\Component\Validator\Constraints\BicValidator; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +class BicComparisonTest_Class +{ + protected $value; + + public function __construct($value) + { + $this->value = $value; + } + + public function __toString() + { + return (string) $this->value; + } + + public function getValue() + { + return $this->value; + } +} + class BicValidatorTest extends ConstraintValidatorTestCase { protected function createValidator() @@ -36,6 +57,113 @@ public function testEmptyStringIsValid() $this->assertNoViolation(); } + public function testValidComparisonToPropertyPath() + { + $constraint = new Bic(array('ibanPropertyPath' => 'value')); + + $object = new BicComparisonTest_Class('FR14 2004 1010 0505 0001 3M02 606'); + + $this->setObject($object); + + $this->validator->validate('SOGEFRPP', $constraint); + + $this->assertNoViolation(); + } + + public function testValidComparisonToPropertyPathOnArray() + { + $constraint = new Bic(array('ibanPropertyPath' => '[root][value]')); + + $this->setObject(array('root' => array('value' => 'FR14 2004 1010 0505 0001 3M02 606'))); + + $this->validator->validate('SOGEFRPP', $constraint); + + $this->assertNoViolation(); + } + + public function testInvalidComparisonToPropertyPath() + { + $constraint = new Bic(array('ibanPropertyPath' => 'value')); + $constraint->ibanMessage = 'Constraint Message'; + + $object = new BicComparisonTest_Class('FR14 2004 1010 0505 0001 3M02 606'); + + $this->setObject($object); + + $this->validator->validate('UNCRIT2B912', $constraint); + + $this->buildViolation('Constraint Message') + ->setParameter('{{ value }}', '"UNCRIT2B912"') + ->setParameter('{{ iban }}', 'FR14 2004 1010 0505 0001 3M02 606') + ->setCode(Bic::INVALID_IBAN_COUNTRY_CODE_ERROR) + ->assertRaised(); + } + + public function testValidComparisonToValue() + { + $constraint = new Bic(array('iban' => 'FR14 2004 1010 0505 0001 3M02 606')); + $constraint->ibanMessage = 'Constraint Message'; + + $this->validator->validate('SOGEFRPP', $constraint); + + $this->assertNoViolation(); + } + + public function testInvalidComparisonToValue() + { + $constraint = new Bic(array('iban' => 'FR14 2004 1010 0505 0001 3M02 606')); + $constraint->ibanMessage = 'Constraint Message'; + + $this->validator->validate('UNCRIT2B912', $constraint); + + $this->buildViolation('Constraint Message') + ->setParameter('{{ value }}', '"UNCRIT2B912"') + ->setParameter('{{ iban }}', 'FR14 2004 1010 0505 0001 3M02 606') + ->setCode(Bic::INVALID_IBAN_COUNTRY_CODE_ERROR) + ->assertRaised(); + } + + public function testNoViolationOnNullObjectWithPropertyPath() + { + $constraint = new Bic(array('ibanPropertyPath' => 'propertyPath')); + + $this->setObject(null); + + $this->validator->validate('UNCRIT2B912', $constraint); + + $this->assertNoViolation(); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + * @expectedExceptionMessage requires only one of the "iban" or "ibanPropertyPath" options to be set, not both. + */ + public function testThrowsConstraintExceptionIfBothValueAndPropertyPath() + { + new Bic(array( + 'iban' => 'value', + 'ibanPropertyPath' => 'propertyPath', + )); + } + + public function testInvalidValuePath() + { + $constraint = new Bic(array('ibanPropertyPath' => 'foo')); + + if (method_exists($this, 'expectException')) { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage(sprintf('Invalid property path "foo" provided to "%s" constraint', \get_class($constraint))); + } else { + $this->setExpectedException(ConstraintDefinitionException::class, sprintf('Invalid property path "foo" provided to "%s" constraint', \get_class($constraint))); + } + + $object = new BicComparisonTest_Class(5); + + $this->setObject($object); + + $this->validator->validate('UNCRIT2B912', $constraint); + } + /** * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException */