Implement iban datatype

This commit is contained in:
Dennis Eichhorn 2016-06-11 09:47:02 +02:00
parent 1c5f0026de
commit d7b00355ce
7 changed files with 445 additions and 34 deletions

View File

@ -45,9 +45,7 @@ abstract class Enum
*/
public static function isValidName(string $name) : bool
{
$constants = self::getConstants();
return isset($constants[$name]);
return defined($name);
}
/**
@ -99,4 +97,25 @@ abstract class Enum
return $constants[mt_rand(0, count($constants))];
}
/**
* Get enum value by name.
*
* @param string $name Enum name
*
* @return mixed
*
* @throws \Exception
*
* @since 1.0.0
* @author Dennis Eichhorn <d.eichhorn@oms.com>
*/
public static function getByName(string $name)
{
if(!self::isValidName($name)) {
throw new \Exception('Undefined constant');
}
return constant($name);
}
}

172
Datatypes/Iban.php Normal file
View File

@ -0,0 +1,172 @@
<?php
/**
* Orange Management
*
* PHP Version 7.0
*
* @category TBD
* @package TBD
* @author OMS Development Team <dev@oms.com>
* @author Dennis Eichhorn <d.eichhorn@oms.com>
* @copyright 2013 Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link http://orange-management.com
*/
namespace phpOMS\Datatypes;
use phpOMS\Localization\ISO3166TwoEnum;
use phpOMS\Validation\Base\IbanEnum;
/**
* Iban class.
*
* @category Framework
* @package phpOMS\Datatypes
* @author OMS Development Team <dev@oms.com>
* @author Dennis Eichhorn <d.eichhorn@oms.com>
* @license OMS License 1.0
* @link http://orange-management.com
* @since 1.0.0
*
* @todo : there is a bug with Hungary ibans since they have two k (checksums) in their definition
*/
class Iban implements \Serializable
{
private $iban = '';
public function __construct(string $iban)
{
$this->parse($iban);
}
private function parse(string $iban)
{
if (!\phpOMS\Validation\Base\Iban::isValid($iban)) {
throw new \InvalidArgumentException('Invalid IBAN');
}
$this->iban = self::normalize($iban);
}
public function getCountry() : string
{
$code = substr($this->iban, 0, 2);
if (!ISO3166TwoEnum::isValidValue($code)) {
throw new \Exception('Invalid country code');
}
return $code;
}
public function getLength() : int
{
return strlen($this->iban);
}
private function getSequence(string $sequence) : string
{
$country = $this->getCountry();
$layout = str_replace(' ', '', IbanEnum::getByName('C_' . $country));
$start = stripos($layout, $sequence);
$end = strrpos($layout, $sequence);
if ($start === false) {
return '';
}
return substr($this->iban, $start, $end - $start);
}
public function getChecksum() : string
{
return $this->getSequence('k');
}
public function getNationalChecksum() : string
{
return $this->getSequence('x');
}
public function getBranchCode() : string
{
return $this->getSequence('s');
}
public function getAccountType() : string
{
return $this->getSequence('t');
}
public function getCurrency() : string
{
return $this->getSequence('m');
}
public function getBicBankCode() : string
{
return $this->getSequence('a');
}
public function getBankCode() : string
{
return $this->getSequence('b');
}
public function getAccount() : string
{
return $this->getSequence('n');
}
public function getHoldersKennital() : string
{
return $this->getSequence('i');
}
public function getOwnerAccountNumber() : string
{
return $this->getSequence('n');
}
public function getBicCode() : string
{
return $this->getSequence('a');
}
public function prettyPrint() : string
{
return wordwrap($this->iban, 4, ' ', true);
}
public static function normalize(string $iban) : string
{
return strtoupper(str_replace(' ', '', $iban));
}
/**
* String representation of object
* @link http://php.net/manual/en/serializable.serialize.php
* @return string the string representation of the object or null
* @since 5.1.0
*/
public function serialize()
{
return $this->prettyPrint();
}
/**
* Constructs the object
* @link http://php.net/manual/en/serializable.unserialize.php
* @param string $serialized <p>
* The string representation of the object.
* </p>
* @return void
* @since 5.1.0
*/
public function unserialize($serialized)
{
$this->parse($serialized);
}
}

View File

@ -279,4 +279,5 @@ class ISO3166NameEnum extends Enum
const C_YEM = 'Yemen';
const C_ZMB = 'Zambia';
const C_ZWE = 'Zimbabwe';
const C_XKK = 'Kosovo';
}

