Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Validator] Checking a BIC along with an IBAN #28479

Merged
merged 1 commit into from Dec 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========

4.3.0
-----

* added options `iban` and `ibanPropertyPath` to Bic constraint

4.2.0
-----

Expand Down
14 changes: 14 additions & 0 deletions 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';
nicolas-grekas marked this conversation as resolved.
Show resolved Hide resolved

protected static $errorNames = array(
self::INVALID_LENGTH_ERROR => 'INVALID_LENGTH_ERROR',
Expand All @@ -38,6 +41,9 @@ 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 }}.';
sylfabre marked this conversation as resolved.
Show resolved Hide resolved
public $iban;
public $ibanPropertyPath;

public function __construct($options = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing "array" type hint?

{
Expand All @@ -46,6 +52,14 @@ public function __construct($options = null)
@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" options of the Iban constraint cannot be used at the same time.', self::class));
}

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.', self::class));
}

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\UnexpectedValueException;

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

public function __construct(PropertyAccessor $propertyAccessor = null)
sylfabre marked this conversation as resolved.
Show resolved Hide resolved
{
$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) {
sylfabre marked this conversation as resolved.
Show resolved Hide resolved
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(PropertyAccess::class)) {
throw new LogicException('Unable to use property path as the Symfony PropertyAccess component is not installed.');
}
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
sylfabre marked this conversation as resolved.
Show resolved Hide resolved
}

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 The "iban" and "ibanPropertyPath" options of the Iban constraint cannot be used at the same time
*/
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)));
sylfabre marked this conversation as resolved.
Show resolved Hide resolved
} 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\UnexpectedValueException
*/
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;
}
}