Skip to content

BicValidator add strict mode to validate bics in strict mode #54879

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

Merged
merged 1 commit into from
Jun 3, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@xabbuh I changed it now to mode, the way that was also done in the email validator, because i need id here for the check. With the normalizer way i wouldn't know to check this or not.

$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
Loading