-
-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Validator] Sync IBAN formats with Swift IBAN registry
- Loading branch information
1 parent
535d93f
commit 59d9066
Showing
4 changed files
with
295 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,8 +20,6 @@ | |
* @author Manuel Reinhard <[email protected]> | ||
* @author Michael Schummel | ||
* @author Bernhard Schussek <[email protected]> | ||
* | ||
* @see http://www.michael-schummel.de/2007/10/05/iban-prufung-mit-php/ | ||
*/ | ||
class IbanValidator extends ConstraintValidator | ||
{ | ||
|
@@ -34,107 +32,135 @@ class IbanValidator extends ConstraintValidator | |
* a BBAN (Basic Bank Account Number) which has a fixed length per country and, | ||
* included within it, a bank identifier with a fixed position and a fixed length per country | ||
* | ||
* @see https://www.swift.com/sites/default/files/resources/iban_registry.pdf | ||
* @see Resources/bin/sync-iban-formats.php | ||
* @see https://www.swift.com/swift-resource/11971/download?language=en | ||
* @see https://en.wikipedia.org/wiki/International_Bank_Account_Number | ||
*/ | ||
private const FORMATS = [ | ||
// auto-generated | ||
'AD' => 'AD\d{2}\d{4}\d{4}[\dA-Z]{12}', // Andorra | ||
'AE' => 'AE\d{2}\d{3}\d{16}', // United Arab Emirates | ||
'AE' => 'AE\d{2}\d{3}\d{16}', // United Arab Emirates (The) | ||
'AL' => 'AL\d{2}\d{8}[\dA-Z]{16}', // Albania | ||
'AO' => 'AO\d{2}\d{21}', // Angola | ||
'AT' => 'AT\d{2}\d{5}\d{11}', // Austria | ||
'AX' => 'FI\d{2}\d{6}\d{7}\d{1}', // Aland Islands | ||
'AX' => 'FI\d{2}\d{3}\d{11}', // Finland | ||
'AZ' => 'AZ\d{2}[A-Z]{4}[\dA-Z]{20}', // Azerbaijan | ||
'BA' => 'BA\d{2}\d{3}\d{3}\d{8}\d{2}', // Bosnia and Herzegovina | ||
'BE' => 'BE\d{2}\d{3}\d{7}\d{2}', // Belgium | ||
'BF' => 'BF\d{2}\d{23}', // Burkina Faso | ||
'BF' => 'BF\d{2}[\dA-Z]{2}\d{22}', // Burkina Faso | ||
'BG' => 'BG\d{2}[A-Z]{4}\d{4}\d{2}[\dA-Z]{8}', // Bulgaria | ||
'BH' => 'BH\d{2}[A-Z]{4}[\dA-Z]{14}', // Bahrain | ||
'BI' => 'BI\d{2}\d{12}', // Burundi | ||
'BJ' => 'BJ\d{2}[A-Z]{1}\d{23}', // Benin | ||
'BY' => 'BY\d{2}[\dA-Z]{4}\d{4}[\dA-Z]{16}', // Belarus - https://bank.codes/iban/structure/belarus/ | ||
'BL' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Saint Barthelemy | ||
'BR' => 'BR\d{2}\d{8}\d{5}\d{10}[A-Z][\dA-Z]', // Brazil | ||
'CG' => 'CG\d{2}\d{23}', // Congo | ||
'BI' => 'BI\d{2}\d{5}\d{5}\d{11}\d{2}', // Burundi | ||
'BJ' => 'BJ\d{2}[\dA-Z]{2}\d{22}', // Benin | ||
'BL' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'BR' => 'BR\d{2}\d{8}\d{5}\d{10}[A-Z]{1}[\dA-Z]{1}', // Brazil | ||
'BY' => 'BY\d{2}[\dA-Z]{4}\d{4}[\dA-Z]{16}', // Republic of Belarus | ||
'CF' => 'CF\d{2}\d{23}', // Central African Republic | ||
'CG' => 'CG\d{2}\d{23}', // Congo, Republic of the | ||
'CH' => 'CH\d{2}\d{5}[\dA-Z]{12}', // Switzerland | ||
'CI' => 'CI\d{2}[A-Z]{1}\d{23}', // Ivory Coast | ||
'CM' => 'CM\d{2}\d{23}', // Cameron | ||
'CR' => 'CR\d{2}0\d{3}\d{14}', // Costa Rica | ||
'CV' => 'CV\d{2}\d{21}', // Cape Verde | ||
'CI' => 'CI\d{2}[A-Z]{1}\d{23}', // Côte d'Ivoire | ||
'CM' => 'CM\d{2}\d{23}', // Cameroon | ||
'CR' => 'CR\d{2}\d{4}\d{14}', // Costa Rica | ||
'CV' => 'CV\d{2}\d{21}', // Cabo Verde | ||
'CY' => 'CY\d{2}\d{3}\d{5}[\dA-Z]{16}', // Cyprus | ||
'CZ' => 'CZ\d{2}\d{20}', // Czech Republic | ||
'CZ' => 'CZ\d{2}\d{4}\d{6}\d{10}', // Czechia | ||
'DE' => 'DE\d{2}\d{8}\d{10}', // Germany | ||
'DJ' => 'DJ\d{2}\d{5}\d{5}\d{11}\d{2}', // Djibouti | ||
'DK' => 'DK\d{2}\d{4}\d{9}\d{1}', // Denmark | ||
'DO' => 'DO\d{2}[\dA-Z]{4}\d{20}', // Dominican Republic | ||
'DK' => 'DK\d{2}\d{4}\d{10}', // Denmark | ||
'DZ' => 'DZ\d{2}\d{20}', // Algeria | ||
'DZ' => 'DZ\d{2}\d{22}', // Algeria | ||
'EE' => 'EE\d{2}\d{2}\d{2}\d{11}\d{1}', // Estonia | ||
'ES' => 'ES\d{2}\d{4}\d{4}\d{1}\d{1}\d{10}', // Spain (also includes Canary Islands, Ceuta and Melilla) | ||
'FI' => 'FI\d{2}\d{6}\d{7}\d{1}', // Finland | ||
'EG' => 'EG\d{2}\d{4}\d{4}\d{17}', // Egypt | ||
'ES' => 'ES\d{2}\d{4}\d{4}\d{1}\d{1}\d{10}', // Spain | ||
'FI' => 'FI\d{2}\d{3}\d{11}', // Finland | ||
'FO' => 'FO\d{2}\d{4}\d{9}\d{1}', // Faroe Islands | ||
'FR' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'GF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // French Guyana | ||
'GB' => 'GB\d{2}[A-Z]{4}\d{6}\d{8}', // United Kingdom of Great Britain and Northern Ireland | ||
'GA' => 'GA\d{2}\d{23}', // Gabon | ||
'GB' => 'GB\d{2}[A-Z]{4}\d{6}\d{8}', // United Kingdom | ||
'GE' => 'GE\d{2}[A-Z]{2}\d{16}', // Georgia | ||
'GF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'GG' => 'GB\d{2}[A-Z]{4}\d{6}\d{8}', // United Kingdom | ||
'GI' => 'GI\d{2}[A-Z]{4}[\dA-Z]{15}', // Gibraltar | ||
'GL' => 'GL\d{2}\d{4}\d{9}\d{1}', // Greenland | ||
'GP' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Guadeloupe | ||
'GP' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'GQ' => 'GQ\d{2}\d{23}', // Equatorial Guinea | ||
'GR' => 'GR\d{2}\d{3}\d{4}[\dA-Z]{16}', // Greece | ||
'GT' => 'GT\d{2}[\dA-Z]{4}[\dA-Z]{20}', // Guatemala | ||
'GW' => 'GW\d{2}[\dA-Z]{2}\d{19}', // Guinea-Bissau | ||
'HN' => 'HN\d{2}[A-Z]{4}\d{20}', // Honduras | ||
'HR' => 'HR\d{2}\d{7}\d{10}', // Croatia | ||
'HU' => 'HU\d{2}\d{3}\d{4}\d{1}\d{15}\d{1}', // Hungary | ||
'IE' => 'IE\d{2}[A-Z]{4}\d{6}\d{8}', // Ireland | ||
'IL' => 'IL\d{2}\d{3}\d{3}\d{13}', // Israel | ||
'IM' => 'GB\d{2}[A-Z]{4}\d{6}\d{8}', // United Kingdom | ||
'IQ' => 'IQ\d{2}[A-Z]{4}\d{3}\d{12}', // Iraq | ||
'IR' => 'IR\d{2}\d{22}', // Iran | ||
'IS' => 'IS\d{2}\d{4}\d{2}\d{6}\d{10}', // Iceland | ||
'IT' => 'IT\d{2}[A-Z]{1}\d{5}\d{5}[\dA-Z]{12}', // Italy | ||
'JE' => 'GB\d{2}[A-Z]{4}\d{6}\d{8}', // United Kingdom | ||
'JO' => 'JO\d{2}[A-Z]{4}\d{4}[\dA-Z]{18}', // Jordan | ||
'KW' => 'KW\d{2}[A-Z]{4}\d{22}', // KUWAIT | ||
'KM' => 'KM\d{2}\d{23}', // Comoros | ||
'KW' => 'KW\d{2}[A-Z]{4}[\dA-Z]{22}', // Kuwait | ||
'KZ' => 'KZ\d{2}\d{3}[\dA-Z]{13}', // Kazakhstan | ||
'LB' => 'LB\d{2}\d{4}[\dA-Z]{20}', // LEBANON | ||
'LI' => 'LI\d{2}\d{5}[\dA-Z]{12}', // Liechtenstein (Principality of) | ||
'LB' => 'LB\d{2}\d{4}[\dA-Z]{20}', // Lebanon | ||
'LC' => 'LC\d{2}[A-Z]{4}[\dA-Z]{24}', // Saint Lucia | ||
'LI' => 'LI\d{2}\d{5}[\dA-Z]{12}', // Liechtenstein | ||
'LT' => 'LT\d{2}\d{5}\d{11}', // Lithuania | ||
'LU' => 'LU\d{2}\d{3}[\dA-Z]{13}', // Luxembourg | ||
'LV' => 'LV\d{2}[A-Z]{4}[\dA-Z]{13}', // Latvia | ||
'LY' => 'LY\d{2}\d{3}\d{3}\d{15}', // Libya | ||
'MA' => 'MA\d{2}\d{24}', // Morocco | ||
'MC' => 'MC\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Monaco | ||
'MD' => 'MD\d{2}[\dA-Z]{2}[\dA-Z]{18}', // Moldova | ||
'ME' => 'ME\d{2}\d{3}\d{13}\d{2}', // Montenegro | ||
'MF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Saint Martin (French part) | ||
'MF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'MG' => 'MG\d{2}\d{23}', // Madagascar | ||
'MK' => 'MK\d{2}\d{3}[\dA-Z]{10}\d{2}', // Macedonia, Former Yugoslav Republic of | ||
'ML' => 'ML\d{2}[A-Z]{1}\d{23}', // Mali | ||
'MQ' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Martinique | ||
'MK' => 'MK\d{2}\d{3}[\dA-Z]{10}\d{2}', // Macedonia | ||
'ML' => 'ML\d{2}[\dA-Z]{2}\d{22}', // Mali | ||
'MQ' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'MR' => 'MR\d{2}\d{5}\d{5}\d{11}\d{2}', // Mauritania | ||
'MT' => 'MT\d{2}[A-Z]{4}\d{5}[\dA-Z]{18}', // Malta | ||
'MU' => 'MU\d{2}[A-Z]{4}\d{2}\d{2}\d{12}\d{3}[A-Z]{3}', // Mauritius | ||
'MZ' => 'MZ\d{2}\d{21}', // Mozambique | ||
'NC' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // New Caledonia | ||
'NL' => 'NL\d{2}[A-Z]{4}\d{10}', // The Netherlands | ||
'NC' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'NE' => 'NE\d{2}[A-Z]{2}\d{22}', // Niger | ||
'NI' => 'NI\d{2}[A-Z]{4}\d{24}', // Nicaragua | ||
'NL' => 'NL\d{2}[A-Z]{4}\d{10}', // Netherlands (The) | ||
'NO' => 'NO\d{2}\d{4}\d{6}\d{1}', // Norway | ||
'PF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // French Polynesia | ||
'PF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'PK' => 'PK\d{2}[A-Z]{4}[\dA-Z]{16}', // Pakistan | ||
'PL' => 'PL\d{2}\d{8}\d{16}', // Poland | ||
'PM' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Saint Pierre et Miquelon | ||
'PM' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'PS' => 'PS\d{2}[A-Z]{4}[\dA-Z]{21}', // Palestine, State of | ||
'PT' => 'PT\d{2}\d{4}\d{4}\d{11}\d{2}', // Portugal (plus Azores and Madeira) | ||
'PT' => 'PT\d{2}\d{4}\d{4}\d{11}\d{2}', // Portugal | ||
'QA' => 'QA\d{2}[A-Z]{4}[\dA-Z]{21}', // Qatar | ||
'RE' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Reunion | ||
'RE' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'RO' => 'RO\d{2}[A-Z]{4}[\dA-Z]{16}', // Romania | ||
'RS' => 'RS\d{2}\d{3}\d{13}\d{2}', // Serbia | ||
'RU' => 'RU\d{2}\d{9}\d{5}[\dA-Z]{15}', // Russia | ||
'SA' => 'SA\d{2}\d{2}[\dA-Z]{18}', // Saudi Arabia | ||
'SC' => 'SC\d{2}[A-Z]{4}\d{2}\d{2}\d{16}[A-Z]{3}', // Seychelles | ||
'SD' => 'SD\d{2}\d{2}\d{12}', // Sudan | ||
'SE' => 'SE\d{2}\d{3}\d{16}\d{1}', // Sweden | ||
'SI' => 'SI\d{2}\d{5}\d{8}\d{2}', // Slovenia | ||
'SK' => 'SK\d{2}\d{4}\d{6}\d{10}', // Slovak Republic | ||
'SK' => 'SK\d{2}\d{4}\d{6}\d{10}', // Slovakia | ||
'SM' => 'SM\d{2}[A-Z]{1}\d{5}\d{5}[\dA-Z]{12}', // San Marino | ||
'SN' => 'SN\d{2}[A-Z]{1}\d{23}', // Senegal | ||
'TF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // French Southern Territories | ||
'SN' => 'SN\d{2}[A-Z]{2}\d{22}', // Senegal | ||
'SO' => 'SO\d{2}\d{4}\d{3}\d{12}', // Somalia | ||
'ST' => 'ST\d{2}\d{4}\d{4}\d{11}\d{2}', // Sao Tome and Principe | ||
'SV' => 'SV\d{2}[A-Z]{4}\d{20}', // El Salvador | ||
'TD' => 'TD\d{2}\d{23}', // Chad | ||
'TF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'TG' => 'TG\d{2}[A-Z]{2}\d{22}', // Togo | ||
'TL' => 'TL\d{2}\d{3}\d{14}\d{2}', // Timor-Leste | ||
'TN' => 'TN\d{2}\d{2}\d{3}\d{13}\d{2}', // Tunisia | ||
'TR' => 'TR\d{2}\d{5}[\dA-Z]{1}[\dA-Z]{16}', // Turkey | ||
'TR' => 'TR\d{2}\d{5}\d{1}[\dA-Z]{16}', // Turkey | ||
'UA' => 'UA\d{2}\d{6}[\dA-Z]{19}', // Ukraine | ||
'VA' => 'VA\d{2}\d{3}\d{15}', // Vatican City State | ||
'VG' => 'VG\d{2}[A-Z]{4}\d{16}', // Virgin Islands, British | ||
'WF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Wallis and Futuna Islands | ||
'XK' => 'XK\d{2}\d{4}\d{10}\d{2}', // Republic of Kosovo | ||
'YT' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Mayotte | ||
'VG' => 'VG\d{2}[A-Z]{4}\d{16}', // Virgin Islands | ||
'WF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
'XK' => 'XK\d{2}\d{4}\d{10}\d{2}', // Kosovo | ||
'YT' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France | ||
]; | ||
|
||
/** | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
#!/usr/bin/env php | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
if ('cli' !== \PHP_SAPI) { | ||
throw new \Exception('This script must be run from the command line.'); | ||
} | ||
|
||
/* | ||
* This script syncs IBAN formats from the upstream and updates them into IbanValidator. | ||
* | ||
* Usage: | ||
* php Resources/bin/sync-iban-formats.php | ||
*/ | ||
|
||
error_reporting(\E_ALL); | ||
|
||
set_error_handler(static function (int $type, string $msg, string $file, int $line): void { | ||
throw new \ErrorException($msg, 0, $type, $file, $line); | ||
}); | ||
|
||
echo "Collecting IBAN formats...\n"; | ||
|
||
$formats = array_merge( | ||
(new WikipediaIbanProvider())->getIbanFormats(), | ||
(new SwiftRegistryIbanProvider())->getIbanFormats() | ||
); | ||
|
||
printf("Collected %d IBAN formats\n", count($formats)); | ||
|
||
echo "Updating validator...\n"; | ||
|
||
updateValidatorFormats(__DIR__.'/../../Constraints/IbanValidator.php', $formats); | ||
|
||
echo "Done.\n"; | ||
|
||
exit(0); | ||
|
||
function updateValidatorFormats(string $validatorPath, array $formats): void | ||
{ | ||
ksort($formats); | ||
|
||
$formatsContent = "[\n"; | ||
$formatsContent .= " // auto-generated\n"; | ||
|
||
foreach ($formats as $countryCode => [$format, $country]) { | ||
$formatsContent .= " '{$countryCode}' => '{$format}', // {$country}\n"; | ||
} | ||
|
||
$formatsContent .= ' ]'; | ||
|
||
$validatorContent = file_get_contents($validatorPath); | ||
|
||
$validatorContent = preg_replace( | ||
'/FORMATS = \[.*?\];/s', | ||
"FORMATS = {$formatsContent};", | ||
$validatorContent | ||
); | ||
|
||
file_put_contents($validatorPath, $validatorContent); | ||
} | ||
|
||
final class SwiftRegistryIbanProvider | ||
{ | ||
/** | ||
* @return array<string, array{string, string}> | ||
*/ | ||
public function getIbanFormats(): array | ||
{ | ||
$items = $this->readPropertiesFromRegistry([ | ||
'Name of country' => 'country', | ||
'IBAN prefix country code (ISO 3166)' => 'country_code', | ||
'IBAN structure' => 'iban_structure', | ||
'Country code includes other countries/territories' => 'included_country_codes', | ||
]); | ||
|
||
$formats = []; | ||
|
||
foreach ($items as $item) { | ||
$formats[$item['country_code']] = [$this->buildIbanRegexp($item['iban_structure']), $item['country']]; | ||
|
||
foreach ($this->parseCountryCodesList($item['included_country_codes']) as $includedCountryCode) { | ||
$formats[$includedCountryCode] = $formats[$item['country_code']]; | ||
} | ||
} | ||
|
||
return $formats; | ||
} | ||
|
||
/** | ||
* @return list<string> | ||
*/ | ||
private function parseCountryCodesList(string $countryCodesList): array | ||
{ | ||
if ('N/A' === $countryCodesList) { | ||
return []; | ||
} | ||
|
||
$countryCodes = []; | ||
|
||
foreach (explode(',', $countryCodesList) as $countryCode) { | ||
$countryCodes[] = preg_replace('/^([A-Z]{2})(\s+\(.+?\))?$/', '$1', trim($countryCode)); | ||
} | ||
|
||
return $countryCodes; | ||
} | ||
|
||
/** | ||
* @param array<string, string> $properties | ||
* | ||
* @return list<array<string, string>> | ||
*/ | ||
private function readPropertiesFromRegistry(array $properties): array | ||
{ | ||
$items = []; | ||
|
||
$registryContent = file_get_contents('https://www.swift.com/swift-resource/11971/download'); | ||
$lines = explode("\n", $registryContent); | ||
|
||
// skip header line | ||
array_shift($lines); | ||
|
||
foreach ($lines as $line) { | ||
$columns = str_getcsv($line, "\t"); | ||
$propertyLabel = array_shift($columns); | ||
|
||
if (!isset($properties[$propertyLabel])) { | ||
continue; | ||
} | ||
|
||
$propertyField = $properties[$propertyLabel]; | ||
|
||
foreach ($columns as $index => $value) { | ||
$items[$index][$propertyField] = $value; | ||
} | ||
} | ||
|
||
return array_values($items); | ||
} | ||
|
||
private function buildIbanRegexp(string $ibanStructure): string | ||
{ | ||
$pattern = $ibanStructure; | ||
|
||
$pattern = preg_replace('/(\d+)!n/', '\\d{$1}', $pattern); | ||
$pattern = preg_replace('/(\d+)!a/', '[A-Z]{$1}', $pattern); | ||
$pattern = preg_replace('/(\d+)!c/', '[\\dA-Z]{$1}', $pattern); | ||
|
||
return $pattern; | ||
} | ||
} | ||
|
||
final class WikipediaIbanProvider | ||
{ | ||
/** | ||
* @return array<string, array{string, string}> | ||
*/ | ||
public function getIbanFormats(): array | ||
{ | ||
$formats = []; | ||
|
||
foreach ($this->readIbanFormatsTable() as $item) { | ||
if (!preg_match('/^([A-Z]{2})/', $item['Example'], $matches)) { | ||
continue; | ||
} | ||
|
||
$countryCode = $matches[1]; | ||
|
||
$formats[$countryCode] = [$this->buildIbanRegexp($countryCode, $item['BBAN Format']), $item['Country']]; | ||
} | ||
|
||
return $formats; | ||
} | ||
|
||
/** | ||
* @return list<array<string, string|int>> | ||
*/ | ||
private function readIbanFormatsTable(): array | ||
{ | ||
$tablesResponse = file_get_contents('https://www.wikitable2json.com/api/International_Bank_Account_Number?table=3&keyRows=1&clearRef=true'); | ||
|
||
return json_decode($tablesResponse, true, 512, JSON_THROW_ON_ERROR)[0]; | ||
} | ||
|
||
private function buildIbanRegexp(string $countryCode, string $bbanFormat): string | ||
{ | ||
$pattern = $bbanFormat; | ||
|
||
$pattern = preg_replace('/\s*,\s*/', '', $pattern); | ||
$pattern = preg_replace('/(\d+)n/', '\\d{$1}', $pattern); | ||
$pattern = preg_replace('/(\d+)a/', '[A-Z]{$1}', $pattern); | ||
$pattern = preg_replace('/(\d+)c/', '[\\dA-Z]{$1}', $pattern); | ||
|
||
return $countryCode.'\\d{2}'.$pattern; | ||
} | ||
} |
Oops, something went wrong.