web/lib/Zend/Validate/CreditCard.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Validate
       
    17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    19  * @version    $Id: CreditCard.php 22668 2010-07-25 14:50:46Z thomas $
       
    20  */
       
    21 
       
    22 /**
       
    23  * @see Zend_Validate_Abstract
       
    24  */
       
    25 require_once 'Zend/Validate/Abstract.php';
       
    26 
       
    27 /**
       
    28  * @category   Zend
       
    29  * @package    Zend_Validate
       
    30  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    31  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    32  */
       
    33 class Zend_Validate_CreditCard extends Zend_Validate_Abstract
       
    34 {
       
    35     /**
       
    36      * Detected CCI list
       
    37      *
       
    38      * @var string
       
    39      */
       
    40     const ALL              = 'All';
       
    41     const AMERICAN_EXPRESS = 'American_Express';
       
    42     const UNIONPAY         = 'Unionpay';
       
    43     const DINERS_CLUB      = 'Diners_Club';
       
    44     const DINERS_CLUB_US   = 'Diners_Club_US';
       
    45     const DISCOVER         = 'Discover';
       
    46     const JCB              = 'JCB';
       
    47     const LASER            = 'Laser';
       
    48     const MAESTRO          = 'Maestro';
       
    49     const MASTERCARD       = 'Mastercard';
       
    50     const SOLO             = 'Solo';
       
    51     const VISA             = 'Visa';
       
    52 
       
    53     const CHECKSUM       = 'creditcardChecksum';
       
    54     const CONTENT        = 'creditcardContent';
       
    55     const INVALID        = 'creditcardInvalid';
       
    56     const LENGTH         = 'creditcardLength';
       
    57     const PREFIX         = 'creditcardPrefix';
       
    58     const SERVICE        = 'creditcardService';
       
    59     const SERVICEFAILURE = 'creditcardServiceFailure';
       
    60 
       
    61     /**
       
    62      * Validation failure message template definitions
       
    63      *
       
    64      * @var array
       
    65      */
       
    66     protected $_messageTemplates = array(
       
    67         self::CHECKSUM       => "'%value%' seems to contain an invalid checksum",
       
    68         self::CONTENT        => "'%value%' must contain only digits",
       
    69         self::INVALID        => "Invalid type given. String expected",
       
    70         self::LENGTH         => "'%value%' contains an invalid amount of digits",
       
    71         self::PREFIX         => "'%value%' is not from an allowed institute",
       
    72         self::SERVICE        => "'%value%' seems to be an invalid creditcard number",
       
    73         self::SERVICEFAILURE => "An exception has been raised while validating '%value%'",
       
    74     );
       
    75 
       
    76     /**
       
    77      * List of allowed CCV lengths
       
    78      *
       
    79      * @var array
       
    80      */
       
    81     protected $_cardLength = array(
       
    82         self::AMERICAN_EXPRESS => array(15),
       
    83         self::DINERS_CLUB      => array(14),
       
    84         self::DINERS_CLUB_US   => array(16),
       
    85         self::DISCOVER         => array(16),
       
    86         self::JCB              => array(16),
       
    87         self::LASER            => array(16, 17, 18, 19),
       
    88         self::MAESTRO          => array(12, 13, 14, 15, 16, 17, 18, 19),
       
    89         self::MASTERCARD       => array(16),
       
    90         self::SOLO             => array(16, 18, 19),
       
    91         self::UNIONPAY         => array(16, 17, 18, 19),
       
    92         self::VISA             => array(16),
       
    93     );
       
    94 
       
    95     /**
       
    96      * List of accepted CCV provider tags
       
    97      *
       
    98      * @var array
       
    99      */
       
   100     protected $_cardType = array(
       
   101         self::AMERICAN_EXPRESS => array('34', '37'),
       
   102         self::DINERS_CLUB      => array('300', '301', '302', '303', '304', '305', '36'),
       
   103         self::DINERS_CLUB_US   => array('54', '55'),
       
   104         self::DISCOVER         => array('6011', '622126', '622127', '622128', '622129', '62213',
       
   105                                         '62214', '62215', '62216', '62217', '62218', '62219',
       
   106                                         '6222', '6223', '6224', '6225', '6226', '6227', '6228',
       
   107                                         '62290', '62291', '622920', '622921', '622922', '622923',
       
   108                                         '622924', '622925', '644', '645', '646', '647', '648',
       
   109                                         '649', '65'),
       
   110         self::JCB              => array('3528', '3529', '353', '354', '355', '356', '357', '358'),
       
   111         self::LASER            => array('6304', '6706', '6771', '6709'),
       
   112         self::MAESTRO          => array('5018', '5020', '5038', '6304', '6759', '6761', '6763'),
       
   113         self::MASTERCARD       => array('51', '52', '53', '54', '55'),
       
   114         self::SOLO             => array('6334', '6767'),
       
   115         self::UNIONPAY         => array('622126', '622127', '622128', '622129', '62213', '62214',
       
   116                                         '62215', '62216', '62217', '62218', '62219', '6222', '6223',
       
   117                                         '6224', '6225', '6226', '6227', '6228', '62290', '62291',
       
   118                                         '622920', '622921', '622922', '622923', '622924', '622925'),
       
   119         self::VISA             => array('4'),
       
   120     );
       
   121 
       
   122     /**
       
   123      * CCIs which are accepted by validation
       
   124      *
       
   125      * @var array
       
   126      */
       
   127     protected $_type = array();
       
   128 
       
   129     /**
       
   130      * Service callback for additional validation
       
   131      *
       
   132      * @var callback
       
   133      */
       
   134     protected $_service;
       
   135 
       
   136     /**
       
   137      * Constructor
       
   138      *
       
   139      * @param string|array $type OPTIONAL Type of CCI to allow
       
   140      */
       
   141     public function __construct($options = array())
       
   142     {
       
   143         if ($options instanceof Zend_Config) {
       
   144             $options = $options->toArray();
       
   145         } else if (!is_array($options)) {
       
   146             $options = func_get_args();
       
   147             $temp['type'] = array_shift($options);
       
   148             if (!empty($options)) {
       
   149                 $temp['service'] = array_shift($options);
       
   150             }
       
   151 
       
   152             $options = $temp;
       
   153         }
       
   154 
       
   155         if (!array_key_exists('type', $options)) {
       
   156             $options['type'] = self::ALL;
       
   157         }
       
   158 
       
   159         $this->setType($options['type']);
       
   160         if (array_key_exists('service', $options)) {
       
   161             $this->setService($options['service']);
       
   162         }
       
   163     }
       
   164 
       
   165     /**
       
   166      * Returns a list of accepted CCIs
       
   167      *
       
   168      * @return array
       
   169      */
       
   170     public function getType()
       
   171     {
       
   172         return $this->_type;
       
   173     }
       
   174 
       
   175     /**
       
   176      * Sets CCIs which are accepted by validation
       
   177      *
       
   178      * @param string|array $type Type to allow for validation
       
   179      * @return Zend_Validate_CreditCard Provides a fluid interface
       
   180      */
       
   181     public function setType($type)
       
   182     {
       
   183         $this->_type = array();
       
   184         return $this->addType($type);
       
   185     }
       
   186 
       
   187     /**
       
   188      * Adds a CCI to be accepted by validation
       
   189      *
       
   190      * @param string|array $type Type to allow for validation
       
   191      * @return Zend_Validate_CreditCard Provides a fluid interface
       
   192      */
       
   193     public function addType($type)
       
   194     {
       
   195         if (is_string($type)) {
       
   196             $type = array($type);
       
   197         }
       
   198 
       
   199         foreach($type as $typ) {
       
   200             if (defined('self::' . strtoupper($typ)) && !in_array($typ, $this->_type)) {
       
   201                 $this->_type[] = $typ;
       
   202             }
       
   203 
       
   204             if (($typ == self::ALL)) {
       
   205                 $this->_type = array_keys($this->_cardLength);
       
   206             }
       
   207         }
       
   208 
       
   209         return $this;
       
   210     }
       
   211 
       
   212     /**
       
   213      * Returns the actual set service
       
   214      *
       
   215      * @return callback
       
   216      */
       
   217     public function getService()
       
   218     {
       
   219         return $this->_service;
       
   220     }
       
   221 
       
   222     /**
       
   223      * Sets a new callback for service validation
       
   224      *
       
   225      * @param unknown_type $service
       
   226      */
       
   227     public function setService($service)
       
   228     {
       
   229         if (!is_callable($service)) {
       
   230             require_once 'Zend/Validate/Exception.php';
       
   231             throw new Zend_Validate_Exception('Invalid callback given');
       
   232         }
       
   233 
       
   234         $this->_service = $service;
       
   235         return $this;
       
   236     }
       
   237 
       
   238     /**
       
   239      * Defined by Zend_Validate_Interface
       
   240      *
       
   241      * Returns true if and only if $value follows the Luhn algorithm (mod-10 checksum)
       
   242      *
       
   243      * @param  string $value
       
   244      * @return boolean
       
   245      */
       
   246     public function isValid($value)
       
   247     {
       
   248         $this->_setValue($value);
       
   249 
       
   250         if (!is_string($value)) {
       
   251             $this->_error(self::INVALID, $value);
       
   252             return false;
       
   253         }
       
   254 
       
   255         if (!ctype_digit($value)) {
       
   256             $this->_error(self::CONTENT, $value);
       
   257             return false;
       
   258         }
       
   259 
       
   260         $length = strlen($value);
       
   261         $types  = $this->getType();
       
   262         $foundp = false;
       
   263         $foundl = false;
       
   264         foreach ($types as $type) {
       
   265             foreach ($this->_cardType[$type] as $prefix) {
       
   266                 if (substr($value, 0, strlen($prefix)) == $prefix) {
       
   267                     $foundp = true;
       
   268                     if (in_array($length, $this->_cardLength[$type])) {
       
   269                         $foundl = true;
       
   270                         break 2;
       
   271                     }
       
   272                 }
       
   273             }
       
   274         }
       
   275 
       
   276         if ($foundp == false){
       
   277             $this->_error(self::PREFIX, $value);
       
   278             return false;
       
   279         }
       
   280 
       
   281         if ($foundl == false) {
       
   282             $this->_error(self::LENGTH, $value);
       
   283             return false;
       
   284         }
       
   285 
       
   286         $sum    = 0;
       
   287         $weight = 2;
       
   288 
       
   289         for ($i = $length - 2; $i >= 0; $i--) {
       
   290             $digit = $weight * $value[$i];
       
   291             $sum += floor($digit / 10) + $digit % 10;
       
   292             $weight = $weight % 2 + 1;
       
   293         }
       
   294 
       
   295         if ((10 - $sum % 10) % 10 != $value[$length - 1]) {
       
   296             $this->_error(self::CHECKSUM, $value);
       
   297             return false;
       
   298         }
       
   299 
       
   300         if (!empty($this->_service)) {
       
   301             try {
       
   302                 require_once 'Zend/Validate/Callback.php';
       
   303                 $callback = new Zend_Validate_Callback($this->_service);
       
   304                 $callback->setOptions($this->_type);
       
   305                 if (!$callback->isValid($value)) {
       
   306                     $this->_error(self::SERVICE, $value);
       
   307                     return false;
       
   308                 }
       
   309             } catch (Zend_Exception $e) {
       
   310                 $this->_error(self::SERVICEFAILURE, $value);
       
   311                 return false;
       
   312             }
       
   313         }
       
   314 
       
   315         return true;
       
   316     }
       
   317 }