diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md
index e69e0e932f6c7..f2f020dbfd049 100644
--- a/src/Symfony/Component/Validator/CHANGELOG.md
+++ b/src/Symfony/Component/Validator/CHANGELOG.md
@@ -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`
+ * validating a BIC based on a given IBAN
4.1.0
-----
diff --git a/src/Symfony/Component/Validator/Constraints/Bic.php b/src/Symfony/Component/Validator/Constraints/Bic.php
index 9af23c8ddb89f..78eca1b8956d6 100644
--- a/src/Symfony/Component/Validator/Constraints/Bic.php
+++ b/src/Symfony/Component/Validator/Constraints/Bic.php
@@ -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;
/**
@@ -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',
@@ -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['propertyPath']) && !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);
}
}
diff --git a/src/Symfony/Component/Validator/Constraints/BicValidator.php b/src/Symfony/Component/Validator/Constraints/BicValidator.php
index 89077db68038a..2b77cd3c6cef8 100644
--- a/src/Symfony/Component/Validator/Constraints/BicValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/BicValidator.php
@@ -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;
@@ -24,6 +28,13 @@
*/
class BicValidator extends ConstraintValidator
{
+ private $propertyAccessor;
+
+ public function __construct(PropertyAccessor $propertyAccessor = null)
+ {
+ $this->propertyAccessor = $propertyAccessor;
+ }
+
/**
* {@inheritdoc}
*/
@@ -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;
}
}
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf
index 4bb2760b418e3..30e6804c7b83e 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf
@@ -326,6 +326,10 @@
This value should be a multiple of {{ compared_value }}.
+
+
+ This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.
+