PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Service/ReCaptcha/ReCaptcha.php

https://github.com/shevron/zf2
PHP | 478 lines | 223 code | 61 blank | 194 comment | 22 complexity | a0c8bcc306a9f079cd4fb8fc49fea071 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. * @package Zend_Service
  9. */
  10. namespace Zend\Service\ReCaptcha;
  11. use Traversable;
  12. use Zend\Http\Request;
  13. use Zend\Service\AbstractService;
  14. use Zend\Stdlib\ArrayUtils;
  15. /**
  16. * Zend_Service_ReCaptcha
  17. *
  18. * @category Zend
  19. * @package Zend_Service
  20. * @subpackage ReCaptcha
  21. */
  22. class ReCaptcha extends AbstractService
  23. {
  24. /**
  25. * URI to the regular API
  26. *
  27. * @var string
  28. */
  29. const API_SERVER = 'http://www.google.com/recaptcha/api';
  30. /**
  31. * URI to the secure API
  32. *
  33. * @var string
  34. */
  35. const API_SECURE_SERVER = 'https://www.google.com/recaptcha/api';
  36. /**
  37. * URI to the verify server
  38. *
  39. * @var string
  40. */
  41. const VERIFY_SERVER = 'http://www.google.com/recaptcha/api/verify';
  42. /**
  43. * Public key used when displaying the captcha
  44. *
  45. * @var string
  46. */
  47. protected $_publicKey = null;
  48. /**
  49. * Private key used when verifying user input
  50. *
  51. * @var string
  52. */
  53. protected $_privateKey = null;
  54. /**
  55. * Ip address used when verifying user input
  56. *
  57. * @var string
  58. */
  59. protected $_ip = null;
  60. /**
  61. * Parameters for the object
  62. *
  63. * @var array
  64. */
  65. protected $_params = array(
  66. 'ssl' => false, /* Use SSL or not when generating the recaptcha */
  67. 'error' => null, /* The error message to display in the recaptcha */
  68. 'xhtml' => false /* Enable XHTML output (this will not be XHTML Strict
  69. compliant since the IFRAME is necessary when
  70. Javascript is disabled) */
  71. );
  72. /**
  73. * Options for tailoring reCaptcha
  74. *
  75. * See the different options on http://recaptcha.net/apidocs/captcha/client.html
  76. *
  77. * @var array
  78. */
  79. protected $_options = array(
  80. 'theme' => 'red',
  81. 'lang' => 'en',
  82. );
  83. /**
  84. * Response from the verify server
  85. *
  86. * @var \Zend\Service\ReCaptcha\Response
  87. */
  88. protected $_response = null;
  89. /**
  90. * Class constructor
  91. *
  92. * @param string $publicKey
  93. * @param string $privateKey
  94. * @param array|Traversable $params
  95. * @param array|Traversable $options
  96. * @param string $ip
  97. */
  98. public function __construct($publicKey = null, $privateKey = null,
  99. $params = null, $options = null, $ip = null)
  100. {
  101. if ($publicKey !== null) {
  102. $this->setPublicKey($publicKey);
  103. }
  104. if ($privateKey !== null) {
  105. $this->setPrivateKey($privateKey);
  106. }
  107. if ($ip !== null) {
  108. $this->setIp($ip);
  109. } else if (isset($_SERVER['REMOTE_ADDR'])) {
  110. $this->setIp($_SERVER['REMOTE_ADDR']);
  111. }
  112. if ($params !== null) {
  113. $this->setParams($params);
  114. }
  115. if ($options !== null) {
  116. $this->setOptions($options);
  117. }
  118. }
  119. /**
  120. * Serialize as string
  121. *
  122. * When the instance is used as a string it will display the recaptcha.
  123. * Since we can't throw exceptions within this method we will trigger
  124. * a user warning instead.
  125. *
  126. * @return string
  127. */
  128. public function __toString()
  129. {
  130. try {
  131. $return = $this->getHtml();
  132. } catch (\Exception $e) {
  133. $return = '';
  134. trigger_error($e->getMessage(), E_USER_WARNING);
  135. }
  136. return $return;
  137. }
  138. /**
  139. * Set the ip property
  140. *
  141. * @param string $ip
  142. * @return \Zend\Service\ReCaptcha\ReCaptcha
  143. */
  144. public function setIp($ip)
  145. {
  146. $this->_ip = $ip;
  147. return $this;
  148. }
  149. /**
  150. * Get the ip property
  151. *
  152. * @return string
  153. */
  154. public function getIp()
  155. {
  156. return $this->_ip;
  157. }
  158. /**
  159. * Set a single parameter
  160. *
  161. * @param string $key
  162. * @param string $value
  163. * @return \Zend\Service\ReCaptcha\ReCaptcha
  164. */
  165. public function setParam($key, $value)
  166. {
  167. $this->_params[$key] = $value;
  168. return $this;
  169. }
  170. /**
  171. * Set parameters
  172. *
  173. * @param array|Traversable $params
  174. * @return \Zend\Service\ReCaptcha\ReCaptcha
  175. * @throws \Zend\Service\ReCaptcha\Exception
  176. */
  177. public function setParams($params)
  178. {
  179. if ($params instanceof Traversable) {
  180. $params = ArrayUtils::iteratorToArray($params);
  181. }
  182. if (!is_array($params)) {
  183. throw new Exception(sprintf(
  184. '%s expects an array or Traversable set of params; received "%s"',
  185. __METHOD__,
  186. (is_object($params) ? get_class($params) : gettype($params))
  187. ));
  188. }
  189. foreach ($params as $k => $v) {
  190. $this->setParam($k, $v);
  191. }
  192. return $this;
  193. }
  194. /**
  195. * Get the parameter array
  196. *
  197. * @return array
  198. */
  199. public function getParams()
  200. {
  201. return $this->_params;
  202. }
  203. /**
  204. * Get a single parameter
  205. *
  206. * @param string $key
  207. * @return mixed
  208. */
  209. public function getParam($key)
  210. {
  211. return $this->_params[$key];
  212. }
  213. /**
  214. * Set a single option
  215. *
  216. * @param string $key
  217. * @param string $value
  218. * @return \Zend\Service\ReCaptcha\ReCaptcha
  219. */
  220. public function setOption($key, $value)
  221. {
  222. $this->_options[$key] = $value;
  223. return $this;
  224. }
  225. /**
  226. * Set options
  227. *
  228. * @param array|Traversable $options
  229. * @return \Zend\Service\ReCaptcha\ReCaptcha
  230. * @throws \Zend\Service\ReCaptcha\Exception
  231. */
  232. public function setOptions($options)
  233. {
  234. if ($options instanceof Traversable) {
  235. $options = ArrayUtils::iteratorToArray($options);
  236. }
  237. if (is_array($options)) {
  238. foreach ($options as $k => $v) {
  239. $this->setOption($k, $v);
  240. }
  241. } else {
  242. throw new Exception(
  243. 'Expected array or Traversable object'
  244. );
  245. }
  246. return $this;
  247. }
  248. /**
  249. * Get the options array
  250. *
  251. * @return array
  252. */
  253. public function getOptions()
  254. {
  255. return $this->_options;
  256. }
  257. /**
  258. * Get a single option
  259. *
  260. * @param string $key
  261. * @return mixed
  262. */
  263. public function getOption($key)
  264. {
  265. return $this->_options[$key];
  266. }
  267. /**
  268. * Get the public key
  269. *
  270. * @return string
  271. */
  272. public function getPublicKey()
  273. {
  274. return $this->_publicKey;
  275. }
  276. /**
  277. * Set the public key
  278. *
  279. * @param string $publicKey
  280. * @return \Zend\Service\ReCaptcha\ReCaptcha
  281. */
  282. public function setPublicKey($publicKey)
  283. {
  284. $this->_publicKey = $publicKey;
  285. return $this;
  286. }
  287. /**
  288. * Get the private key
  289. *
  290. * @return string
  291. */
  292. public function getPrivateKey()
  293. {
  294. return $this->_privateKey;
  295. }
  296. /**
  297. * Set the private key
  298. *
  299. * @param string $privateKey
  300. * @return \Zend\Service\ReCaptcha\ReCaptcha
  301. */
  302. public function setPrivateKey($privateKey)
  303. {
  304. $this->_privateKey = $privateKey;
  305. return $this;
  306. }
  307. /**
  308. * Get the HTML code for the captcha
  309. *
  310. * This method uses the public key to fetch a recaptcha form.
  311. *
  312. * @param null|string $name Base name for recaptcha form elements
  313. * @return string
  314. * @throws \Zend\Service\ReCaptcha\Exception
  315. */
  316. public function getHtml($name = null)
  317. {
  318. if ($this->_publicKey === null) {
  319. throw new Exception('Missing public key');
  320. }
  321. $host = self::API_SERVER;
  322. if ((bool) $this->_params['ssl'] === true) {
  323. $host = self::API_SECURE_SERVER;
  324. }
  325. $htmlBreak = '<br>';
  326. $htmlInputClosing = '>';
  327. if ((bool) $this->_params['xhtml'] === true) {
  328. $htmlBreak = '<br />';
  329. $htmlInputClosing = '/>';
  330. }
  331. $errorPart = '';
  332. if (!empty($this->_params['error'])) {
  333. $errorPart = '&error=' . urlencode($this->_params['error']);
  334. }
  335. $reCaptchaOptions = '';
  336. if (!empty($this->_options)) {
  337. $encoded = \Zend\Json\Json::encode($this->_options);
  338. $reCaptchaOptions = <<<SCRIPT
  339. <script type="text/javascript">
  340. var RecaptchaOptions = {$encoded};
  341. </script>
  342. SCRIPT;
  343. }
  344. $challengeField = 'recaptcha_challenge_field';
  345. $responseField = 'recaptcha_response_field';
  346. if (!empty($name)) {
  347. $challengeField = $name . '[' . $challengeField . ']';
  348. $responseField = $name . '[' . $responseField . ']';
  349. }
  350. $return = $reCaptchaOptions;
  351. $return .= <<<HTML
  352. <script type="text/javascript"
  353. src="{$host}/challenge?k={$this->_publicKey}{$errorPart}">
  354. </script>
  355. HTML;
  356. $return .= <<<HTML
  357. <noscript>
  358. <iframe src="{$host}/noscript?k={$this->_publicKey}{$errorPart}"
  359. height="300" width="500" frameborder="0"></iframe>{$htmlBreak}
  360. <textarea name="{$challengeField}" rows="3" cols="40">
  361. </textarea>
  362. <input type="hidden" name="{$responseField}"
  363. value="manual_challenge"{$htmlInputClosing}
  364. </noscript>
  365. HTML;
  366. return $return;
  367. }
  368. /**
  369. * Post a solution to the verify server
  370. *
  371. * @param string $challengeField
  372. * @param string $responseField
  373. * @return \Zend\Http\Response
  374. * @throws \Zend\Service\ReCaptcha\Exception
  375. */
  376. protected function _post($challengeField, $responseField)
  377. {
  378. if ($this->_privateKey === null) {
  379. throw new Exception('Missing private key');
  380. }
  381. if ($this->_ip === null) {
  382. throw new Exception('Missing ip address');
  383. }
  384. if (empty($challengeField)) {
  385. throw new Exception('Missing challenge field');
  386. }
  387. if (empty($responseField)) {
  388. throw new Exception('Missing response field');
  389. }
  390. /* Fetch an instance of the http client */
  391. $httpClient = $this->getHttpClient();
  392. $postParams = array('privatekey' => $this->_privateKey,
  393. 'remoteip' => $this->_ip,
  394. 'challenge' => $challengeField,
  395. 'response' => $responseField);
  396. /* Make the POST and return the response */
  397. return $httpClient->setUri(self::VERIFY_SERVER)
  398. ->setParameterPost($postParams)
  399. ->setMethod(Request::METHOD_POST)
  400. ->send();
  401. }
  402. /**
  403. * Verify the user input
  404. *
  405. * This method calls up the post method and returns a
  406. * Zend_Service_ReCaptcha_Response object.
  407. *
  408. * @param string $challengeField
  409. * @param string $responseField
  410. * @return \Zend\Service\ReCaptcha\Response
  411. */
  412. public function verify($challengeField, $responseField)
  413. {
  414. $response = $this->_post($challengeField, $responseField);
  415. return new Response(null, null, $response);
  416. }
  417. }