Word.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  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_Captcha
  17. * @subpackage Adapter
  18. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. /** @see Zend_Captcha_Base */
  22. require_once 'Zend/Captcha/Base.php';
  23. /** @see Zend_Crypt_Math */
  24. require_once 'Zend/Crypt/Math.php';
  25. /**
  26. * Word-based captcha adapter
  27. *
  28. * Generates random word which user should recognise
  29. *
  30. * @category Zend
  31. * @package Zend_Captcha
  32. * @subpackage Adapter
  33. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  34. * @license http://framework.zend.com/license/new-bsd New BSD License
  35. * @version $Id$
  36. */
  37. abstract class Zend_Captcha_Word extends Zend_Captcha_Base
  38. {
  39. /**#@+
  40. * @var array Character sets
  41. */
  42. static public $V = array("a", "e", "i", "o", "u", "y");
  43. static public $VN = array("a", "e", "i", "o", "u", "y","2","3","4","5","6","7","8","9");
  44. static public $C = array("b","c","d","f","g","h","j","k","m","n","p","q","r","s","t","u","v","w","x","z");
  45. static public $CN = array("b","c","d","f","g","h","j","k","m","n","p","q","r","s","t","u","v","w","x","z","2","3","4","5","6","7","8","9");
  46. /**#@-*/
  47. /**
  48. * Random session ID
  49. *
  50. * @var string
  51. */
  52. protected $_id;
  53. /**
  54. * Generated word
  55. *
  56. * @var string
  57. */
  58. protected $_word;
  59. /**
  60. * Session
  61. *
  62. * @var Zend_Session_Namespace
  63. */
  64. protected $_session;
  65. /**
  66. * Class name for sessions
  67. *
  68. * @var string
  69. */
  70. protected $_sessionClass = 'Zend_Session_Namespace';
  71. /**
  72. * Should the numbers be used or only letters
  73. *
  74. * @var boolean
  75. */
  76. protected $_useNumbers = true;
  77. /**
  78. * Should both cases be used or only lowercase
  79. *
  80. * @var boolean
  81. */
  82. // protected $_useCase = false;
  83. /**
  84. * Session lifetime for the captcha data
  85. *
  86. * @var integer
  87. */
  88. protected $_timeout = 300;
  89. /**
  90. * Should generate() keep session or create a new one?
  91. *
  92. * @var boolean
  93. */
  94. protected $_keepSession = false;
  95. /**#@+
  96. * Error codes
  97. */
  98. const MISSING_VALUE = 'missingValue';
  99. const MISSING_ID = 'missingID';
  100. const BAD_CAPTCHA = 'badCaptcha';
  101. /**#@-*/
  102. /**
  103. * Error messages
  104. * @var array
  105. */
  106. protected $_messageTemplates = array(
  107. self::MISSING_VALUE => 'Empty captcha value',
  108. self::MISSING_ID => 'Captcha ID field is missing',
  109. self::BAD_CAPTCHA => 'Captcha value is wrong',
  110. );
  111. /**
  112. * Length of the word to generate
  113. *
  114. * @var integer
  115. */
  116. protected $_wordlen = 8;
  117. /**
  118. * Retrieve session class to utilize
  119. *
  120. * @return string
  121. */
  122. public function getSessionClass()
  123. {
  124. return $this->_sessionClass;
  125. }
  126. /**
  127. * Set session class for persistence
  128. *
  129. * @param string $_sessionClass
  130. * @return Zend_Captcha_Word
  131. */
  132. public function setSessionClass($_sessionClass)
  133. {
  134. $this->_sessionClass = $_sessionClass;
  135. return $this;
  136. }
  137. /**
  138. * Retrieve word length to use when genrating captcha
  139. *
  140. * @return integer
  141. */
  142. public function getWordlen()
  143. {
  144. return $this->_wordlen;
  145. }
  146. /**
  147. * Set word length of captcha
  148. *
  149. * @param integer $wordlen
  150. * @return Zend_Captcha_Word
  151. */
  152. public function setWordlen($wordlen)
  153. {
  154. $this->_wordlen = $wordlen;
  155. return $this;
  156. }
  157. /**
  158. * Retrieve captcha ID
  159. *
  160. * @return string
  161. */
  162. public function getId()
  163. {
  164. if (null === $this->_id) {
  165. $this->_setId($this->_generateRandomId());
  166. }
  167. return $this->_id;
  168. }
  169. /**
  170. * Set captcha identifier
  171. *
  172. * @param string $id
  173. * @return Zend_Captcha_Word
  174. */
  175. protected function _setId($id)
  176. {
  177. $this->_id = $id;
  178. return $this;
  179. }
  180. /**
  181. * Set timeout for session token
  182. *
  183. * @param int $ttl
  184. * @return Zend_Captcha_Word
  185. */
  186. public function setTimeout($ttl)
  187. {
  188. $this->_timeout = (int) $ttl;
  189. return $this;
  190. }
  191. /**
  192. * Get session token timeout
  193. *
  194. * @return int
  195. */
  196. public function getTimeout()
  197. {
  198. return $this->_timeout;
  199. }
  200. /**
  201. * Sets if session should be preserved on generate()
  202. *
  203. * @param bool $keepSession Should session be kept on generate()?
  204. * @return Zend_Captcha_Word
  205. */
  206. public function setKeepSession($keepSession)
  207. {
  208. $this->_keepSession = $keepSession;
  209. return $this;
  210. }
  211. /**
  212. * Numbers should be included in the pattern?
  213. *
  214. * @return bool
  215. */
  216. public function getUseNumbers()
  217. {
  218. return $this->_useNumbers;
  219. }
  220. /**
  221. * Set if numbers should be included in the pattern
  222. *
  223. * @param bool $_useNumbers numbers should be included in the pattern?
  224. * @return Zend_Captcha_Word
  225. */
  226. public function setUseNumbers($_useNumbers)
  227. {
  228. $this->_useNumbers = $_useNumbers;
  229. return $this;
  230. }
  231. /**
  232. * Get session object
  233. *
  234. * @return Zend_Session_Namespace
  235. */
  236. public function getSession()
  237. {
  238. if (!isset($this->_session) || (null === $this->_session)) {
  239. $id = $this->getId();
  240. if (!class_exists($this->_sessionClass)) {
  241. require_once 'Zend/Loader.php';
  242. Zend_Loader::loadClass($this->_sessionClass);
  243. }
  244. $this->_session = new $this->_sessionClass('Zend_Form_Captcha_' . $id);
  245. $this->_session->setExpirationHops(1, null, true);
  246. $this->_session->setExpirationSeconds($this->getTimeout());
  247. }
  248. return $this->_session;
  249. }
  250. /**
  251. * Set session namespace object
  252. *
  253. * @param Zend_Session_Namespace $session
  254. * @return Zend_Captcha_Word
  255. */
  256. public function setSession(Zend_Session_Namespace $session)
  257. {
  258. $this->_session = $session;
  259. if ($session) {
  260. $this->_keepSession = true;
  261. }
  262. return $this;
  263. }
  264. /**
  265. * Get captcha word
  266. *
  267. * @return string
  268. */
  269. public function getWord()
  270. {
  271. if (empty($this->_word)) {
  272. $session = $this->getSession();
  273. $this->_word = $session->word;
  274. }
  275. return $this->_word;
  276. }
  277. /**
  278. * Set captcha word
  279. *
  280. * @param string $word
  281. * @return Zend_Captcha_Word
  282. */
  283. protected function _setWord($word)
  284. {
  285. $session = $this->getSession();
  286. $session->word = $word;
  287. $this->_word = $word;
  288. return $this;
  289. }
  290. /**
  291. * Generate new random word
  292. *
  293. * @return string
  294. */
  295. protected function _generateWord()
  296. {
  297. $word = '';
  298. $wordLen = $this->getWordLen();
  299. $vowels = $this->_useNumbers ? self::$VN : self::$V;
  300. $consonants = $this->_useNumbers ? self::$CN : self::$C;
  301. $totIndexCon = count($consonants) - 1;
  302. $totIndexVow = count($vowels) - 1;
  303. for ($i=0; $i < $wordLen; $i = $i + 2) {
  304. // generate word with mix of vowels and consonants
  305. $consonant = $consonants[Zend_Crypt_Math::randInteger(0, $totIndexCon, true)];
  306. $vowel = $vowels[Zend_Crypt_Math::randInteger(0, $totIndexVow, true)];
  307. $word .= $consonant . $vowel;
  308. }
  309. if (strlen($word) > $wordLen) {
  310. $word = substr($word, 0, $wordLen);
  311. }
  312. return $word;
  313. }
  314. /**
  315. * Generate new session ID and new word
  316. *
  317. * @return string session ID
  318. */
  319. public function generate()
  320. {
  321. if (!$this->_keepSession) {
  322. $this->_session = null;
  323. }
  324. $id = $this->_generateRandomId();
  325. $this->_setId($id);
  326. $word = $this->_generateWord();
  327. $this->_setWord($word);
  328. return $id;
  329. }
  330. protected function _generateRandomId()
  331. {
  332. return md5(Zend_Crypt_Math::randBytes(32));
  333. }
  334. /**
  335. * Validate the word
  336. *
  337. * @see Zend_Validate_Interface::isValid()
  338. * @param mixed $value
  339. * @param array|null $context
  340. * @return boolean
  341. */
  342. public function isValid($value, $context = null)
  343. {
  344. if (!is_array($value) && !is_array($context)) {
  345. $this->_error(self::MISSING_VALUE);
  346. return false;
  347. }
  348. if (!is_array($value) && is_array($context)) {
  349. $value = $context;
  350. }
  351. $name = $this->getName();
  352. if (isset($value[$name])) {
  353. $value = $value[$name];
  354. }
  355. if (!isset($value['input'])) {
  356. $this->_error(self::MISSING_VALUE);
  357. return false;
  358. }
  359. $input = strtolower($value['input']);
  360. $this->_setValue($input);
  361. if (!isset($value['id'])) {
  362. $this->_error(self::MISSING_ID);
  363. return false;
  364. }
  365. $this->_id = $value['id'];
  366. if ($input !== $this->getWord()) {
  367. $this->_error(self::BAD_CAPTCHA);
  368. return false;
  369. }
  370. return true;
  371. }
  372. /**
  373. * Get captcha decorator
  374. *
  375. * @return string
  376. */
  377. public function getDecorator()
  378. {
  379. return "Captcha_Word";
  380. }
  381. }