Skip to content

Commit

Permalink
[Validator] BicValidator add strict mode to validate bics in strict mode
Browse files Browse the repository at this point in the history
  • Loading branch information
maxbeckers committed May 15, 2024
1 parent 5a00a8b commit a28b5e6
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 4 deletions.
30 changes: 28 additions & 2 deletions src/Symfony/Component/Validator/Constraints/Bic.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\InvalidArgumentException;
use Symfony\Component\Validator\Exception\LogicException;

/**
Expand All @@ -27,6 +28,14 @@
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Bic extends Constraint
{
public const VALIDATION_MODE_STRICT = 'strict';
public const VALIDATION_MODE_CASE_INSENSITIVE = 'case-insensitive';

public const VALIDATION_MODES = [
self::VALIDATION_MODE_STRICT,
self::VALIDATION_MODE_CASE_INSENSITIVE,
];

public const INVALID_LENGTH_ERROR = '66dad313-af0b-4214-8566-6c799be9789c';
public const INVALID_CHARACTERS_ERROR = 'f424c529-7add-4417-8f2d-4b656e4833e2';
/**
Expand All @@ -49,25 +58,42 @@ class Bic extends Constraint
public string $ibanMessage = 'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.';
public ?string $iban = null;
public ?string $ibanPropertyPath = null;
public ?string $mode = self::VALIDATION_MODE_STRICT;

/**
* @param array<string,mixed>|null $options
* @param string|null $iban An IBAN value to validate that its country code is the same as the BIC's one
* @param string|null $ibanPropertyPath Property path to the IBAN value when validating objects
* @param string[]|null $groups
* @param string|null $mode The mode used to validate the BIC; pass null to use the default mode (strict)
*/
public function __construct(?array $options = null, ?string $message = null, ?string $iban = null, ?string $ibanPropertyPath = null, ?string $ibanMessage = null, ?array $groups = null, mixed $payload = null)
{
public function __construct(
?array $options = null,
?string $message = null,
?string $iban = null,
?string $ibanPropertyPath = null,
?string $ibanMessage = null,
?array $groups = null,
mixed $payload = null,
?string $mode = null,
) {
if (!class_exists(Countries::class)) {
throw new LogicException('The Intl component is required to use the Bic constraint. Try running "composer require symfony/intl".');
}
if (\is_array($options) && \array_key_exists('mode', $options) && !\in_array($options['mode'], self::VALIDATION_MODES, true)) {
throw new InvalidArgumentException('The "mode" parameter value is not valid.');
}
if (null !== $mode && !\in_array($mode, self::VALIDATION_MODES, true)) {
throw new InvalidArgumentException('The "mode" parameter value is not valid.');
}

parent::__construct($options, $groups, $payload);

$this->message = $message ?? $this->message;
$this->ibanMessage = $ibanMessage ?? $this->ibanMessage;
$this->iban = $iban ?? $this->iban;
$this->ibanPropertyPath = $ibanPropertyPath ?? $this->ibanPropertyPath;
$this->mode = $mode ?? $this->mode;

if (null !== $this->iban && null !== $this->ibanPropertyPath) {
throw new ConstraintDefinitionException('The "iban" and "ibanPropertyPath" options of the Iban constraint cannot be used at the same time.');
Expand Down
7 changes: 5 additions & 2 deletions src/Symfony/Component/Validator/Constraints/BicValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ public function validate(mixed $value, Constraint $constraint): void
}

$bicCountryCode = substr($canonicalize, 4, 2);
if (Bic::VALIDATION_MODE_CASE_INSENSITIVE === $constraint->mode) {
$bicCountryCode = strtoupper($bicCountryCode);
}
if (!isset(self::BIC_COUNTRY_TO_IBAN_COUNTRY_MAP[$bicCountryCode]) && !Countries::exists($bicCountryCode)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
Expand All @@ -109,8 +112,8 @@ public function validate(mixed $value, Constraint $constraint): void
return;
}

// should contain uppercase characters only
if (strtoupper($canonicalize) !== $canonicalize) {
// should contain uppercase characters only in strict mode
if (Bic::VALIDATION_MODE_STRICT === $constraint->mode && strtoupper($canonicalize) !== $canonicalize) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Bic::INVALID_CASE_ERROR)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Symfony\Component\Validator\Constraints\Bic;
use Symfony\Component\Validator\Constraints\BicValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\InvalidArgumentException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\AttributeLoader;
Expand Down Expand Up @@ -301,6 +302,36 @@ public static function getValidBicSpecialCases()
yield ['CAIXICBBXXX', 'ES79 2100 0813 6101 2345 6789'];
yield ['CAIXEABBXXX', 'ES79 2100 0813 6101 2345 6789'];
}

/**
* @dataProvider getValidBicsWithNormalizerToUpper
*/
public function testValidBicsWithNormalizerToUpper($bic)
{
$this->validator->validate($bic, new Bic(mode: Bic::VALIDATION_MODE_CASE_INSENSITIVE));

$this->assertNoViolation();
}

public static function getValidBicsWithNormalizerToUpper()
{
return [
['ASPKAT2LXXX'],
['ASPKat2LXXX'],
['ASPKaT2LXXX'],
['ASPKAt2LXXX'],
['aspkat2lxxx'],
];
}

public function testFailOnInvalidMode()
{
$this->expectException(InvalidArgumentException::class);
$this->validator->validate('ASPKAT2LXXX', new Bic(mode: 'invalid'));

$this->expectException(InvalidArgumentException::class);
$this->validator->validate('ASPKAT2LXXX', new Bic(options: ['mode' => 'invalid']));
}
}

class BicComparisonTestClass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public static function getValidIbans()
return [
['CH9300762011623852957'], // Switzerland without spaces
['CH93 0076 2011 6238 5295 7'], // Switzerland with multiple spaces
['ch93 0076 2011 6238 5295 7'], // Switzerland lower case

// Country list
// http://www.rbs.co.uk/corporate/international/g0/guide-to-international-business/regulatory-information/iban/iban-example.ashx
Expand Down

0 comments on commit a28b5e6

Please sign in to comment.