AbstractComparison.php
1 year ago
AbstractComparisonValidator.php
1 year ago
All.php
1 year ago
AllValidator.php
4 years ago
AtLeastOneOf.php
1 year ago
AtLeastOneOfValidator.php
1 year ago
Bic.php
1 year ago
BicValidator.php
1 year ago
Blank.php
1 year ago
BlankValidator.php
4 years ago
Callback.php
1 year ago
CallbackValidator.php
4 years ago
CardScheme.php
1 year ago
CardSchemeValidator.php
1 year ago
Cascade.php
1 year ago
Choice.php
1 year ago
ChoiceValidator.php
1 year ago
Cidr.php
1 year ago
CidrValidator.php
4 years ago
Collection.php
1 year ago
CollectionValidator.php
4 years ago
Composite.php
1 year ago
Compound.php
4 years ago
CompoundValidator.php
4 years ago
Count.php
1 year ago
CountValidator.php
4 years ago
Country.php
1 year ago
CountryValidator.php
4 years ago
CssColor.php
1 year ago
CssColorValidator.php
1 year ago
Currency.php
1 year ago
CurrencyValidator.php
4 years ago
Date.php
1 year ago
DateTime.php
1 year ago
DateTimeValidator.php
2 years ago
DateValidator.php
1 year ago
DisableAutoMapping.php
1 year ago
DivisibleBy.php
4 years ago
DivisibleByValidator.php
4 years ago
Email.php
1 year ago
EmailValidator.php
1 year ago
EnableAutoMapping.php
1 year ago
EqualTo.php
4 years ago
EqualToValidator.php
4 years ago
Existence.php
4 years ago
Expression.php
1 year ago
ExpressionLanguageSyntax.php
1 year ago
ExpressionLanguageSyntaxValidator.php
1 year ago
ExpressionValidator.php
1 year ago
File.php
1 year ago
FileValidator.php
4 years ago
GreaterThan.php
4 years ago
GreaterThanOrEqual.php
4 years ago
GreaterThanOrEqualValidator.php
4 years ago
GreaterThanValidator.php
4 years ago
GroupSequence.php
4 years ago
GroupSequenceProvider.php
4 years ago
Hostname.php
1 year ago
HostnameValidator.php
4 years ago
Iban.php
1 year ago
IbanValidator.php
1 year ago
IdenticalTo.php
4 years ago
IdenticalToValidator.php
4 years ago
Image.php
1 year ago
ImageValidator.php
4 years ago
Ip.php
1 year ago
IpValidator.php
4 years ago
IsFalse.php
1 year ago
IsFalseValidator.php
4 years ago
IsNull.php
1 year ago
IsNullValidator.php
4 years ago
IsTrue.php
1 year ago
IsTrueValidator.php
4 years ago
Isbn.php
1 year ago
IsbnValidator.php
1 year ago
Isin.php
1 year ago
IsinValidator.php
4 years ago
Issn.php
1 year ago
IssnValidator.php
4 years ago
Json.php
1 year ago
JsonValidator.php
1 year ago
Language.php
1 year ago
LanguageValidator.php
4 years ago
Length.php
1 year ago
LengthValidator.php
4 years ago
LessThan.php
4 years ago
LessThanOrEqual.php
4 years ago
LessThanOrEqualValidator.php
4 years ago
LessThanValidator.php
4 years ago
Locale.php
1 year ago
LocaleValidator.php
4 years ago
Luhn.php
1 year ago
LuhnValidator.php
4 years ago
Negative.php
4 years ago
NegativeOrZero.php
4 years ago
NotBlank.php
1 year ago
NotBlankValidator.php
4 years ago
NotCompromisedPassword.php
1 year ago
NotCompromisedPasswordValidator.php
1 year ago
NotEqualTo.php
4 years ago
NotEqualToValidator.php
4 years ago
NotIdenticalTo.php
4 years ago
NotIdenticalToValidator.php
4 years ago
NotNull.php
1 year ago
NotNullValidator.php
4 years ago
NumberConstraintTrait.php
4 years ago
Optional.php
4 years ago
Positive.php
4 years ago
PositiveOrZero.php
4 years ago
Range.php
1 year ago
RangeValidator.php
1 year ago
Regex.php
1 year ago
RegexValidator.php
4 years ago
Required.php
4 years ago
Sequentially.php
1 year ago
SequentiallyValidator.php
4 years ago
Time.php
1 year ago
TimeValidator.php
1 year ago
Timezone.php
1 year ago
TimezoneValidator.php
1 year ago
Traverse.php
4 years ago
Type.php
1 year ago
TypeValidator.php
4 years ago
Ulid.php
1 year ago
UlidValidator.php
2 years ago
Unique.php
1 year ago
UniqueValidator.php
1 year ago
Url.php
1 year ago
UrlValidator.php
1 year ago
Uuid.php
1 year ago
UuidValidator.php
4 years ago
Valid.php
1 year ago
ValidValidator.php
4 years ago
ZeroComparisonConstraintTrait.php
1 year ago
index.php
3 years ago
IbanValidator.php
350 lines
| 1 | <?php |
| 2 | namespace MailPoetVendor\Symfony\Component\Validator\Constraints; |
| 3 | if (!defined('ABSPATH')) exit; |
| 4 | use MailPoetVendor\Symfony\Component\Validator\Constraint; |
| 5 | use MailPoetVendor\Symfony\Component\Validator\ConstraintValidator; |
| 6 | use MailPoetVendor\Symfony\Component\Validator\Exception\UnexpectedTypeException; |
| 7 | use MailPoetVendor\Symfony\Component\Validator\Exception\UnexpectedValueException; |
| 8 | class IbanValidator extends ConstraintValidator |
| 9 | { |
| 10 | private const FORMATS = [ |
| 11 | // auto-generated |
| 12 | 'AD' => 'AD\\d{2}\\d{4}\\d{4}[\\dA-Z]{12}', |
| 13 | // Andorra |
| 14 | 'AE' => 'AE\\d{2}\\d{3}\\d{16}', |
| 15 | // United Arab Emirates (The) |
| 16 | 'AL' => 'AL\\d{2}\\d{8}[\\dA-Z]{16}', |
| 17 | // Albania |
| 18 | 'AO' => 'AO\\d{2}\\d{21}', |
| 19 | // Angola |
| 20 | 'AT' => 'AT\\d{2}\\d{5}\\d{11}', |
| 21 | // Austria |
| 22 | 'AX' => 'FI\\d{2}\\d{3}\\d{11}', |
| 23 | // Finland |
| 24 | 'AZ' => 'AZ\\d{2}[A-Z]{4}[\\dA-Z]{20}', |
| 25 | // Azerbaijan |
| 26 | 'BA' => 'BA\\d{2}\\d{3}\\d{3}\\d{8}\\d{2}', |
| 27 | // Bosnia and Herzegovina |
| 28 | 'BE' => 'BE\\d{2}\\d{3}\\d{7}\\d{2}', |
| 29 | // Belgium |
| 30 | 'BF' => 'BF\\d{2}[\\dA-Z]{2}\\d{22}', |
| 31 | // Burkina Faso |
| 32 | 'BG' => 'BG\\d{2}[A-Z]{4}\\d{4}\\d{2}[\\dA-Z]{8}', |
| 33 | // Bulgaria |
| 34 | 'BH' => 'BH\\d{2}[A-Z]{4}[\\dA-Z]{14}', |
| 35 | // Bahrain |
| 36 | 'BI' => 'BI\\d{2}\\d{5}\\d{5}\\d{11}\\d{2}', |
| 37 | // Burundi |
| 38 | 'BJ' => 'BJ\\d{2}[\\dA-Z]{2}\\d{22}', |
| 39 | // Benin |
| 40 | 'BL' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 41 | // France |
| 42 | 'BR' => 'BR\\d{2}\\d{8}\\d{5}\\d{10}[A-Z]{1}[\\dA-Z]{1}', |
| 43 | // Brazil |
| 44 | 'BY' => 'BY\\d{2}[\\dA-Z]{4}\\d{4}[\\dA-Z]{16}', |
| 45 | // Republic of Belarus |
| 46 | 'CF' => 'CF\\d{2}\\d{23}', |
| 47 | // Central African Republic |
| 48 | 'CG' => 'CG\\d{2}\\d{23}', |
| 49 | // Congo, Republic of the |
| 50 | 'CH' => 'CH\\d{2}\\d{5}[\\dA-Z]{12}', |
| 51 | // Switzerland |
| 52 | 'CI' => 'CI\\d{2}[A-Z]{1}\\d{23}', |
| 53 | // Côte d'Ivoire |
| 54 | 'CM' => 'CM\\d{2}\\d{23}', |
| 55 | // Cameroon |
| 56 | 'CR' => 'CR\\d{2}\\d{4}\\d{14}', |
| 57 | // Costa Rica |
| 58 | 'CV' => 'CV\\d{2}\\d{21}', |
| 59 | // Cabo Verde |
| 60 | 'CY' => 'CY\\d{2}\\d{3}\\d{5}[\\dA-Z]{16}', |
| 61 | // Cyprus |
| 62 | 'CZ' => 'CZ\\d{2}\\d{4}\\d{6}\\d{10}', |
| 63 | // Czechia |
| 64 | 'DE' => 'DE\\d{2}\\d{8}\\d{10}', |
| 65 | // Germany |
| 66 | 'DJ' => 'DJ\\d{2}\\d{5}\\d{5}\\d{11}\\d{2}', |
| 67 | // Djibouti |
| 68 | 'DK' => 'DK\\d{2}\\d{4}\\d{9}\\d{1}', |
| 69 | // Denmark |
| 70 | 'DO' => 'DO\\d{2}[\\dA-Z]{4}\\d{20}', |
| 71 | // Dominican Republic |
| 72 | 'DZ' => 'DZ\\d{2}\\d{22}', |
| 73 | // Algeria |
| 74 | 'EE' => 'EE\\d{2}\\d{2}\\d{2}\\d{11}\\d{1}', |
| 75 | // Estonia |
| 76 | 'EG' => 'EG\\d{2}\\d{4}\\d{4}\\d{17}', |
| 77 | // Egypt |
| 78 | 'ES' => 'ES\\d{2}\\d{4}\\d{4}\\d{1}\\d{1}\\d{10}', |
| 79 | // Spain |
| 80 | 'FI' => 'FI\\d{2}\\d{3}\\d{11}', |
| 81 | // Finland |
| 82 | 'FK' => 'FK\\d{2}[A-Z]{2}\\d{12}', |
| 83 | // Falkland Islands |
| 84 | 'FO' => 'FO\\d{2}\\d{4}\\d{9}\\d{1}', |
| 85 | // Faroe Islands |
| 86 | 'FR' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 87 | // France |
| 88 | 'GA' => 'GA\\d{2}\\d{23}', |
| 89 | // Gabon |
| 90 | 'GB' => 'GB\\d{2}[A-Z]{4}\\d{6}\\d{8}', |
| 91 | // United Kingdom |
| 92 | 'GE' => 'GE\\d{2}[A-Z]{2}\\d{16}', |
| 93 | // Georgia |
| 94 | 'GF' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 95 | // France |
| 96 | 'GG' => 'GB\\d{2}[A-Z]{4}\\d{6}\\d{8}', |
| 97 | // United Kingdom |
| 98 | 'GI' => 'GI\\d{2}[A-Z]{4}[\\dA-Z]{15}', |
| 99 | // Gibraltar |
| 100 | 'GL' => 'GL\\d{2}\\d{4}\\d{9}\\d{1}', |
| 101 | // Greenland |
| 102 | 'GP' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 103 | // France |
| 104 | 'GQ' => 'GQ\\d{2}\\d{23}', |
| 105 | // Equatorial Guinea |
| 106 | 'GR' => 'GR\\d{2}\\d{3}\\d{4}[\\dA-Z]{16}', |
| 107 | // Greece |
| 108 | 'GT' => 'GT\\d{2}[\\dA-Z]{4}[\\dA-Z]{20}', |
| 109 | // Guatemala |
| 110 | 'GW' => 'GW\\d{2}[\\dA-Z]{2}\\d{19}', |
| 111 | // Guinea-Bissau |
| 112 | 'HN' => 'HN\\d{2}[A-Z]{4}\\d{20}', |
| 113 | // Honduras |
| 114 | 'HR' => 'HR\\d{2}\\d{7}\\d{10}', |
| 115 | // Croatia |
| 116 | 'HU' => 'HU\\d{2}\\d{3}\\d{4}\\d{1}\\d{15}\\d{1}', |
| 117 | // Hungary |
| 118 | 'IE' => 'IE\\d{2}[A-Z]{4}\\d{6}\\d{8}', |
| 119 | // Ireland |
| 120 | 'IL' => 'IL\\d{2}\\d{3}\\d{3}\\d{13}', |
| 121 | // Israel |
| 122 | 'IM' => 'GB\\d{2}[A-Z]{4}\\d{6}\\d{8}', |
| 123 | // United Kingdom |
| 124 | 'IQ' => 'IQ\\d{2}[A-Z]{4}\\d{3}\\d{12}', |
| 125 | // Iraq |
| 126 | 'IR' => 'IR\\d{2}\\d{22}', |
| 127 | // Iran |
| 128 | 'IS' => 'IS\\d{2}\\d{4}\\d{2}\\d{6}\\d{10}', |
| 129 | // Iceland |
| 130 | 'IT' => 'IT\\d{2}[A-Z]{1}\\d{5}\\d{5}[\\dA-Z]{12}', |
| 131 | // Italy |
| 132 | 'JE' => 'GB\\d{2}[A-Z]{4}\\d{6}\\d{8}', |
| 133 | // United Kingdom |
| 134 | 'JO' => 'JO\\d{2}[A-Z]{4}\\d{4}[\\dA-Z]{18}', |
| 135 | // Jordan |
| 136 | 'KM' => 'KM\\d{2}\\d{23}', |
| 137 | // Comoros |
| 138 | 'KW' => 'KW\\d{2}[A-Z]{4}[\\dA-Z]{22}', |
| 139 | // Kuwait |
| 140 | 'KZ' => 'KZ\\d{2}\\d{3}[\\dA-Z]{13}', |
| 141 | // Kazakhstan |
| 142 | 'LB' => 'LB\\d{2}\\d{4}[\\dA-Z]{20}', |
| 143 | // Lebanon |
| 144 | 'LC' => 'LC\\d{2}[A-Z]{4}[\\dA-Z]{24}', |
| 145 | // Saint Lucia |
| 146 | 'LI' => 'LI\\d{2}\\d{5}[\\dA-Z]{12}', |
| 147 | // Liechtenstein |
| 148 | 'LT' => 'LT\\d{2}\\d{5}\\d{11}', |
| 149 | // Lithuania |
| 150 | 'LU' => 'LU\\d{2}\\d{3}[\\dA-Z]{13}', |
| 151 | // Luxembourg |
| 152 | 'LV' => 'LV\\d{2}[A-Z]{4}[\\dA-Z]{13}', |
| 153 | // Latvia |
| 154 | 'LY' => 'LY\\d{2}\\d{3}\\d{3}\\d{15}', |
| 155 | // Libya |
| 156 | 'MA' => 'MA\\d{2}\\d{24}', |
| 157 | // Morocco |
| 158 | 'MC' => 'MC\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 159 | // Monaco |
| 160 | 'MD' => 'MD\\d{2}[\\dA-Z]{2}[\\dA-Z]{18}', |
| 161 | // Moldova |
| 162 | 'ME' => 'ME\\d{2}\\d{3}\\d{13}\\d{2}', |
| 163 | // Montenegro |
| 164 | 'MF' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 165 | // France |
| 166 | 'MG' => 'MG\\d{2}\\d{23}', |
| 167 | // Madagascar |
| 168 | 'MK' => 'MK\\d{2}\\d{3}[\\dA-Z]{10}\\d{2}', |
| 169 | // Macedonia |
| 170 | 'ML' => 'ML\\d{2}[\\dA-Z]{2}\\d{22}', |
| 171 | // Mali |
| 172 | 'MN' => 'MN\\d{2}\\d{4}\\d{12}', |
| 173 | // Mongolia |
| 174 | 'MQ' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 175 | // France |
| 176 | 'MR' => 'MR\\d{2}\\d{5}\\d{5}\\d{11}\\d{2}', |
| 177 | // Mauritania |
| 178 | 'MT' => 'MT\\d{2}[A-Z]{4}\\d{5}[\\dA-Z]{18}', |
| 179 | // Malta |
| 180 | 'MU' => 'MU\\d{2}[A-Z]{4}\\d{2}\\d{2}\\d{12}\\d{3}[A-Z]{3}', |
| 181 | // Mauritius |
| 182 | 'MZ' => 'MZ\\d{2}\\d{21}', |
| 183 | // Mozambique |
| 184 | 'NC' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 185 | // France |
| 186 | 'NE' => 'NE\\d{2}[A-Z]{2}\\d{22}', |
| 187 | // Niger |
| 188 | 'NI' => 'NI\\d{2}[A-Z]{4}\\d{24}', |
| 189 | // Nicaragua |
| 190 | 'NL' => 'NL\\d{2}[A-Z]{4}\\d{10}', |
| 191 | // Netherlands (The) |
| 192 | 'NO' => 'NO\\d{2}\\d{4}\\d{6}\\d{1}', |
| 193 | // Norway |
| 194 | 'OM' => 'OM\\d{2}\\d{3}[\\dA-Z]{16}', |
| 195 | // Oman |
| 196 | 'PF' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 197 | // France |
| 198 | 'PK' => 'PK\\d{2}[A-Z]{4}[\\dA-Z]{16}', |
| 199 | // Pakistan |
| 200 | 'PL' => 'PL\\d{2}\\d{8}\\d{16}', |
| 201 | // Poland |
| 202 | 'PM' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 203 | // France |
| 204 | 'PS' => 'PS\\d{2}[A-Z]{4}[\\dA-Z]{21}', |
| 205 | // Palestine, State of |
| 206 | 'PT' => 'PT\\d{2}\\d{4}\\d{4}\\d{11}\\d{2}', |
| 207 | // Portugal |
| 208 | 'QA' => 'QA\\d{2}[A-Z]{4}[\\dA-Z]{21}', |
| 209 | // Qatar |
| 210 | 'RE' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 211 | // France |
| 212 | 'RO' => 'RO\\d{2}[A-Z]{4}[\\dA-Z]{16}', |
| 213 | // Romania |
| 214 | 'RS' => 'RS\\d{2}\\d{3}\\d{13}\\d{2}', |
| 215 | // Serbia |
| 216 | 'RU' => 'RU\\d{2}\\d{9}\\d{5}[\\dA-Z]{15}', |
| 217 | // Russia |
| 218 | 'SA' => 'SA\\d{2}\\d{2}[\\dA-Z]{18}', |
| 219 | // Saudi Arabia |
| 220 | 'SC' => 'SC\\d{2}[A-Z]{4}\\d{2}\\d{2}\\d{16}[A-Z]{3}', |
| 221 | // Seychelles |
| 222 | 'SD' => 'SD\\d{2}\\d{2}\\d{12}', |
| 223 | // Sudan |
| 224 | 'SE' => 'SE\\d{2}\\d{3}\\d{16}\\d{1}', |
| 225 | // Sweden |
| 226 | 'SI' => 'SI\\d{2}\\d{5}\\d{8}\\d{2}', |
| 227 | // Slovenia |
| 228 | 'SK' => 'SK\\d{2}\\d{4}\\d{6}\\d{10}', |
| 229 | // Slovakia |
| 230 | 'SM' => 'SM\\d{2}[A-Z]{1}\\d{5}\\d{5}[\\dA-Z]{12}', |
| 231 | // San Marino |
| 232 | 'SN' => 'SN\\d{2}[A-Z]{2}\\d{22}', |
| 233 | // Senegal |
| 234 | 'SO' => 'SO\\d{2}\\d{4}\\d{3}\\d{12}', |
| 235 | // Somalia |
| 236 | 'ST' => 'ST\\d{2}\\d{4}\\d{4}\\d{11}\\d{2}', |
| 237 | // Sao Tome and Principe |
| 238 | 'SV' => 'SV\\d{2}[A-Z]{4}\\d{20}', |
| 239 | // El Salvador |
| 240 | 'TD' => 'TD\\d{2}\\d{23}', |
| 241 | // Chad |
| 242 | 'TF' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 243 | // France |
| 244 | 'TG' => 'TG\\d{2}[A-Z]{2}\\d{22}', |
| 245 | // Togo |
| 246 | 'TL' => 'TL\\d{2}\\d{3}\\d{14}\\d{2}', |
| 247 | // Timor-Leste |
| 248 | 'TN' => 'TN\\d{2}\\d{2}\\d{3}\\d{13}\\d{2}', |
| 249 | // Tunisia |
| 250 | 'TR' => 'TR\\d{2}\\d{5}\\d{1}[\\dA-Z]{16}', |
| 251 | // Turkey |
| 252 | 'UA' => 'UA\\d{2}\\d{6}[\\dA-Z]{19}', |
| 253 | // Ukraine |
| 254 | 'VA' => 'VA\\d{2}\\d{3}\\d{15}', |
| 255 | // Vatican City State |
| 256 | 'VG' => 'VG\\d{2}[A-Z]{4}\\d{16}', |
| 257 | // Virgin Islands |
| 258 | 'WF' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 259 | // France |
| 260 | 'XK' => 'XK\\d{2}\\d{4}\\d{10}\\d{2}', |
| 261 | // Kosovo |
| 262 | 'YE' => 'YE\\d{2}[A-Z]{4}\\d{4}[\\dA-Z]{18}', |
| 263 | // Yemen |
| 264 | 'YT' => 'FR\\d{2}\\d{5}\\d{5}[\\dA-Z]{11}\\d{2}', |
| 265 | ]; |
| 266 | public function validate($value, Constraint $constraint) |
| 267 | { |
| 268 | if (!$constraint instanceof Iban) { |
| 269 | throw new UnexpectedTypeException($constraint, Iban::class); |
| 270 | } |
| 271 | if (null === $value || '' === $value) { |
| 272 | return; |
| 273 | } |
| 274 | if (!\is_scalar($value) && !(\is_object($value) && \method_exists($value, '__toString'))) { |
| 275 | throw new UnexpectedValueException($value, 'string'); |
| 276 | } |
| 277 | $value = (string) $value; |
| 278 | // Remove spaces and convert to uppercase |
| 279 | $canonicalized = \str_replace(' ', '', \strtoupper($value)); |
| 280 | // The IBAN must contain only digits and characters... |
| 281 | if (!\ctype_alnum($canonicalized)) { |
| 282 | $this->context->buildViolation($constraint->message)->setParameter('{{ value }}', $this->formatValue($value))->setCode(Iban::INVALID_CHARACTERS_ERROR)->addViolation(); |
| 283 | return; |
| 284 | } |
| 285 | // ...start with a two-letter country code |
| 286 | $countryCode = \substr($canonicalized, 0, 2); |
| 287 | if (!\ctype_alpha($countryCode)) { |
| 288 | $this->context->buildViolation($constraint->message)->setParameter('{{ value }}', $this->formatValue($value))->setCode(Iban::INVALID_COUNTRY_CODE_ERROR)->addViolation(); |
| 289 | return; |
| 290 | } |
| 291 | // ...have a format available |
| 292 | if (!\array_key_exists($countryCode, self::FORMATS)) { |
| 293 | $this->context->buildViolation($constraint->message)->setParameter('{{ value }}', $this->formatValue($value))->setCode(Iban::NOT_SUPPORTED_COUNTRY_CODE_ERROR)->addViolation(); |
| 294 | return; |
| 295 | } |
| 296 | // ...and have a valid format |
| 297 | if (!\preg_match('/^' . self::FORMATS[$countryCode] . '$/', $canonicalized)) { |
| 298 | $this->context->buildViolation($constraint->message)->setParameter('{{ value }}', $this->formatValue($value))->setCode(Iban::INVALID_FORMAT_ERROR)->addViolation(); |
| 299 | return; |
| 300 | } |
| 301 | // Check digits should always between 2 and 98 |
| 302 | // A ECBS document (https://www.ecbs.org/Download/EBS204_V3.PDF) replicates part of the ISO/IEC 7064:2003 standard as a method for generating check digits in the range 02 to 98. |
| 303 | $checkDigits = (int) \substr($canonicalized, 2, 2); |
| 304 | if ($checkDigits < 2 || $checkDigits > 98) { |
| 305 | $this->context->buildViolation($constraint->message)->setParameter('{{ value }}', $this->formatValue($value))->setCode(Iban::CHECKSUM_FAILED_ERROR)->addViolation(); |
| 306 | return; |
| 307 | } |
| 308 | // Move the first four characters to the end |
| 309 | // e.g. CH93 0076 2011 6238 5295 7 |
| 310 | // -> 0076 2011 6238 5295 7 CH93 |
| 311 | $canonicalized = \substr($canonicalized, 4) . \substr($canonicalized, 0, 4); |
| 312 | // Convert all remaining letters to their ordinals |
| 313 | // The result is an integer, which is too large for PHP's int |
| 314 | // data type, so we store it in a string instead. |
| 315 | // e.g. 0076 2011 6238 5295 7 CH93 |
| 316 | // -> 0076 2011 6238 5295 7 121893 |
| 317 | $checkSum = self::toBigInt($canonicalized); |
| 318 | // Do a modulo-97 operation on the large integer |
| 319 | // We cannot use PHP's modulo operator, so we calculate the |
| 320 | // modulo step-wisely instead |
| 321 | if (1 !== self::bigModulo97($checkSum)) { |
| 322 | $this->context->buildViolation($constraint->message)->setParameter('{{ value }}', $this->formatValue($value))->setCode(Iban::CHECKSUM_FAILED_ERROR)->addViolation(); |
| 323 | } |
| 324 | } |
| 325 | private static function toBigInt(string $string) : string |
| 326 | { |
| 327 | $chars = \str_split($string); |
| 328 | $bigInt = ''; |
| 329 | foreach ($chars as $char) { |
| 330 | // Convert uppercase characters to ordinals, starting with 10 for "A" |
| 331 | if (\ctype_upper($char)) { |
| 332 | $bigInt .= \ord($char) - 55; |
| 333 | continue; |
| 334 | } |
| 335 | // Simply append digits |
| 336 | $bigInt .= $char; |
| 337 | } |
| 338 | return $bigInt; |
| 339 | } |
| 340 | private static function bigModulo97(string $bigInt) : int |
| 341 | { |
| 342 | $parts = \str_split($bigInt, 7); |
| 343 | $rest = 0; |
| 344 | foreach ($parts as $part) { |
| 345 | $rest = ($rest . $part) % 97; |
| 346 | } |
| 347 | return $rest; |
| 348 | } |
| 349 | } |
| 350 |