View File

@ -279,4 +279,5 @@ class ISO3166TwoEnum extends EnumArray
const C_YEM = 'YE';
const C_ZMB = 'ZM';
const C_ZWE = 'ZW';
const C_XKK = 'XK';
}

View File

@ -13,7 +13,8 @@
* @version 1.0.0
* @link http://orange-management.com
*/
namespace phpOMS\Validation;
namespace phpOMS\Validation\Base;
use phpOMS\Validation\ValidatorAbstract;
/**
@ -27,9 +28,8 @@ namespace phpOMS\Validation;
* @link http://orange-management.com
* @since 1.0.0
*/
abstract class IBAN extends ValidatorAbstract
abstract class Iban extends ValidatorAbstract
{
/**
* Constructor.
*
@ -44,38 +44,104 @@ abstract class IBAN extends ValidatorAbstract
* {@inheritdoc}
*/
// todo: this is bad see wiki for better checks...
public static function isValid($value)
public static function isValid($value) : bool
{
$value = strtolower(str_replace(' ', '', $value));
$Countries = ['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, 'mc' => 27, 'md' => 24, 'me' => 22, 'nl' => 18, 'no' => 15, 'pk' => 24,
'ps' => 29, 'pl' => 28, 'pt' => 25, 'qa' => 29, 'ro' => 24, 'sm' => 27, 'sa' => 24, 'rs' => 22,
'sk' => 24, 'si' => 19, 'es' => 24, 'se' => 24, 'ch' => 21, 'tn' => 24, 'tr' => 26, 'ae' => 23,
'gb' => 22, 'vg' => 24,];
$Chars = ['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17, 'i' => 18,
'j' => 19, 'k' => 20, 'l' => 21, 'm' => 22, 'n' => 23, 'o' => 24, 'p' => 25, 'q' => 26, 'r' => 27,
's' => 28, 't' => 29, 'u' => 30, 'v' => 31, 'w' => 32, 'x' => 33, 'y' => 34, 'z' => 35,];
$value = \phpOMS\Datatypes\Iban::normalize($value);
$layout = str_replace(' ', '', IbanEnum::getByName($enumName = 'C_' . strtoupper(substr($value, 0, 2))));
if (strlen($value) == $Countries[substr($value, 0, 2)]) {
$MovedChar = substr($value, 4) . substr($value, 0, 4);
$MovedCharArray = str_split($MovedChar);
$NewString = '';
if (!IbanEnum::isValidName($enumName)) {
self::$error = IbanErrorType::INVALID_COUNTRY;
foreach ($MovedCharArray as $key => $value) {
if (!is_numeric($MovedCharArray[$key])) {
$MovedCharArray[$key] = $Chars[$MovedCharArray[$key]];
}
$NewString .= $MovedCharArray[$key];
}
return bcmod($NewString, '97') == 1;
} else {
return false;
}
if (strlen($value) !== strlen($layout)) {
self::$error = IbanErrorType::INVALID_LENGTH;
return false;
}
if (!self::validateZeros($value, $layout)) {
self::$error = IbanErrorType::EXPECTED_ZERO;
return false;
}
if (!self::validateNumeric($value, $layout)) {
self::$error = IbanErrorType::EXPECTED_NUMERIC;
return false;
}
if (!self::validateChecksum($value)) {
self::$error = IbanErrorType::INVALID_CHECKSUM;
return false;
}
return true;
}
private static function validateChecksum(string $iban) : bool
{
$chars = ['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17, 'i' => 18,
'j' => 19, 'k' => 20, 'l' => 21, 'm' => 22, 'n' => 23, 'o' => 24, 'p' => 25, 'q' => 26, 'r' => 27,
's' => 28, 't' => 29, 'u' => 30, 'v' => 31, 'w' => 32, 'x' => 33, 'y' => 34, 'z' => 35,];
$moved = substr($iban, 4) . substr($iban, 0, 4);
$movedArray = str_split($moved);
$new = '';
// performing checksum calculation
foreach ($movedArray as $key => $value) {
if (!is_numeric($movedArray[$key])) {
$movedArray[$key] = $chars[$movedArray[$key]];
}
$new .= $movedArray[$key];
}
$mod = bcmod($new, '97');
if ($mod != 1) {
self::$error = IbanErrorType::INVALID_CHECKSUM;
}
return $mod == 1;
}
private static function validateZeros(string $iban, string $layout) : bool
{
if (strpos($layout, '0') === false) {
return true;
}
$lastPos = 0;
while (($lastPos = strpos($layout, '0', $lastPos)) !== false) {
if ($iban[$lastPos] !== '0') {
return false;
}
$lastPos += 1;
}
return true;
}
private static function validateNumeric(string $iban, string $layout) : bool
{
if (strpos($layout, 'n') === false) {
return true;
}
$lastPos = 0;
while (($lastPos = strpos($layout, 'n', $lastPos)) !== false) {
if (!is_numeric($iban[$lastPos])) {
return false;
}
$lastPos += 1;
}
return true;
}
}

View File

@ -0,0 +1,115 @@
<?php
/**
* Orange Management
*
* PHP Version 7.0
*
* @category TBD
* @package TBD
* @author OMS Development Team <dev@oms.com>
* @author Dennis Eichhorn <d.eichhorn@oms.com>
* @copyright 2013 Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link http://orange-management.com
*/
namespace phpOMS\Validation\Base;
use phpOMS\Datatypes\Enum;
/**
* Country codes ISO list.
*
* @category Framework
* @package phpOMS\Localization
* @author OMS Development Team <dev@oms.com>
* @author Dennis Eichhorn <d.eichhorn@oms.com>
* @license OMS License 1.0
* @link http://orange-management.com
* @since 1.0.0
*/
class IbanEnum extends Enum
{
const C_AL = 'ALkk bbbs sssx cccc cccc cccc cccc';
const C_AD = 'ADkk bbbb ssss cccc cccc cccc';
const C_AT = 'ATkk bbbb bccc cccc cccc';
const C_AZ = 'AZkk bbbb cccc cccc cccc cccc cccc ';
const C_BH = 'BHkk bbbb cccc cccc cccc cc';
const C_BE = 'BEkk bbbc cccc ccxx';
const C_BA = 'BAkk bbbs sscc cccc ccxx';
const C_BR = 'BRkk bbbb bbbb ssss sccc cccc ccct n';
const C_BG = 'BGkk bbbb ssss ttcc cccc cc';
const C_CR = 'CRkk bbbc cccc cccc cccc c';
const C_HR = 'HRkk bbbb bbbc cccc cccc c';
const C_CY = 'CYkk bbbs ssss cccc cccc cccc cccc';
const C_CZ = 'CZkk bbbb ssss sscc cccc cccc';
const C_DK = 'DKkk bbbb cccc cccc cc';
const C_DO = 'DOkk bbbb cccc cccc cccc cccc cccc';
const C_TL = 'TLkk bbbc cccc cccc cccc cxx';
const C_EE = 'EEkk bbss cccc cccc cccx';
const C_FO = 'FOkk bbbb cccc cccc cx';
const C_FI = 'FIkk bbbb bbcc cccc cx';
const C_FR = 'FRkk bbbb bsss sscc cccc cccc cxx';
const C_GE = 'GEkk bbcc cccc cccc cccc cc';
const C_DE = 'DEkk bbbb bbbb cccc cccc cc';
const C_GI = 'GIkk bbbb cccc cccc cccc ccc';
const C_GR = 'GRkk bbbs sssc cccc cccc cccc ccc';
const C_GL = 'GLkk bbbb cccc cccc cc';
const C_GT = 'GTkk bbbb mmtt cccc cccc cccc cccc';
const C_HU = 'HUkk bbbs sssk cccc cccc cccc cccx';
const C_IS = 'ISkk bbbb sscc cccc iiii iiii ii';
const C_IE = 'IEkk aaaa bbbb bbcc cccc cc';
const C_IL = 'ILkk bbbn nncc cccc cccc ccc';
const C_IT = 'ITkk xbbb bbss sssc cccc cccc ccc';
const C_JO = 'JOkk bbbb ssss cccc cccc cccc cccc cc';
const C_KZ = 'KZkk bbbc cccc cccc cccc';
const C_XK = 'XKkk bbbb cccc cccc cccc';
const C_KW = 'KWkk bbbb cccc cccc cccc cccc cccc cc';
const C_LV = 'LVkk bbbb cccc cccc cccc c';
const C_LB = 'LBkk bbbb cccc cccc cccc cccc cccc';
const C_LI = 'LIkk bbbb bccc cccc cccc c';
const C_LT = 'LTkk bbbb bccc cccc cccc';
const C_LU = 'LUkk bbbc cccc cccc cccc';
const C_MK = 'MKkk bbbc cccc cccc cxx';
const C_MT = 'MTkk bbbb ssss sccc cccc cccc cccc ccc';
const C_MR = 'MRkk bbbb bsss sscc cccc cccc cxx';
const C_MU = 'MUkk bbbb bbss cccc cccc cccc 000m mm';
const C_MC = 'MCkk bbbb bsss sscc cccc cccc cxx';
const C_MD = 'MDkk bbcc cccc cccc cccc cccc';
const C_ME = 'MEkk bbbc cccc cccc cccc xx';
const C_NL = 'NLkk bbbb cccc cccc cc';
const C_NO = 'NOkk bbbb cccc ccx';
const C_PK = 'PKkk bbbb cccc cccc cccc cccc';
const C_PS = 'PSkk bbbb xxxx xxxx xccc cccc cccc c';
const C_PL = 'PLkk bbbs sssx cccc cccc cccc cccc';
const C_PT = 'PTkk bbbb ssss cccc cccc cccx x';
const C_QA = 'QAkk bbbb cccc cccc cccc cccc cccc c';
const C_RO = 'ROkk bbbb cccc cccc cccc cccc';
const C_SM = 'SMkk xbbb bbss sssc cccc cccc ccc';
const C_SA = 'SAkk bbcc cccc cccc cccc cccc';
const C_RS = 'RSkk bbbc cccc cccc cccc xx';
const C_SK = 'SKkk bbbb ssss sscc cccc cccc';
const C_SI = 'SIkk bbss sccc cccc cxx';
const C_ES = 'ESkk bbbb ssss xxcc cccc cccc';
const C_SE = 'SEkk bbbc cccc cccc cccc cccc';
const C_CH = 'CHkk bbbb bccc cccc cccc c';
const C_TN = 'TNkk bbss sccc cccc cccc cccc';
const C_TR = 'TRkk bbbb bxcc cccc cccc cccc cc';
const C_UA = 'UAkk bbbb bbcc cccc cccc cccc cccc c';
const C_AE = 'AEkk bbbc cccc cccc cccc ccc';
const C_GB = 'GBkk bbbb ssss sscc cccc cc';
const C_VG = 'VGkk bbbb cccc cccc cccc cccc';
const C_SN = 'SNkk annn nnnn nnnn nnnn nnnn nnnn';
const C_MZ = 'MZkk nnnn nnnn nnnn nnnn nnnn n';
const C_ML = 'MLkk annn nnnn nnnn nnnn nnnn nnnn';
const C_MG = 'MGkk nnnn nnnn nnnn nnnn nnnn nnn';
const C_CI = 'CIkk annn nnnn nnnn nnnn nnnn nnnn';
const C_IR = 'IRkk nnnn nnnn nnnn nnnn nnnn nn';
const C_CV = 'CVkk nnnn nnnn nnnn nnnn nnnn n';
const C_CM = 'CMkk nnnn nnnn nnnn nnnn nnnn nnn';
const C_BI = 'BIkk nnnn nnnn nnnn';
const C_BF = 'BFkk nnnn nnnn nnnn nnnn nnnn nnn';
const C_BJ = 'BJkk annn nnnn nnnn nnnn nnnn nnnn';
const C_AO = 'AOkk nnnn nnnn nnnn nnnn nnnn n';
const C_DZ = 'DZkk nnnn nnnn nnnn nnnn nnnn';
}

View File

@ -0,0 +1,37 @@
<?php
/**
* Orange Management
*
* PHP Version 7.0
*
* @category TBD
* @package TBD
* @author OMS Development Team <dev@oms.com>
* @author Dennis Eichhorn <d.eichhorn@oms.com>
* @copyright 2013 Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link http://orange-management.com
*/
namespace phpOMS\Validation\Base;
use phpOMS\Datatypes\Enum;
/**
* Address type enum.
*
* @category Framework
* @package phpOMS\Datatypes
* @author OMS Development Team <dev@oms.com>
* @author Dennis Eichhorn <d.eichhorn@oms.com>
* @license OMS License 1.0
* @link http://orange-management.com
* @since 1.0.0
*/
abstract class IbanErrorType extends Enum
{
const INVALID_COUNTRY = 1;
const INVALID_LENGTH = 2;
const INVALID_CHECKSUM = 3;
const EXPECTED_ZERO = 4;
const EXPECTED_NUMERIC = 5;
}