mirror of
https://github.com/Respect/Validation.git
synced 2024-06-04 14:52:17 +02:00
Create "Iban" rule
The validation procedure is adhering to what is described in Wikipedia. Link: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN Co-authored-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
parent
3723a124c8
commit
6d3eb6afed
|
@ -179,6 +179,7 @@
|
|||
## Banking
|
||||
|
||||
- [CreditCard](rules/CreditCard.md)
|
||||
- [Iban](rules/Iban.md)
|
||||
|
||||
## Other
|
||||
|
||||
|
@ -273,6 +274,7 @@
|
|||
- [Graph](rules/Graph.md)
|
||||
- [GreaterThan](rules/GreaterThan.md)
|
||||
- [HexRgbColor](rules/HexRgbColor.md)
|
||||
- [Iban](rules/Iban.md)
|
||||
- [INSTALL](installation.md)
|
||||
- [Identical](rules/Identical.md)
|
||||
- [IdentityCard](rules/IdentityCard.md)
|
||||
|
|
|
@ -45,6 +45,7 @@ Version | Description
|
|||
See also:
|
||||
|
||||
- [Digit](Digit.md)
|
||||
- [Iban](Iban.md)
|
||||
- [Luhn](Luhn.md)
|
||||
- [NoWhitespace](NoWhitespace.md)
|
||||
- [Regex](Regex.md)
|
||||
|
|
30
docs/rules/Iban.md
Normal file
30
docs/rules/Iban.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Iban
|
||||
|
||||
- `v::iban()`
|
||||
|
||||
Validates whether the input is a valid [IBAN][] (International Bank Account
|
||||
Number) or not.
|
||||
|
||||
```php
|
||||
v::iban()->validate('SE35 5000 0000 0549 1000 0003'); // true
|
||||
v::iban()->validate('ch9300762011623852957'); // true
|
||||
|
||||
v::iban()->validate('ZZ32 5000 5880 7742'); // false
|
||||
v::iban()->validate(123456789); // false
|
||||
v::iban()->validate(''); // false
|
||||
```
|
||||
|
||||
## Changelog
|
||||
|
||||
Version | Description
|
||||
--------|-------------
|
||||
2.0.0 | Created
|
||||
|
||||
***
|
||||
See also:
|
||||
|
||||
- [CreditCard](CreditCard.md)
|
||||
- [MacAddress](MacAddress.md)
|
||||
- [PostalCode](PostalCode.md)
|
||||
|
||||
[IBAN]: https://en.wikipedia.org/wiki/International_Bank_Account_Number
|
|
@ -19,5 +19,6 @@ Version | Description
|
|||
See also:
|
||||
|
||||
- [Domain](Domain.md)
|
||||
- [Iban](Iban.md)
|
||||
- [Ip](Ip.md)
|
||||
- [Tld](Tld.md)
|
||||
|
|
|
@ -26,3 +26,4 @@ Version | Description
|
|||
See also:
|
||||
|
||||
- [CountryCode](CountryCode.md)
|
||||
- [Iban](Iban.md)
|
||||
|
|
32
library/Exceptions/IbanException.php
Normal file
32
library/Exceptions/IbanException.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Respect/Validation.
|
||||
*
|
||||
* (c) Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the "LICENSE.md"
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Respect\Validation\Exceptions;
|
||||
|
||||
/**
|
||||
* @author Mazen Touati <mazen_touati@hotmail.com>
|
||||
*/
|
||||
final class IbanException extends ValidationException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $defaultTemplates = [
|
||||
self::MODE_DEFAULT => [
|
||||
self::STANDARD => '{{name}} must be a valid IBAN',
|
||||
],
|
||||
self::MODE_NEGATIVE => [
|
||||
self::STANDARD => '{{name}} must not be a valid IBAN',
|
||||
],
|
||||
];
|
||||
}
|
152
library/Rules/Iban.php
Normal file
152
library/Rules/Iban.php
Normal file
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Respect/Validation.
|
||||
*
|
||||
* (c) Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the "LICENSE.md"
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Respect\Validation\Rules;
|
||||
|
||||
use function bcmod;
|
||||
use function is_string;
|
||||
use function ord;
|
||||
use function preg_match;
|
||||
use function preg_replace_callback;
|
||||
use function str_replace;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Validates whether the input is a valid IBAN (International Bank Account Number) or not.
|
||||
*
|
||||
* @author Mazen Touati <mazen_touati@hotmail.com>
|
||||
*/
|
||||
final class Iban extends AbstractRule
|
||||
{
|
||||
private const COUNTRIES_LENGTHS = [
|
||||
'AL' => 28,
|
||||
'AD' => 24,
|
||||
'AT' => 20,
|
||||
'AZ' => 28,
|
||||
'BH' => 22,
|
||||
'BE' => 16,
|
||||
'BA' => 20,
|
||||
'BR' => 29,
|
||||
'BG' => 22,
|
||||
'CR' => 21,
|
||||
'HR' => 21,
|
||||
'CY' => 28,
|
||||
'CZ' => 24,
|
||||
'DK' => 18,
|
||||
'DO' => 28,
|
||||
'EE' => 20,
|
||||
'FO' => 18,
|
||||
'FI' => 18,
|
||||
'FR' => 27,
|
||||
'GE' => 22,
|
||||
'DE' => 22,
|
||||
'GI' => 23,
|
||||
'GR' => 27,
|
||||
'GL' => 18,
|
||||
'GT' => 28,
|
||||
'HU' => 28,
|
||||
'IS' => 26,
|
||||
'IE' => 22,
|
||||
'IL' => 23,
|
||||
'IT' => 27,
|
||||
'JO' => 30,
|
||||
'KZ' => 20,
|
||||
'KW' => 30,
|
||||
'LV' => 21,
|
||||
'LB' => 28,
|
||||
'LI' => 21,
|
||||
'LT' => 20,
|
||||
'LU' => 20,
|
||||
'MK' => 19,
|
||||
'MT' => 31,
|
||||
'MR' => 27,
|
||||
'MU' => 30,
|
||||
'MD' => 24,
|
||||
'MC' => 27,
|
||||
'ME' => 22,
|
||||
'NL' => 18,
|
||||
'NO' => 15,
|
||||
'PK' => 24,
|
||||
'PL' => 28,
|
||||
'PS' => 29,
|
||||
'PT' => 25,
|
||||
'QA' => 29,
|
||||
'XK' => 20,
|
||||
'RO' => 24,
|
||||
'LC' => 32,
|
||||
'SM' => 27,
|
||||
'ST' => 25,
|
||||
'SA' => 24,
|
||||
'RS' => 22,
|
||||
'SC' => 31,
|
||||
'SK' => 24,
|
||||
'SI' => 19,
|
||||
'ES' => 24,
|
||||
'SE' => 24,
|
||||
'CH' => 21,
|
||||
'TL' => 23,
|
||||
'TN' => 24,
|
||||
'TR' => 26,
|
||||
'UA' => 29,
|
||||
'AE' => 23,
|
||||
'GB' => 22,
|
||||
'VG' => 24,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($input): bool
|
||||
{
|
||||
if (!is_string($input)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$iban = str_replace(' ', '', $input);
|
||||
if (!preg_match('/[A-Z0-9]{15,34}/', $iban)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$countryCode = substr($iban, 0, 2);
|
||||
if (!$this->hasValidCountryLength($iban, $countryCode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$checkDigits = substr($iban, 2, 2);
|
||||
$bban = substr($iban, 4);
|
||||
$rearranged = $bban.$countryCode.$checkDigits;
|
||||
|
||||
return bcmod($this->convertToInteger($rearranged), '97') === '1';
|
||||
}
|
||||
|
||||
private function hasValidCountryLength(string $iban, string $countryCode): bool
|
||||
{
|
||||
if (!isset(self::COUNTRIES_LENGTHS[$countryCode])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strlen($iban) === self::COUNTRIES_LENGTHS[$countryCode];
|
||||
}
|
||||
|
||||
private function convertToInteger(string $reArrangedIban): string
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/[A-Z]/',
|
||||
static function (array $match): int {
|
||||
return ord($match[0]) - 55;
|
||||
},
|
||||
$reArrangedIban
|
||||
);
|
||||
}
|
||||
}
|
|
@ -78,6 +78,7 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|||
* @method static Validator graph(string ...$additionalChars)
|
||||
* @method static Validator greaterThan($compareTo)
|
||||
* @method static Validator hexRgbColor()
|
||||
* @method static Validator iban()
|
||||
* @method static Validator identical($value)
|
||||
* @method static Validator identityCard(string $countryCode)
|
||||
* @method static Validator image(finfo $fileInfo = null)
|
||||
|
|
42
tests/integration/rules/iban.phpt
Normal file
42
tests/integration/rules/iban.phpt
Normal file
|
@ -0,0 +1,42 @@
|
|||
--CREDITS--
|
||||
Mazen Touati <mazen_touati@hotmail.com>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use Respect\Validation\Exceptions\IbanException;
|
||||
use Respect\Validation\Exceptions\NestedValidationException;
|
||||
use Respect\Validation\Validator as v;
|
||||
|
||||
try {
|
||||
v::iban()->check('SE35 5000 5880 7742');
|
||||
} catch (IbanException $exception) {
|
||||
echo $exception->getMessage().PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
v::not(v::iban())->check('GB82 WEST 1234 5698 7654 32');
|
||||
} catch (IbanException $exception) {
|
||||
echo $exception->getMessage().PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
v::iban()->assert('NOT AN IBAN');
|
||||
} catch (NestedValidationException $exception) {
|
||||
echo $exception->getFullMessage().PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
v::not(v::iban())->assert('HU93 1160 0006 0000 0000 1234 5676');
|
||||
} catch (NestedValidationException $exception) {
|
||||
echo $exception->getFullMessage().PHP_EOL;
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
"SE35 5000 5880 7742" must be a valid IBAN
|
||||
"GB82 WEST 1234 5698 7654 32" must not be a valid IBAN
|
||||
- "NOT AN IBAN" must be a valid IBAN
|
||||
- "HU93 1160 0006 0000 0000 1234 5676" must not be a valid IBAN
|
71
tests/unit/Rules/IbanTest.php
Normal file
71
tests/unit/Rules/IbanTest.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Respect/Validation.
|
||||
*
|
||||
* (c) Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the "LICENSE.md"
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Respect\Validation\Rules;
|
||||
|
||||
use Respect\Validation\Test\RuleTestCase;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* @group rule
|
||||
*
|
||||
* @covers \Respect\Validation\Rules\Iban
|
||||
*
|
||||
* @author Mazen Touati <mazen_touati@hotmail.com>
|
||||
*/
|
||||
final class IbanTest extends RuleTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function providerForValidInput(): array
|
||||
{
|
||||
$sut = new Iban();
|
||||
|
||||
return [
|
||||
'Belgium' => [$sut, 'BE71 0961 2345 6769'],
|
||||
'France' => [$sut, 'FR76 3000 6000 0112 3456 7890 189'],
|
||||
'Germany' => [$sut, 'DE89 3704 0044 0532 0130 00'],
|
||||
'Greece' => [$sut, 'GR96 0810 0010 0000 0123 4567 890'],
|
||||
'Romania' => [$sut, 'RO09 BCYP 0000 0012 3456 7890'],
|
||||
'Saudi Arabia' => [$sut, 'SA44 2000 0001 2345 6789 1234'],
|
||||
'Spain' => [$sut, 'ES79 2100 0813 6101 2345 6789'],
|
||||
'Sweden' => [$sut, 'SE35 5000 0000 0549 1000 0003'],
|
||||
'Switzerland' => [$sut, 'CH56 0483 5012 3456 7800 9'],
|
||||
'Switzerland without spaces' => [$sut, 'CH9300762011623852957'],
|
||||
'United Kingdom' => [$sut, 'GB98 MIDL 0700 9312 3456 78'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function providerForInvalidInput(): array
|
||||
{
|
||||
$sut = new Iban();
|
||||
|
||||
return [
|
||||
'Array' => [$sut, []],
|
||||
'Number' => [$sut, 123456789],
|
||||
'Bool' => [$sut, true],
|
||||
'Object' => [$sut, new stdClass()],
|
||||
'Empty' => [$sut, ''],
|
||||
'Alpha' => [$sut, 'ABCDEFGHIKLMNOPQRSTVXYZ'],
|
||||
'Symbols' => [$sut, '&"\'(-_)@-*/+.'],
|
||||
'SwedenWrong' => [$sut, 'SE35 5000 5880 7742'],
|
||||
'SwitzerlandWrong' => [$sut, 'CH93 5000 5880 7742'],
|
||||
'HungaryWrong' => [$sut, 'HU42 5000 5880 7742'],
|
||||
'GermanydWrong' => [$sut, 'DE89 5000 5880 7742'],
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue