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 Oct 23, 2018
1 parent 9aaec94 commit bfd52dd
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
Expand Up @@ -13,6 +13,7 @@ CHANGELOG
* deprecated using the `Bic`, `Country`, `Currency`, `Language` and `Locale` constraints without `symfony/intl`
* deprecated using the `Email` constraint without `egulias/email-validator`
* deprecated using the `Expression` constraint without `symfony/expression-language`
* added options `iban` and `ibanPropertyPath` to Bic constraint

4.1.0
-----
Expand Down
16 changes: 15 additions & 1 deletion src/Symfony/Component/Validator/Constraints/Bic.php
Expand Up @@ -12,7 +12,9 @@
namespace Symfony\Component\Validator\Constraints;

use Symfony\Component\Intl\Intl;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\LogicException;

/**
Expand All @@ -28,6 +30,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 @@ -38,14 +41,25 @@ 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($options = null)
public function __construct(array $options = null)
{
if (!class_exists(Intl::class)) {
// throw new LogicException(sprintf('The "symfony/intl" component is required to use the "%s" constraint.', __CLASS__));
@trigger_error(sprintf('Using the "%s" constraint without the "symfony/intl" component installed is deprecated since Symfony 4.2.', __CLASS__), E_USER_DEPRECATED);
}

if (isset($options['iban']) && isset($options['ibanPropertyPath'])) {
throw new ConstraintDefinitionException(sprintf('The "iban" and "ibanPropertyPath" of the Iban constraint cannot be used at the same time.', \get_class($this)));
}

if (isset($options['ibanPropertyPath']) && !class_exists(PropertyAccess::class)) {
throw new LogicException(sprintf('The "symfony/property-access" component is required to use the "%s" constraint with the "ibanPropertyPath" option.', \get_class($this)));
}

parent::__construct($options);
}
}
45 changes: 45 additions & 0 deletions src/Symfony/Component/Validator/Constraints/BicValidator.php
Expand Up @@ -12,8 +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\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\LogicException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

Expand All @@ -24,6 +28,13 @@
*/
class BicValidator extends ConstraintValidator
{
private $propertyAccessor;

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

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -95,5 +106,39 @@ 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(): PropertyAccessor
{
if (null === $this->propertyAccessor) {
if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccess')) {
throw new LogicException('Unable to use property path as the Symfony PropertyAccess component is not installed.');
}
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
}

return $this->propertyAccessor;
}
}
Expand Up @@ -326,6 +326,10 @@
<source>This value should be a multiple of {{ compared_value }}.</source>
<target>This value should be a multiple of {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="85">
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</target>
</trans-unit>
</body>
</file>
</xliff>
Expand Up @@ -326,6 +326,10 @@
<source>This value should be a multiple of {{ compared_value }}.</source>
<target>Cette valeur doit être un multiple de {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="85">
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>Ce BIC n'est pas associé à l'IBAN {{ iban }}.</target>
</trans-unit>
</body>
</file>
</xliff>
128 changes: 128 additions & 0 deletions src/Symfony/Component/Validator/Tests/Constraints/BicValidatorTest.php
Expand Up @@ -13,6 +13,7 @@

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 BicValidatorTest extends ConstraintValidatorTestCase
Expand All @@ -36,6 +37,113 @@ public function testEmptyStringIsValid()
$this->assertNoViolation();
}

public function testValidComparisonToPropertyPath()
{
$constraint = new Bic(array('ibanPropertyPath' => 'value'));

$object = new BicComparisonTestClass('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 BicComparisonTestClass('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 BicComparisonTestClass(5);

$this->setObject($object);

$this->validator->validate('UNCRIT2B912', $constraint);
}

/**
* @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
*/
Expand Down Expand Up @@ -114,3 +222,23 @@ public function getInvalidBics()
);
}
}

class BicComparisonTestClass
{
protected $value;

public function __construct($value)
{
$this->value = $value;
}

public function __toString()
{
return (string) $this->value;
}

public function getValue()
{
return $this->value;
}
}

0 comments on commit bfd52dd

Please sign in to comment.