Services_ReCaptcha
[ class tree: Services_ReCaptcha ] [ index: Services_ReCaptcha ] [ all elements ]

Source for file ReCaptcha.php

Documentation is available at ReCaptcha.php

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * This file is part of the PEAR Services_ReCaptcha package.
  7.  *
  8.  * PHP version 5
  9.  *
  10.  * LICENSE: This source file is subject to the MIT license that is available
  11.  * through the world-wide-web at the following URI:
  12.  * http://opensource.org/licenses/mit-license.php
  13.  *
  14.  * @category  Services
  15.  * @package   Services_ReCaptcha
  16.  * @author    David Jean Louis <izi@php.net>
  17.  * @copyright 2008-2009 David Jean Louis
  18.  * @license   http://opensource.org/licenses/mit-license.php MIT License
  19.  * @version   CVS: $Id$
  20.  * @link      http://pear.php.net/package/Services_ReCaptcha
  21.  * @link      http://recaptcha.net/apidocs/captcha/client.html
  22.  * @since     File available since release 0.1.0
  23.  * @filesource
  24.  */
  25.  
  26. /**
  27.  * Dependencies.
  28.  */
  29. require_once 'Services/ReCaptcha/Base.php';
  30. require_once 'HTTP/Request2.php';
  31.  
  32. /**
  33.  * PHP5 interface to the reCATCHA API.
  34.  *
  35.  * reCAPTCHA is a freely available CAPTCHA implementation. It distinguishes
  36.  * humans from computers.
  37.  *
  38.  * In order to use reCAPTCHA, you need a public/private API key pair. This key
  39.  * pair helps to prevent an attack where somebody hosts a reCAPTCHA on their
  40.  * website, collects answers from their visitors and submits the answers to
  41.  * your site. You can sign up for a key on the
  42.  * {@link http://recaptcha.net/api/getkey reCAPTCHA Administration Portal}
  43.  *
  44.  * @category  Services
  45.  * @package   Services_ReCaptcha
  46.  * @author    David Jean Louis <izi@php.net>
  47.  * @copyright 2008-2009 David Jean Louis
  48.  * @license   http://opensource.org/licenses/mit-license.php MIT License
  49.  * @version   Release: @package_version@
  50.  * @link      http://pear.php.net/package/Services_ReCaptcha
  51.  * @link      http://recaptcha.net/apidocs/captcha/client.html
  52.  * @since     Class available since release 0.1.0
  53.  * @example   examples/example-01.php Services_ReCaptcha simple example
  54.  * @example   examples/example-02.php Services_ReCaptcha advanced example
  55.  */
  56. {
  57.     // properties {{{
  58.  
  59.     /**
  60.      * The reCAPTCHA API URL.
  61.      *
  62.      * @var string $apiURL 
  63.      */
  64.     public $apiURL = 'https://www.google.com/recaptcha/api';
  65.  
  66.     /**
  67.      * The reCAPTCHA API secure URL, this is URL is used by default when the
  68.      * script is running on an https host or when you force it via the 'secure'
  69.      * option.
  70.      *
  71.      * @var string $apiSecureURL 
  72.      */
  73.     public $apiSecureURL = 'https://www.google.com/recaptcha/api';
  74.  
  75.     /**
  76.      * Url of the ReCaptcha verify API.
  77.      *
  78.      * @var string $apiVerifyURL 
  79.      */
  80.     public $apiVerifyURL = 'http://www.google.com/recaptcha/api/verify';
  81.  
  82.     /**
  83.      * The error code used to display the error message in the captcha.
  84.      *
  85.      * @var string $error 
  86.      * @see Services_ReCaptcha::getError()
  87.      * @see Services_ReCaptcha::setError()
  88.      */
  89.     protected $error;
  90.  
  91.     /**
  92.      * Options to customize the html, the url and the look and feel of the captcha.
  93.      * Available options are:
  94.      *   - secure: whether to force the ssl url (default: false);
  95.      *   - xhtml: whether the html should be xhtml compliant (default: true);
  96.      *   - theme: string, defines which theme to use for reCAPTCHA (default: red);
  97.      *   - lang: string, one of the supported language codes (default: en);
  98.      *   - custom_translations: array, se this to specify custom translations
  99.      *     of reCAPTCHA strings (default: null).
  100.      *   - custom_theme_widget: string, the ID of a DOM element (default: null);
  101.      *   - tabindex: integer, the tabindex for the reCAPTCHA text area.
  102.      * 
  103.      * For a full documentation of theses options please consult the
  104.      * {@link http://recaptcha.net/apidocs/captcha/client.html API documentation}.
  105.      *
  106.      * @var array $options 
  107.      * @see Services_ReCaptcha_Base::getOption()
  108.      * @see Services_ReCaptcha_Base::setOption()
  109.      * @see Services_ReCaptcha_Base::getOptions()
  110.      * @see Services_ReCaptcha_Base::setOptions()
  111.      */
  112.     protected $options = array(
  113.         'secure'              => false,
  114.         'xhtml'               => true,
  115.         'theme'               => null,
  116.         'lang'                => null,
  117.         'custom_translations' => null,
  118.         'custom_theme_widget' => null,
  119.         'tabindex'            => null,
  120.     );
  121.  
  122.     /**
  123.      * The HTTP_Request2 instance.
  124.      *
  125.      * You can customize the request if you need to (ie: if you use a proxy)
  126.      * with the get/setRequest() methods, for example:
  127.      *
  128.      * <code>
  129.      * require_once 'Services/ReCaptcha.php';
  130.      *
  131.      * $recaptcha = new Services_ReCaptcha('pubkey', 'privkey');
  132.      * $recaptcha->getRequest()->setConfig(array(
  133.      *     'proxy_host' => 'localhost',
  134.      *     'proxy_port' => 8118,
  135.      * ));
  136.      * </code>
  137.      *
  138.      * @var HTTP_Request2 $request 
  139.      * @see Services_ReCaptcha::getRequest()
  140.      * @see Services_ReCaptcha::setRequest()
  141.      */
  142.     protected $request;
  143.  
  144.     // }}}
  145.     // __construct() {{{
  146.     
  147.     /**
  148.      * Constructor, you must pass a valid public and private API key.
  149.      *
  150.      * @param string $pubKey  The public API key (mandatory)
  151.      * @param string $privKey The private API key (mandatory)
  152.      * @param array  $options An array of options (optional)
  153.      * 
  154.      * @see Services_ReCaptcha::$options
  155.      * @return void 
  156.      */
  157.     public function __construct($pubKey$privKeyarray $options = array()) 
  158.     {
  159.         parent::__construct($pubKey$privKey$options);
  160.     }
  161.     
  162.     // }}}
  163.     // getURL() {{{
  164.     
  165.     /**
  166.      * Returns the URL of the script or iframe src attribute.
  167.      *
  168.      * @param string $path Set this to "noscript" to get the iframe URL instead
  169.      *                      of the script URL
  170.      *
  171.      * @return string The URL of the script or iframe src attribute
  172.      */
  173.     public function getURL($path 'challenge')
  174.     {
  175.         // in order to avoid getting browser warnings, if we have an SSL web 
  176.         // site we use the secure url
  177.         if ($this->options['secure'
  178.             || (isset($_SERVER['HTTPS']&& $_SERVER['HTTPS'== 'on')
  179.         {
  180.             $url $this->apiSecureURL;
  181.         else {
  182.             $url $this->apiURL;
  183.         }
  184.  
  185.         $url .= '/' $path '?k=' $this->apiPublicKey;
  186.         if ($this->error !== null{
  187.             $url .= '&error=' urlencode($this->error);
  188.         }
  189.         return $url;
  190.     }
  191.     
  192.     // }}}
  193.     // getHTML() {{{
  194.     
  195.     /**
  196.      * Returns the HTML code to insert into your form.
  197.      *
  198.      * Instead of this method you can use the __toString() magic method to get
  199.      * the HTML code, for example:
  200.      *
  201.      * <code>
  202.      * require_once 'Services/ReCaptcha.php';
  203.      *
  204.      * $recaptcha = new Services_ReCaptcha('pubkey', 'privkey');
  205.      * // both are equivalents:
  206.      * $html = $recaptcha->getHTML();
  207.      * $html = (string) $recaptcha;
  208.      * </code>
  209.      *
  210.      * @return string The HTML to include in your webpage
  211.      * @see Services_ReCaptcha_Base::__toString()
  212.      */
  213.     public function getHTML()
  214.     {
  215.         $return '';
  216.         // we use array_slice to get only look and feel options, and
  217.         // array_filter to filter null keys
  218.         $options array_filter(array_slice($this->options2));
  219.  
  220.         // whether to be xhtml or html compliant
  221.         if ($this->options['xhtml']{
  222.             $br  '<br/>';
  223.             $end '/>';
  224.         else {
  225.             $br  '<br>';
  226.             $end '>';
  227.         }
  228.  
  229.         if (!empty($options)) {
  230.             $opts    json_encode($options);
  231.             $return .= <<<HTML
  232. <script type="text/javascript">
  233.     var RecaptchaOptions = $opts;
  234. </script>
  235.  
  236. HTML;
  237.         }
  238.         $scriptURL $this->getURL();
  239.         $iframeURL $this->getURL('noscript');
  240.         $return   .= <<<HTML
  241. <script type="text/javascript" src="{$scriptURL}"></script>
  242. <noscript>
  243.     <iframe src="{$iframeURL}" height="300" width="500" frameborder="0">
  244.     </iframe>
  245.     $br
  246.     <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
  247.     <input type="hidden" name="recaptcha_response_field" value="manual_challenge"$end
  248. </noscript>
  249.  
  250. HTML;
  251.         return $return;
  252.     }
  253.     
  254.     // }}}
  255.     // validate() {{{
  256.     
  257.     /**
  258.      * Validates the challenge response typed by the user and returns true if
  259.      * the challenge response is ok or false otherwise.
  260.      *
  261.      * You can explicitely pass the challenge, response and ip values if for
  262.      * some reason you need to do so.
  263.      *
  264.      * @param string $challenge Value of the challenge field, if not given, the
  265.      *                           method will look for the value in $_POST array
  266.      * @param string $response  Value of the response field, if not given, the
  267.      *                           method will look for the value in $_POST array
  268.      * @param int    $ip        The user IP address, if not given, the method
  269.      *                           will use $_SERVER['REMOTE_ADDR']
  270.      *
  271.      * @return bool Whether the challenge response is valid or not
  272.      * @throws Services_ReCaptcha_Exception When the request cannot be sent
  273.      * @throws Services_ReCaptcha_HTTPException When the server returns an
  274.      *                                           error response
  275.      */
  276.     public function validate($challenge = null$response = null$ip = null)
  277.     {
  278.         if ($challenge === null && isset($_POST['recaptcha_challenge_field'])) {
  279.             $challenge $_POST['recaptcha_challenge_field'];
  280.         }
  281.         if ($response === null && isset($_POST['recaptcha_response_field'])) {
  282.             $response $_POST['recaptcha_response_field'];
  283.         }
  284.         if ($ip === null && isset($_SERVER['REMOTE_ADDR'])) {
  285.             $ip $_SERVER['REMOTE_ADDR'];
  286.         }
  287.  
  288.         if (!$challenge || !$response{
  289.             // Don't send a request if challenge or response is empty
  290.             $this->setError('incorrect-captcha-sol');
  291.             return false;
  292.         }
  293.  
  294.         try {
  295.             $request = clone $this->getRequest();
  296.             $request->setMethod(HTTP_Request2::METHOD_POST);
  297.             $request->setUrl($this->apiVerifyURL);
  298.             $params = array(
  299.                 'privatekey' => $this->apiPrivateKey,
  300.                 'remoteip'   => $ip,
  301.                 'challenge'  => $challenge,
  302.                 'response'   => $response,
  303.             );
  304.             $request->addPostParameter($params);
  305.             $httpResponse $request->send();
  306.         catch (Exception $exc{
  307.             throw new Services_ReCaptcha_Exception(
  308.                 $exc->getMessage(),
  309.                 $exc
  310.             );
  311.         }
  312.  
  313.         if ($httpResponse->getStatus(!= 200{
  314.             throw new Services_ReCaptcha_HTTPException(
  315.                 $httpResponse->getReasonPhrase(),
  316.                 $httpResponse->getStatus(),
  317.                 $httpResponse
  318.             );
  319.         }
  320.  
  321.         $body explode("\n"$httpResponse->getBody()2);
  322.         if (count($body!= 2{
  323.             $this->setError('unknown');
  324.             return false;
  325.         }
  326.         if ($body[0!= 'true'{
  327.             $this->setError($body[1]);
  328.             return false;
  329.         }
  330.  
  331.         return true;
  332.     }
  333.     
  334.     // }}}
  335.     // getError() {{{
  336.  
  337.     /**
  338.      * Returns the current error code.
  339.      *
  340.      * @return string The error code
  341.      * @see Services_ReCaptcha::$error
  342.      */
  343.     public function getError()
  344.     {
  345.         return $this->error;
  346.     }
  347.  
  348.     // }}}
  349.     // setError() {{{
  350.  
  351.     /**
  352.      * Sets the error code to display in the captcha and returns the current
  353.      * Services_ReCaptcha instance.
  354.      *
  355.      * @param string $error The error message
  356.      *
  357.      * @return Services_ReCaptcha 
  358.      * @see Services_ReCaptcha::$error
  359.      */
  360.     public function setError($error)
  361.     {
  362.         $this->error = $error;
  363.         return $this;
  364.     }
  365.  
  366.     // }}}
  367.     // getRequest() {{{
  368.     
  369.     /**
  370.      * Returns the HTTP_Request2 instance, if it's not yet set it is
  371.      * instanciated on the fly.
  372.      * 
  373.      * @return HTTP_Request2 The request instance
  374.      * @see Services_ReCaptcha::$request
  375.      */
  376.     public function getRequest()
  377.     {
  378.         if (!$this->request instanceof HTTP_Request2{
  379.             $this->request = new HTTP_Request2();
  380.         }
  381.         return $this->request;
  382.     }
  383.     
  384.     // }}}
  385.     // setRequest() {{{
  386.     
  387.     /**
  388.      * Sets the HTTP_Request2 instance and returns the current
  389.      * Services_ReCaptcha instance.
  390.      * 
  391.      * @param HTTP_Request2 $request The request to set
  392.      *
  393.      * @return Services_ReCaptcha 
  394.      * @see Services_ReCaptcha::$request
  395.      */
  396.     public function setRequest(HTTP_Request2 $request)
  397.     {
  398.         $this->request = $request;
  399.         return $this;
  400.     }
  401.     
  402.     // }}}
  403. }

Documentation generated on Thu, 28 Apr 2011 21:30:02 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.