Skip to content

Commit 80bb90a

Browse files
committed
feature symfony#54879 BicValidator add strict mode to validate bics in strict mode (maxbeckers)
This PR was merged into the 7.2 branch. Discussion ---------- BicValidator add strict mode to validate bics in strict mode | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix symfony#54822 | License | MIT Add a strict mode for bic validation (default strict, behavior before the pr is strict). But it is possible to set strict to `false` to allow lowercase input. I'll add it to the docs when this PR is merged. Commits ------- a28b5e6 [Validator] BicValidator add strict mode to validate bics in strict mode
2 parents b46424c + a28b5e6 commit 80bb90a

File tree

4 files changed

+65
-4
lines changed

4 files changed

+65
-4
lines changed

src/Symfony/Component/Validator/Constraints/Bic.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\PropertyAccess\PropertyAccess;
1616
use Symfony\Component\Validator\Constraint;
1717
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
18+
use Symfony\Component\Validator\Exception\InvalidArgumentException;
1819
use Symfony\Component\Validator\Exception\LogicException;
1920

2021
/**
@@ -27,6 +28,14 @@
2728
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
2829
class Bic extends Constraint
2930
{
31+
public const VALIDATION_MODE_STRICT = 'strict';
32+
public const VALIDATION_MODE_CASE_INSENSITIVE = 'case-insensitive';
33+
34+
public const VALIDATION_MODES = [
35+
self::VALIDATION_MODE_STRICT,
36+
self::VALIDATION_MODE_CASE_INSENSITIVE,
37+
];
38+
3039
public const INVALID_LENGTH_ERROR = '66dad313-af0b-4214-8566-6c799be9789c';
3140
public const INVALID_CHARACTERS_ERROR = 'f424c529-7add-4417-8f2d-4b656e4833e2';
3241
/**
@@ -49,25 +58,42 @@ class Bic extends Constraint
4958
public string $ibanMessage = 'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.';
5059
public ?string $iban = null;
5160
public ?string $ibanPropertyPath = null;
61+
public ?string $mode = self::VALIDATION_MODE_STRICT;
5262

5363
/**
5464
* @param array<string,mixed>|null $options
5565
* @param string|null $iban An IBAN value to validate that its country code is the same as the BIC's one
5666
* @param string|null $ibanPropertyPath Property path to the IBAN value when validating objects
5767
* @param string[]|null $groups
68+
* @param string|null $mode The mode used to validate the BIC; pass null to use the default mode (strict)
5869
*/
59-
public function __construct(?array $options = null, ?string $message = null, ?string $iban = null, ?string $ibanPropertyPath = null, ?string $ibanMessage = null, ?array $groups = null, mixed $payload = null)
60-
{
70+
public function __construct(
71+
?array $options = null,
72+
?string $message = null,
73+
?string $iban = null,
74+
?string $ibanPropertyPath = null,
75+
?string $ibanMessage = null,
76+
?array $groups = null,
77+
mixed $payload = null,
78+
?string $mode = null,
79+
) {
6180
if (!class_exists(Countries::class)) {
6281
throw new LogicException('The Intl component is required to use the Bic constraint. Try running "composer require symfony/intl".');
6382
}
83+
if (\is_array($options) && \array_key_exists('mode', $options) && !\in_array($options['mode'], self::VALIDATION_MODES, true)) {
84+
throw new InvalidArgumentException('The "mode" parameter value is not valid.');
85+
}
86+
if (null !== $mode && !\in_array($mode, self::VALIDATION_MODES, true)) {
87+
throw new InvalidArgumentException('The "mode" parameter value is not valid.');
88+
}
6489

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

6792
$this->message = $message ?? $this->message;
6893
$this->ibanMessage = $ibanMessage ?? $this->ibanMessage;
6994
$this->iban = $iban ?? $this->iban;
7095
$this->ibanPropertyPath = $ibanPropertyPath ?? $this->ibanPropertyPath;
96+
$this->mode = $mode ?? $this->mode;
7197

7298
if (null !== $this->iban && null !== $this->ibanPropertyPath) {
7399
throw new ConstraintDefinitionException('The "iban" and "ibanPropertyPath" options of the Iban constraint cannot be used at the same time.');

src/Symfony/Component/Validator/Constraints/BicValidator.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ public function validate(mixed $value, Constraint $constraint): void
100100
}
101101

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

112-
// should contain uppercase characters only
113-
if (strtoupper($canonicalize) !== $canonicalize) {
115+
// should contain uppercase characters only in strict mode
116+
if (Bic::VALIDATION_MODE_STRICT === $constraint->mode && strtoupper($canonicalize) !== $canonicalize) {
114117
$this->context->buildViolation($constraint->message)
115118
->setParameter('{{ value }}', $this->formatValue($value))
116119
->setCode(Bic::INVALID_CASE_ERROR)

src/Symfony/Component/Validator/Tests/Constraints/BicValidatorTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Validator\Constraints\Bic;
1515
use Symfony\Component\Validator\Constraints\BicValidator;
1616
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
17+
use Symfony\Component\Validator\Exception\InvalidArgumentException;
1718
use Symfony\Component\Validator\Exception\UnexpectedValueException;
1819
use Symfony\Component\Validator\Mapping\ClassMetadata;
1920
use Symfony\Component\Validator\Mapping\Loader\AttributeLoader;
@@ -301,6 +302,36 @@ public static function getValidBicSpecialCases()
301302
yield ['CAIXICBBXXX', 'ES79 2100 0813 6101 2345 6789'];
302303
yield ['CAIXEABBXXX', 'ES79 2100 0813 6101 2345 6789'];
303304
}
305+
306+
/**
307+
* @dataProvider getValidBicsWithNormalizerToUpper
308+
*/
309+
public function testValidBicsWithNormalizerToUpper($bic)
310+
{
311+
$this->validator->validate($bic, new Bic(mode: Bic::VALIDATION_MODE_CASE_INSENSITIVE));
312+
313+
$this->assertNoViolation();
314+
}
315+
316+
public static function getValidBicsWithNormalizerToUpper()
317+
{
318+
return [
319+
['ASPKAT2LXXX'],
320+
['ASPKat2LXXX'],
321+
['ASPKaT2LXXX'],
322+
['ASPKAt2LXXX'],
323+
['aspkat2lxxx'],
324+
];
325+
}
326+
327+
public function testFailOnInvalidMode()
328+
{
329+
$this->expectException(InvalidArgumentException::class);
330+
$this->validator->validate('ASPKAT2LXXX', new Bic(mode: 'invalid'));
331+
332+
$this->expectException(InvalidArgumentException::class);
333+
$this->validator->validate('ASPKAT2LXXX', new Bic(options: ['mode' => 'invalid']));
334+
}
304335
}
305336

306337
class BicComparisonTestClass

src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public static function getValidIbans()
5353
return [
5454
['CH9300762011623852957'], // Switzerland without spaces
5555
['CH93 0076 2011 6238 5295 7'], // Switzerland with multiple spaces
56+
['ch93 0076 2011 6238 5295 7'], // Switzerland lower case
5657

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

0 commit comments

Comments
 (0)