Isbn.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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$
  20. */
  21. /**
  22. * @see Zend_Validate_Abstract
  23. */
  24. require_once 'Zend/Validate/Abstract.php';
  25. /**
  26. * @category Zend
  27. * @package Zend_Validate
  28. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  29. * @license http://framework.zend.com/license/new-bsd New BSD License
  30. */
  31. class Zend_Validate_Isbn extends Zend_Validate_Abstract
  32. {
  33. const AUTO = 'auto';
  34. const ISBN10 = '10';
  35. const ISBN13 = '13';
  36. const INVALID = 'isbnInvalid';
  37. /**
  38. * Validation failure message template definitions.
  39. *
  40. * @var array
  41. */
  42. protected $_messageTemplates = array(
  43. self::INVALID => "'%value%' is no valid ISBN number",
  44. );
  45. /**
  46. * Allowed type.
  47. *
  48. * @var string
  49. */
  50. protected $_type = self::AUTO;
  51. /**
  52. * Separator character.
  53. *
  54. * @var string
  55. */
  56. protected $_separator = '';
  57. /**
  58. * Set up options.
  59. *
  60. * @param Zend_Config|array $options
  61. * @throws Zend_Validate_Exception When $options is not valid
  62. * @return void
  63. */
  64. public function __construct($options = array())
  65. {
  66. // prepare options
  67. if ($options instanceof Zend_Config) {
  68. $options = $options->toArray();
  69. }
  70. if (!is_array($options)) {
  71. /**
  72. * @see Zend_Validate_Exception
  73. */
  74. require_once 'Zend/Validate/Exception.php';
  75. throw new Zend_Validate_Exception('Invalid options provided.');
  76. }
  77. // set type
  78. if (array_key_exists('type', $options)) {
  79. $this->setType($options['type']);
  80. }
  81. // set separator
  82. if (array_key_exists('separator', $options)) {
  83. $this->setSeparator($options['separator']);
  84. }
  85. }
  86. /**
  87. * Detect input format.
  88. *
  89. * @return string
  90. */
  91. protected function _detectFormat()
  92. {
  93. // prepare separator and pattern list
  94. $sep = quotemeta($this->_separator);
  95. $patterns = array();
  96. $lengths = array();
  97. // check for ISBN-10
  98. if ($this->_type == self::ISBN10 || $this->_type == self::AUTO) {
  99. if (empty($sep)) {
  100. $pattern = '/^[0-9]{9}[0-9X]{1}$/';
  101. $length = 10;
  102. } else {
  103. $pattern = "/^[0-9]{1,7}[{$sep}]{1}[0-9]{1,7}[{$sep}]{1}[0-9]{1,7}[{$sep}]{1}[0-9X]{1}$/";
  104. $length = 13;
  105. }
  106. $patterns[$pattern] = self::ISBN10;
  107. $lengths[$pattern] = $length;
  108. }
  109. // check for ISBN-13
  110. if ($this->_type == self::ISBN13 || $this->_type == self::AUTO) {
  111. if (empty($sep)) {
  112. $pattern = '/^[0-9]{13}$/';
  113. $length = 13;
  114. } else {
  115. $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}$/";
  116. $length = 17;
  117. }
  118. $patterns[$pattern] = self::ISBN13;
  119. $lengths[$pattern] = $length;
  120. }
  121. // check pattern list
  122. foreach ($patterns as $pattern => $type) {
  123. if ((strlen($this->_value) == $lengths[$pattern]) && preg_match($pattern, $this->_value)) {
  124. return $type;
  125. }
  126. }
  127. return null;
  128. }
  129. /**
  130. * Defined by Zend_Validate_Interface.
  131. *
  132. * Returns true if and only if $value contains a valid ISBN.
  133. *
  134. * @param string $value
  135. * @return boolean
  136. */
  137. public function isValid($value)
  138. {
  139. // save value
  140. $value = (string) $value;
  141. $this->_setValue($value);
  142. switch ($this->_detectFormat()) {
  143. case self::ISBN10:
  144. // sum
  145. $isbn10 = preg_replace('/[^0-9X]/', '', $value);
  146. $sum = 0;
  147. for ($i = 0; $i < 9; $i++) {
  148. $sum += (10 - $i) * $isbn10{$i};
  149. }
  150. // checksum
  151. $checksum = 11 - ($sum % 11);
  152. if ($checksum == 11) {
  153. $checksum = '0';
  154. } elseif ($checksum == 10) {
  155. $checksum = 'X';
  156. }
  157. break;
  158. case self::ISBN13:
  159. // sum
  160. $isbn13 = preg_replace('/[^0-9]/', '', $value);
  161. $sum = 0;
  162. for ($i = 0; $i < 12; $i++) {
  163. if ($i % 2 == 0) {
  164. $sum += $isbn13{$i};
  165. } else {
  166. $sum += 3 * $isbn13{$i};
  167. }
  168. }
  169. // checksum
  170. $checksum = 10 - ($sum % 10);
  171. if ($checksum == 10) {
  172. $checksum = '0';
  173. }
  174. break;
  175. default:
  176. $this->_error(self::INVALID);
  177. return false;
  178. }
  179. // validate
  180. if (substr($this->_value, -1) != $checksum) {
  181. $this->_error(self::INVALID);
  182. return false;
  183. }
  184. return true;
  185. }
  186. /**
  187. * Set separator characters.
  188. *
  189. * It is allowed only empty string, hyphen and space.
  190. *
  191. * @param string $separator
  192. * @throws Zend_Validate_Exception When $separator is not valid
  193. * @return Zend_Validate_Isbn Provides a fluent interface
  194. */
  195. public function setSeparator($separator)
  196. {
  197. // check separator
  198. if (!in_array($separator, array('-', ' ', ''))) {
  199. /**
  200. * @see Zend_Validate_Exception
  201. */
  202. require_once 'Zend/Validate/Exception.php';
  203. throw new Zend_Validate_Exception('Invalid ISBN separator.');
  204. }
  205. $this->_separator = $separator;
  206. return $this;
  207. }
  208. /**
  209. * Get separator characters.
  210. *
  211. * @return string
  212. */
  213. public function getSeparator()
  214. {
  215. return $this->_separator;
  216. }
  217. /**
  218. * Set allowed ISBN type.
  219. *
  220. * @param string $type
  221. * @throws Zend_Validate_Exception When $type is not valid
  222. * @return Zend_Validate_Isbn Provides a fluent interface
  223. */
  224. public function setType($type)
  225. {
  226. // check type
  227. if (!in_array($type, array(self::AUTO, self::ISBN10, self::ISBN13))) {
  228. /**
  229. * @see Zend_Validate_Exception
  230. */
  231. require_once 'Zend/Validate/Exception.php';
  232. throw new Zend_Validate_Exception('Invalid ISBN type');
  233. }
  234. $this->_type = $type;
  235. return $this;
  236. }
  237. /**
  238. * Get allowed ISBN type.
  239. *
  240. * @return string
  241. */
  242. public function getType()
  243. {
  244. return $this->_type;
  245. }
  246. }