Captcha.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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_Form
  17. * @subpackage Element
  18. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /** @see Zend_Form_Element_Xhtml */
  23. require_once 'Zend/Form/Element/Xhtml.php';
  24. /** @see Zend_Captcha_Adapter */
  25. require_once 'Zend/Captcha/Adapter.php';
  26. /**
  27. * Generic captcha element
  28. *
  29. * This element allows to insert CAPTCHA into the form in order
  30. * to validate that human is submitting the form. The actual
  31. * logic is contained in the captcha adapter.
  32. *
  33. * @see http://en.wikipedia.org/wiki/Captcha
  34. *
  35. * @category Zend
  36. * @package Zend_Form
  37. * @subpackage Element
  38. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  39. * @license http://framework.zend.com/license/new-bsd New BSD License
  40. */
  41. class Zend_Form_Element_Captcha extends Zend_Form_Element_Xhtml
  42. {
  43. /**
  44. * Captcha plugin type constant
  45. */
  46. const CAPTCHA = 'CAPTCHA';
  47. /**
  48. * Captcha adapter
  49. *
  50. * @var Zend_Captcha_Adapter
  51. */
  52. protected $_captcha;
  53. /**
  54. * Get captcha adapter
  55. *
  56. * @return Zend_Captcha_Adapter
  57. */
  58. public function getCaptcha()
  59. {
  60. return $this->_captcha;
  61. }
  62. /**
  63. * Set captcha adapter
  64. *
  65. * @param string|array|Zend_Captcha_Adapter $captcha
  66. * @param array $options
  67. */
  68. public function setCaptcha($captcha, $options = array())
  69. {
  70. if ($captcha instanceof Zend_Captcha_Adapter) {
  71. $instance = $captcha;
  72. } else {
  73. if (is_array($captcha)) {
  74. if (array_key_exists('captcha', $captcha)) {
  75. $name = $captcha['captcha'];
  76. unset($captcha['captcha']);
  77. } else {
  78. $name = array_shift($captcha);
  79. }
  80. $options = array_merge($options, $captcha);
  81. } else {
  82. $name = $captcha;
  83. }
  84. $name = $this->getPluginLoader(self::CAPTCHA)->load($name);
  85. if (empty($options)) {
  86. $instance = new $name;
  87. } else {
  88. $r = new ReflectionClass($name);
  89. if ($r->hasMethod('__construct')) {
  90. $instance = $r->newInstanceArgs(array($options));
  91. } else {
  92. $instance = $r->newInstance();
  93. }
  94. }
  95. }
  96. $this->_captcha = $instance;
  97. $this->_captcha->setName($this->getName());
  98. return $this;
  99. }
  100. /**
  101. * Constructor
  102. *
  103. * $spec may be:
  104. * - string: name of element
  105. * - array: options with which to configure element
  106. * - Zend_Config: Zend_Config with options for configuring element
  107. *
  108. * @param string|array|Zend_Config $spec
  109. * @return void
  110. */
  111. public function __construct($spec, $options = null)
  112. {
  113. parent::__construct($spec, $options);
  114. $this->setAllowEmpty(true)
  115. ->setRequired(true)
  116. ->setAutoInsertNotEmptyValidator(false)
  117. ->addValidator($this->getCaptcha(), true);
  118. }
  119. /**
  120. * Return all attributes
  121. *
  122. * @return array
  123. */
  124. public function getAttribs()
  125. {
  126. $attribs = get_object_vars($this);
  127. unset($attribs['helper']);
  128. foreach ($attribs as $key => $value) {
  129. if ('_' == substr($key, 0, 1)) {
  130. unset($attribs[$key]);
  131. }
  132. }
  133. return $attribs;
  134. }
  135. /**
  136. * Set options
  137. *
  138. * Overrides to allow passing captcha options
  139. *
  140. * @param array $options
  141. * @return Zend_Form_Element_Captcha
  142. */
  143. public function setOptions(array $options)
  144. {
  145. $captcha = null;
  146. $captchaOptions = array();
  147. if (array_key_exists('captcha', $options)) {
  148. $captcha = $options['captcha'];
  149. if (array_key_exists('captchaOptions', $options)) {
  150. $captchaOptions = $options['captchaOptions'];
  151. unset($options['captchaOptions']);
  152. }
  153. unset($options['captcha']);
  154. }
  155. parent::setOptions($options);
  156. if(null !== $captcha) {
  157. $this->setCaptcha($captcha, $captchaOptions);
  158. }
  159. return $this;
  160. }
  161. /**
  162. * Render form element
  163. *
  164. * @param Zend_View_Interface $view
  165. * @return string
  166. */
  167. public function render(Zend_View_Interface $view = null)
  168. {
  169. $captcha = $this->getCaptcha();
  170. $captcha->setName($this->getFullyQualifiedName());
  171. if (!$this->loadDefaultDecoratorsIsDisabled()) {
  172. $decorators = $this->getDecorators();
  173. $decorator = $captcha->getDecorator();
  174. $key = get_class($this->_getDecorator($decorator, null));
  175. if (!empty($decorator) && !array_key_exists($key, $decorators)) {
  176. array_unshift($decorators, $decorator);
  177. }
  178. $decorator = array('Captcha', array('captcha' => $captcha));
  179. $key = get_class($this->_getDecorator($decorator[0], $decorator[1]));
  180. if ($captcha instanceof Zend_Captcha_Word && !array_key_exists($key, $decorators)) {
  181. array_unshift($decorators, $decorator);
  182. }
  183. $this->setDecorators($decorators);
  184. }
  185. $this->setValue($this->getCaptcha()->generate());
  186. return parent::render($view);
  187. }
  188. /**
  189. * Retrieve plugin loader for validator or filter chain
  190. *
  191. * Support for plugin loader for Captcha adapters
  192. *
  193. * @param string $type
  194. * @return Zend_Loader_PluginLoader
  195. * @throws Zend_Loader_Exception on invalid type.
  196. */
  197. public function getPluginLoader($type)
  198. {
  199. $type = strtoupper($type);
  200. if ($type == self::CAPTCHA) {
  201. if (!isset($this->_loaders[$type])) {
  202. require_once 'Zend/Loader/PluginLoader.php';
  203. $this->_loaders[$type] = new Zend_Loader_PluginLoader(
  204. array('Zend_Captcha' => 'Zend/Captcha/')
  205. );
  206. }
  207. return $this->_loaders[$type];
  208. } else {
  209. return parent::getPluginLoader($type);
  210. }
  211. }
  212. /**
  213. * Add prefix path for plugin loader for captcha adapters
  214. *
  215. * This method handles the captcha type, the rest is handled by
  216. * the parent
  217. * @param string $prefix
  218. * @param string $path
  219. * @param string $type
  220. * @return Zend_Form_Element
  221. * @see Zend_Form_Element::addPrefixPath
  222. */
  223. public function addPrefixPath($prefix, $path, $type = null)
  224. {
  225. $type = strtoupper($type);
  226. switch ($type) {
  227. case null:
  228. $loader = $this->getPluginLoader(self::CAPTCHA);
  229. $cPrefix = rtrim($prefix, '_') . '_Captcha';
  230. $cPath = rtrim($path, '/\\') . '/Captcha';
  231. $loader->addPrefixPath($cPrefix, $cPath);
  232. return parent::addPrefixPath($prefix, $path);
  233. case self::CAPTCHA:
  234. $loader = $this->getPluginLoader($type);
  235. $loader->addPrefixPath($prefix, $path);
  236. return $this;
  237. default:
  238. return parent::addPrefixPath($prefix, $path, $type);
  239. }
  240. }
  241. /**
  242. * Load default decorators
  243. *
  244. * @return Zend_Form_Element_Captcha
  245. */
  246. public function loadDefaultDecorators()
  247. {
  248. if ($this->loadDefaultDecoratorsIsDisabled()) {
  249. return $this;
  250. }
  251. $decorators = $this->getDecorators();
  252. if (empty($decorators)) {
  253. $this->addDecorator('Errors')
  254. ->addDecorator('Description', array('tag' => 'p', 'class' => 'description'))
  255. ->addDecorator('HtmlTag', array('tag' => 'dd', 'id' => $this->getName() . '-element'))
  256. ->addDecorator('Label', array('tag' => 'dt'));
  257. }
  258. return $this;
  259. }
  260. /**
  261. * Is the captcha valid?
  262. *
  263. * @param mixed $value
  264. * @param mixed $context
  265. * @return boolean
  266. */
  267. public function isValid($value, $context = null)
  268. {
  269. $this->getCaptcha()->setName($this->getName());
  270. $belongsTo = $this->getBelongsTo();
  271. if (empty($belongsTo) || !is_array($context)) {
  272. return parent::isValid($value, $context);
  273. }
  274. $name = $this->getFullyQualifiedName();
  275. $root = substr($name, 0, strpos($name, '['));
  276. $segments = substr($name, strpos($name, '['));
  277. $segments = ltrim($segments, '[');
  278. $segments = rtrim($segments, ']');
  279. $segments = explode('][', $segments);
  280. array_unshift($segments, $root);
  281. array_pop($segments);
  282. $newContext = $context;
  283. foreach ($segments as $segment) {
  284. if (array_key_exists($segment, $newContext)) {
  285. $newContext = $newContext[$segment];
  286. }
  287. }
  288. return parent::isValid($value, $newContext);
  289. }
  290. }