Captcha.php 4.94 KB
Newer Older
1 2 3 4 5 6 7
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

crtlib committed
8
namespace yii\captcha;
9 10 11

use Yii;
use yii\base\InvalidConfigException;
12
use yii\helpers\Url;
13 14
use yii\helpers\Html;
use yii\helpers\Json;
Qiang Xue committed
15 16
use yii\widgets\InputWidget;

17
/**
18
 * Captcha renders a CAPTCHA image and an input field that takes user-entered verification code.
19 20 21 22 23 24 25 26 27 28
 *
 * Captcha is used together with [[CaptchaAction]] provide [CAPTCHA](http://en.wikipedia.org/wiki/Captcha)
 * - a way of preventing Website spamming.
 *
 * The image element rendered by Captcha will display a CAPTCHA image generated by
 * an action whose route is specified by [[captchaAction]]. This action must be an instance of [[CaptchaAction]].
 *
 * When the user clicks on the CAPTCHA image, it will cause the CAPTCHA image
 * to be refreshed with a new CAPTCHA.
 *
29
 * You may use [[\yii\captcha\CaptchaValidator]] to validate the user input matches
30 31 32 33 34
 * the current CAPTCHA verification code.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
35
class Captcha extends InputWidget
36
{
37
    /**
38
     * @var string|array the route of the action that generates the CAPTCHA images.
39
     * The action represented by this route must be an action of [[CaptchaAction]].
40
     * Please refer to [[\yii\helpers\Url::toRoute()]] for acceptable formats.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
     */
    public $captchaAction = 'site/captcha';
    /**
     * @var array HTML attributes to be applied to the CAPTCHA image tag.
     * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
     */
    public $imageOptions = [];
    /**
     * @var string the template for arranging the CAPTCHA image tag and the text input tag.
     * In this template, the token `{image}` will be replaced with the actual image tag,
     * while `{input}` will be replaced with the text input tag.
     */
    public $template = '{image} {input}';
    /**
     * @var array the HTML attributes for the input tag.
     * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
     */
    public $options = ['class' => 'form-control'];
59

60 61 62 63 64 65
    /**
     * Initializes the widget.
     */
    public function init()
    {
        parent::init();
66

67
        $this->checkRequirements();
68

69 70 71 72
        if (!isset($this->imageOptions['id'])) {
            $this->imageOptions['id'] = $this->options['id'] . '-image';
        }
    }
73

74 75 76 77 78 79 80 81 82 83 84
    /**
     * Renders the widget.
     */
    public function run()
    {
        $this->registerClientScript();
        if ($this->hasModel()) {
            $input = Html::activeTextInput($this->model, $this->attribute, $this->options);
        } else {
            $input = Html::textInput($this->name, $this->value, $this->options);
        }
85 86 87 88 89 90 91
        $route = $this->captchaAction;
        if (is_array($route)) {
            $route['v'] = uniqid();
        } else {
            $route = [$route, 'v' => uniqid()];
        }
        $image = Html::img($route, $this->imageOptions);
92 93 94 95 96
        echo strtr($this->template, [
            '{input}' => $input,
            '{image}' => $image,
        ]);
    }
97

98 99 100 101 102 103 104 105 106 107 108 109
    /**
     * Registers the needed JavaScript.
     */
    public function registerClientScript()
    {
        $options = $this->getClientOptions();
        $options = empty($options) ? '' : Json::encode($options);
        $id = $this->imageOptions['id'];
        $view = $this->getView();
        CaptchaAsset::register($view);
        $view->registerJs("jQuery('#$id').yiiCaptcha($options);");
    }
110

111 112 113 114 115 116
    /**
     * Returns the options for the captcha JS widget.
     * @return array the options
     */
    protected function getClientOptions()
    {
117 118 119 120 121 122 123
        $route = $this->captchaAction;
        if (is_array($route)) {
            $route[CaptchaAction::REFRESH_GET_VAR] = 1;
        } else {
            $route = [$route, CaptchaAction::REFRESH_GET_VAR => 1];
        }

124
        $options = [
125
            'refreshUrl' => Url::toRoute($route),
Qiang Xue committed
126
            'hashKey' => "yiiCaptcha/{$route[0]}",
127
        ];
128

129 130
        return $options;
    }
131

132 133 134
    /**
     * Checks if there is graphic extension available to generate CAPTCHA images.
     * This method will check the existence of ImageMagick and GD extensions.
135
     * @return string the name of the graphic extension, either "imagick" or "gd".
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
     * @throws InvalidConfigException if neither ImageMagick nor GD is installed.
     */
    public static function checkRequirements()
    {
        if (extension_loaded('imagick')) {
            $imagick = new \Imagick();
            $imagickFormats = $imagick->queryFormats('PNG');
            if (in_array('PNG', $imagickFormats)) {
                return 'imagick';
            }
        }
        if (extension_loaded('gd')) {
            $gdInfo = gd_info();
            if (!empty($gdInfo['FreeType Support'])) {
                return 'gd';
            }
        }
        throw new InvalidConfigException('GD with FreeType or ImageMagick PHP extensions are required.');
    }
155
}