Skip to content

Commit

Permalink
[Validator] Checking a BIC along with an IBAN
Browse files Browse the repository at this point in the history
Fix #28166
  • Loading branch information
sylfabre committed Sep 20, 2018
1 parent bf4d011 commit 645b016
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
Expand Up @@ -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
-----
Expand Down
19 changes: 19 additions & 0 deletions src/Symfony/Component/Validator/Constraints/Bic.php
Expand Up @@ -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
Expand All @@ -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',
Expand All @@ -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);
}
}
43 changes: 42 additions & 1 deletion src/Symfony/Component/Validator/Constraints/BicValidator.php
Expand Up @@ -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 <michael.vhirsch@gmail.com>
Expand All @@ -23,6 +26,13 @@
*/
class BicValidator extends ConstraintValidator
{
private $propertyAccessor;

public function __construct(PropertyAccessor $propertyAccessor = null)
{
$this->propertyAccessor = $propertyAccessor;
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -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;
}
}
128 changes: 128 additions & 0 deletions src/Symfony/Component/Validator/Tests/Constraints/BicValidatorTest.php
Expand Up @@ -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()
Expand All @@ -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
*/
Expand Down

0 comments on commit 645b016

Please sign in to comment.