File indexing completed on 2024-12-22 05:37:12

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Validate
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /**
0023  * @see Zend_Validate_Abstract
0024  */
0025 // require_once 'Zend/Validate/Abstract.php';
0026 
0027 /**
0028  * Validates IBAN Numbers (International Bank Account Numbers)
0029  *
0030  * @category   Zend
0031  * @package    Zend_Validate
0032  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0033  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0034  */
0035 class Zend_Validate_Iban extends Zend_Validate_Abstract
0036 {
0037     const NOTSUPPORTED = 'ibanNotSupported';
0038     const FALSEFORMAT  = 'ibanFalseFormat';
0039     const CHECKFAILED  = 'ibanCheckFailed';
0040 
0041     /**
0042      * Validation failure message template definitions
0043      *
0044      * @var array
0045      */
0046     protected $_messageTemplates = array(
0047         self::NOTSUPPORTED => "Unknown country within the IBAN '%value%'",
0048         self::FALSEFORMAT  => "'%value%' has a false IBAN format",
0049         self::CHECKFAILED  => "'%value%' has failed the IBAN check",
0050     );
0051 
0052     /**
0053      * Optional locale
0054      *
0055      * @var string|Zend_Locale|null
0056      */
0057     protected $_locale;
0058 
0059     /**
0060      * IBAN regexes by region
0061      *
0062      * @var array
0063      */
0064     protected $_ibanregex = array(
0065         'AD' => '/^AD[0-9]{2}[0-9]{8}[A-Z0-9]{12}$/',
0066         'AE' => '/^AE[0-9]{2}[0-9]{3}[0-9]{16}$/',
0067         'AL' => '/^AL[0-9]{2}[0-9]{8}[A-Z0-9]{16}$/',
0068         'AT' => '/^AT[0-9]{2}[0-9]{5}[0-9]{11}$/',
0069         'AZ' => '/^AZ[0-9]{2}[0-9]{4}[A-Z0-9]{20}$/',
0070         'BA' => '/^BA[0-9]{2}[0-9]{6}[0-9]{10}$/',
0071         'BE' => '/^BE[0-9]{2}[0-9]{3}[0-9]{9}$/',
0072         'BG' => '/^BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Z0-9]{8}$/',
0073         'BH' => '/^BH[0-9]{2}[A-Z]{4}[A-Z0-9]{14}$/',
0074         'BR' => '/^BR[0-9]{2}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z]{1}[A-Z0-9]{1}$/',
0075         'CH' => '/^CH[0-9]{2}[0-9]{5}[A-Z0-9]{12}$/',
0076         'CR' => '/^CR[0-9]{2}[0-9]{3}[0-9]{14}$/',
0077         'CS' => '/^CS[0-9]{2}[0-9]{3}[0-9]{15}$/',
0078         'CY' => '/^CY[0-9]{2}[0-9]{8}[A-Z0-9]{16}$/',
0079         'CZ' => '/^CZ[0-9]{2}[0-9]{4}[0-9]{16}$/',
0080         'DE' => '/^DE[0-9]{2}[0-9]{8}[0-9]{10}$/',
0081         'DK' => '/^DK[0-9]{2}[0-9]{4}[0-9]{10}$/',
0082         'DO' => '/^DO[0-9]{2}[A-Z0-9]{4}[0-9]{20}$/',
0083         'EE' => '/^EE[0-9]{2}[0-9]{4}[0-9]{12}$/',
0084         'ES' => '/^ES[0-9]{2}[0-9]{8}[0-9]{12}$/',
0085         'FR' => '/^FR[0-9]{2}[0-9]{10}[A-Z0-9]{11}[0-9]{2}$/',
0086         'FI' => '/^FI[0-9]{2}[0-9]{6}[0-9]{8}$/',
0087         'FO' => '/^FO[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}$/',
0088         'GB' => '/^GB[0-9]{2}[A-Z]{4}[0-9]{14}$/',
0089         'GE' => '/^GE[0-9]{2}[A-Z]{2}[0-9]{16}$/',
0090         'GI' => '/^GI[0-9]{2}[A-Z]{4}[A-Z0-9]{15}$/',
0091         'GL' => '/^GL[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}$/',
0092         'GR' => '/^GR[0-9]{2}[0-9]{7}[A-Z0-9]{16}$/',
0093         'GT' => '/^GT[0-9]{2}[A-Z0-9]{4}[A-Z0-9]{20}$/',
0094         'HR' => '/^HR[0-9]{2}[0-9]{7}[0-9]{10}$/',
0095         'HU' => '/^HU[0-9]{2}[0-9]{7}[0-9]{1}[0-9]{15}[0-9]{1}$/',
0096         'IE' => '/^IE[0-9]{2}[A-Z0-9]{4}[0-9]{6}[0-9]{8}$/',
0097         'IL' => '/^IL[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{13}$/',
0098         'IS' => '/^IS[0-9]{2}[0-9]{4}[0-9]{18}$/',
0099         'IT' => '/^IT[0-9]{2}[A-Z]{1}[0-9]{10}[A-Z0-9]{12}$/',
0100         'KW' => '/^KW[0-9]{2}[A-Z]{4}[0-9]{3}[0-9]{22}$/',
0101         'KZ' => '/^KZ[A-Z]{2}[0-9]{2}[0-9]{3}[A-Z0-9]{13}$/',
0102         'LB' => '/^LB[0-9]{2}[0-9]{4}[A-Z0-9]{20}$/',
0103         'LI' => '/^LI[0-9]{2}[0-9]{5}[A-Z0-9]{12}$/',
0104         'LU' => '/^LU[0-9]{2}[0-9]{3}[A-Z0-9]{13}$/',
0105         'LT' => '/^LT[0-9]{2}[0-9]{5}[0-9]{11}$/',
0106         'LV' => '/^LV[0-9]{2}[A-Z]{4}[A-Z0-9]{13}$/',
0107         'MC' => '/^MC[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}$/',
0108         'MD' => '/^MD[0-9]{2}[A-Z0-9]{20}$/',
0109         'ME' => '/^ME[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}$/',
0110         'MK' => '/^MK[0-9]{2}[0-9]{3}[A-Z0-9]{10}[0-9]{2}$/',
0111         'MR' => '/^MR13[0-9]{5}[0-9]{5}[0-9]{11}[0-9]{2}$/',
0112         'MU' => '/^MU[0-9]{2}[A-Z]{4}[0-9]{2}[0-9]{2}[0-9]{12}[0-9]{3}[A-Z]{2}$/',
0113         'MT' => '/^MT[0-9]{2}[A-Z]{4}[0-9]{5}[A-Z0-9]{18}$/',
0114         'NL' => '/^NL[0-9]{2}[A-Z]{4}[0-9]{10}$/',
0115         'NO' => '/^NO[0-9]{2}[0-9]{4}[0-9]{7}$/',
0116         'PK' => '/^PK[0-9]{2}[A-Z]{4}[0-9]{16}$/',
0117         'PL' => '/^PL[0-9]{2}[0-9]{8}[0-9]{16}$/',
0118         'PS' => '/^PS[0-9]{2}[A-Z]{4}[0-9]{21}$/',
0119         'PT' => '/^PT[0-9]{2}[0-9]{8}[0-9]{13}$/',
0120         'RO' => '/^RO[0-9]{2}[A-Z]{4}[A-Z0-9]{16}$/',
0121         'RS' => '/^RS[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}$/',
0122         'SA' => '/^SA[0-9]{2}[0-9]{2}[A-Z0-9]{18}$/',
0123         'SE' => '/^SE[0-9]{2}[0-9]{3}[0-9]{17}$/',
0124         'SI' => '/^SI[0-9]{2}[0-9]{5}[0-9]{8}[0-9]{2}$/',
0125         'SK' => '/^SK[0-9]{2}[0-9]{4}[0-9]{16}$/',
0126         'SM' => '/^SM[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}$/',
0127         'TN' => '/^TN[0-9]{2}[0-9]{5}[0-9]{15}$/',
0128         'TR' => '/^TR[0-9]{2}[0-9]{5}[A-Z0-9]{17}$/',
0129         'VG' => '/^VG[0-9]{2}[A-Z]{4}[0-9]{16}$/'
0130     );
0131 
0132     /**
0133      * Sets validator options
0134      *
0135      * @param string|Zend_Config|Zend_Locale $locale OPTIONAL
0136      */
0137     public function __construct($locale = null)
0138     {
0139         if ($locale instanceof Zend_Config) {
0140             $locale = $locale->toArray();
0141         }
0142 
0143         if (is_array($locale)) {
0144             if (array_key_exists('locale', $locale)) {
0145                 $locale = $locale['locale'];
0146             } else {
0147                 $locale = null;
0148             }
0149         }
0150 
0151         if (empty($locale)) {
0152             // require_once 'Zend/Registry.php';
0153             if (Zend_Registry::isRegistered('Zend_Locale')) {
0154                 $locale = Zend_Registry::get('Zend_Locale');
0155             }
0156         }
0157 
0158         if ($locale !== null) {
0159             $this->setLocale($locale);
0160         }
0161     }
0162 
0163     /**
0164      * Returns the locale option
0165      *
0166      * @return string|Zend_Locale|null
0167      */
0168     public function getLocale()
0169     {
0170         return $this->_locale;
0171     }
0172 
0173     /**
0174      * Sets the locale option
0175      *
0176      * @param  string|Zend_Locale $locale
0177      * @throws Zend_Locale_Exception
0178      * @throws Zend_Validate_Exception
0179      * @return Zend_Validate_Date provides a fluent interface
0180      */
0181     public function setLocale($locale = null)
0182     {
0183         if ($locale !== false) {
0184             // require_once 'Zend/Locale.php';
0185             $locale = Zend_Locale::findLocale($locale);
0186             if (strlen($locale) < 4) {
0187                 // require_once 'Zend/Validate/Exception.php';
0188                 throw new Zend_Validate_Exception('Region must be given for IBAN validation');
0189             }
0190         }
0191 
0192         $this->_locale = $locale;
0193         return $this;
0194     }
0195 
0196     /**
0197      * Defined by Zend_Validate_Interface
0198      *
0199      * Returns true if $value is a valid IBAN
0200      *
0201      * @param  string $value
0202      * @return boolean
0203      */
0204     public function isValid($value)
0205     {
0206         $value = strtoupper($value);
0207         $this->_setValue($value);
0208 
0209         if (empty($this->_locale)) {
0210             $region = substr($value, 0, 2);
0211         } else {
0212             $region = new Zend_Locale($this->_locale);
0213             $region = $region->getRegion();
0214         }
0215 
0216         if (!array_key_exists($region, $this->_ibanregex)) {
0217             $this->_setValue($region);
0218             $this->_error(self::NOTSUPPORTED);
0219             return false;
0220         }
0221 
0222         if (!preg_match($this->_ibanregex[$region], $value)) {
0223             $this->_error(self::FALSEFORMAT);
0224             return false;
0225         }
0226 
0227         $format = substr($value, 4) . substr($value, 0, 4);
0228         $format = str_replace(
0229             array('A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',
0230                   'N',  'O',  'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',  'Y',  'Z'),
0231             array('10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22',
0232                   '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35'),
0233             $format);
0234 
0235         $temp = intval(substr($format, 0, 1));
0236         $len  = strlen($format);
0237         for ($x = 1; $x < $len; ++$x) {
0238             $temp *= 10;
0239             $temp += intval(substr($format, $x, 1));
0240             $temp %= 97;
0241         }
0242 
0243         if ($temp != 1) {
0244             $this->_error(self::CHECKFAILED);
0245             return false;
0246         }
0247 
0248         return true;
0249     }
0250 }