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  * @category   Zend
0029  * @package    Zend_Validate
0030  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0031  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0032  */
0033 class Zend_Validate_Isbn extends Zend_Validate_Abstract
0034 {
0035     const AUTO    = 'auto';
0036     const ISBN10  = '10';
0037     const ISBN13  = '13';
0038     const INVALID = 'isbnInvalid';
0039     const NO_ISBN = 'isbnNoIsbn';
0040 
0041     /**
0042      * Validation failure message template definitions.
0043      *
0044      * @var array
0045      */
0046     protected $_messageTemplates = array(
0047         self::INVALID => "Invalid type given. String or integer expected",
0048         self::NO_ISBN => "'%value%' is not a valid ISBN number",
0049     );
0050 
0051     /**
0052      * Allowed type.
0053      *
0054      * @var string
0055      */
0056     protected $_type = self::AUTO;
0057 
0058     /**
0059      * Separator character.
0060      *
0061      * @var string
0062      */
0063     protected $_separator = '';
0064 
0065     /**
0066      * Set up options.
0067      *
0068      * @param  Zend_Config|array $options
0069      * @throws Zend_Validate_Exception When $options is not valid
0070      */
0071     public function __construct($options = array())
0072     {
0073         // prepare options
0074         if ($options instanceof Zend_Config) {
0075             $options = $options->toArray();
0076         }
0077         if (!is_array($options)) {
0078             /**
0079              * @see Zend_Validate_Exception
0080              */
0081             // require_once 'Zend/Validate/Exception.php';
0082             throw new Zend_Validate_Exception('Invalid options provided.');
0083         }
0084 
0085         // set type
0086         if (array_key_exists('type', $options)) {
0087             $this->setType($options['type']);
0088         }
0089 
0090         // set separator
0091         if (array_key_exists('separator', $options)) {
0092             $this->setSeparator($options['separator']);
0093         }
0094     }
0095 
0096     /**
0097      * Detect input format.
0098      *
0099      * @return string
0100      */
0101     protected function _detectFormat()
0102     {
0103         // prepare separator and pattern list
0104         $sep      = quotemeta($this->_separator);
0105         $patterns = array();
0106         $lengths  = array();
0107 
0108         // check for ISBN-10
0109         if ($this->_type == self::ISBN10 || $this->_type == self::AUTO) {
0110             if (empty($sep)) {
0111                 $pattern = '/^[0-9]{9}[0-9X]{1}$/';
0112                 $length  = 10;
0113             } else {
0114                 $pattern = "/^[0-9]{1,7}[{$sep}]{1}[0-9]{1,7}[{$sep}]{1}[0-9]{1,7}[{$sep}]{1}[0-9X]{1}$/";
0115                 $length  = 13;
0116             }
0117 
0118             $patterns[$pattern] = self::ISBN10;
0119             $lengths[$pattern]  = $length;
0120         }
0121 
0122         // check for ISBN-13
0123         if ($this->_type == self::ISBN13 || $this->_type == self::AUTO) {
0124             if (empty($sep)) {
0125                 $pattern = '/^[0-9]{13}$/';
0126                 $length  = 13;
0127             } else {
0128                 $pattern = "/^[0-9]{1,9}[{$sep}]{1}[0-9]{1,5}[{$sep}]{1}[0-9]{1,9}[{$sep}]{1}[0-9]{1,9}[{$sep}]{1}[0-9]{1}$/";
0129                 $length  = 17;
0130             }
0131 
0132             $patterns[$pattern] = self::ISBN13;
0133             $lengths[$pattern]  = $length;
0134         }
0135 
0136         // check pattern list
0137         foreach ($patterns as $pattern => $type) {
0138             if ((strlen($this->_value) == $lengths[$pattern]) && preg_match($pattern, $this->_value)) {
0139                 return $type;
0140             }
0141         }
0142 
0143         return null;
0144     }
0145 
0146     /**
0147      * Defined by Zend_Validate_Interface.
0148      *
0149      * Returns true if and only if $value is a valid ISBN.
0150      *
0151      * @param  string $value
0152      * @return boolean
0153      */
0154     public function isValid($value)
0155     {
0156         if (!is_string($value) && !is_int($value)) {
0157             $this->_error(self::INVALID);
0158             return false;
0159         }
0160 
0161         $value = (string) $value;
0162         $this->_setValue($value);
0163 
0164         switch ($this->_detectFormat()) {
0165             case self::ISBN10:
0166                 // sum
0167                 $isbn10 = str_replace($this->_separator, '', $value);
0168                 $sum    = 0;
0169                 for ($i = 0; $i < 9; $i++) {
0170                     $sum += (10 - $i) * $isbn10{$i};
0171                 }
0172 
0173                 // checksum
0174                 $checksum = 11 - ($sum % 11);
0175                 if ($checksum == 11) {
0176                     $checksum = '0';
0177                 } elseif ($checksum == 10) {
0178                     $checksum = 'X';
0179                 }
0180                 break;
0181 
0182             case self::ISBN13:
0183                 // sum
0184                 $isbn13 = str_replace($this->_separator, '', $value);
0185                 $sum    = 0;
0186                 for ($i = 0; $i < 12; $i++) {
0187                     if ($i % 2 == 0) {
0188                         $sum += $isbn13{$i};
0189                     } else {
0190                         $sum += 3 * $isbn13{$i};
0191                     }
0192                 }
0193                 // checksum
0194                 $checksum = 10 - ($sum % 10);
0195                 if ($checksum == 10) {
0196                     $checksum = '0';
0197                 }
0198                 break;
0199 
0200             default:
0201                 $this->_error(self::NO_ISBN);
0202                 return false;
0203         }
0204 
0205         // validate
0206         if (substr($this->_value, -1) != $checksum) {
0207             $this->_error(self::NO_ISBN);
0208             return false;
0209         }
0210         return true;
0211     }
0212 
0213     /**
0214      * Set separator characters.
0215      *
0216      * It is allowed only empty string, hyphen and space.
0217      *
0218      * @param  string $separator
0219      * @throws Zend_Validate_Exception When $separator is not valid
0220      * @return Zend_Validate_Isbn Provides a fluent interface
0221      */
0222     public function setSeparator($separator)
0223     {
0224         // check separator
0225         if (!in_array($separator, array('-', ' ', ''))) {
0226             /**
0227              * @see Zend_Validate_Exception
0228              */
0229             // require_once 'Zend/Validate/Exception.php';
0230             throw new Zend_Validate_Exception('Invalid ISBN separator.');
0231         }
0232 
0233         $this->_separator = $separator;
0234         return $this;
0235     }
0236 
0237     /**
0238      * Get separator characters.
0239      *
0240      * @return string
0241      */
0242     public function getSeparator()
0243     {
0244         return $this->_separator;
0245     }
0246 
0247     /**
0248      * Set allowed ISBN type.
0249      *
0250      * @param  string $type
0251      * @throws Zend_Validate_Exception When $type is not valid
0252      * @return Zend_Validate_Isbn Provides a fluent interface
0253      */
0254     public function setType($type)
0255     {
0256         // check type
0257         if (!in_array($type, array(self::AUTO, self::ISBN10, self::ISBN13))) {
0258             /**
0259              * @see Zend_Validate_Exception
0260              */
0261             // require_once 'Zend/Validate/Exception.php';
0262             throw new Zend_Validate_Exception('Invalid ISBN type');
0263         }
0264 
0265         $this->_type = $type;
0266         return $this;
0267     }
0268 
0269     /**
0270      * Get allowed ISBN type.
0271      *
0272      * @return string
0273      */
0274     public function getType()
0275     {
0276         return $this->_type;
0277     }
0278 }