DbHelp.ru
Маленький Yii блог
Комментарии
guest_is_guest: @zolter, Осваиваю Yii по Вашим урокам и столкнулся с таким в...
Alex: Да, спасибо, теперь разобрался!
Гость: public $passwd2; не забыл в моделе?
Alex: Добрый день! Очень хороший блог, тяжело переоценить по...
Гость: Это обучающая статья просто по выводу Hello World. По уму - к...



Сервер Ultima Online - Forest Wars (от создателя данного блога)

Настройка 3d капчи на Yii

Рубрика: Работаем с капчей (captcha)

Оцените эту статью:

Рейтинг: 0.00 (0)
18 Апр. 2009
Опубликовать в Twitter Написать в Facebook Опубликовать в своем блоге livejournal.com

Эта статья устарела т.к. была написана для yii версии 1.0.х; Если вы используете более новую версию - у вас могут возникнуть ошибки из-за несовместимости. Обычно ответы на все вопросы работы на 1.1.х были описаны в комментариях ниже статьи.

Yii Framework Blog img http://dbhelp.rucap4-3d Честно сказать не думал что когда то буду ковырять капчу более чем на уровне настройки, но не тут то было. Спасибо romanoza за ссылку на 3d капчу которая сразу меня заинтересовала.

---

Посидел я пару минут в прочтении комментариев на хабре и обнаружил ссылку на либсу с исходным кодом http://code.google.com/p/3dcaptcha/. (Именно её я решил прикрутить к Yii).

Yii Framework Blog img http://dbhelp.rucap2-3d Если вам вдруг захотелось такую капчу на сайте – я расскажу как её прикрутить, но для начала вы должны убедиться что:
1.    Ваш сайт написан на Yii
2.    У вас уже есть страница с капчей и в одном из ваших контроллеров есть код:

public function actions()
{
    return array(
        'captcha'=>array(
            ….
            'class'=>'CCaptchaAction',
          ….
        ),
    );
}
Если у вас нет капчи – вы можете прочитать статью про то, как её установить.

Давайте теперь всё-таки приступим.
Заходим protected/components/ и создаем там файл Yii3dCaptcha.php :

/**
 * 3dCaptcha class file
 *
 * Данный клас позволяет вместо стандартной капчи на вашем сайте -
 * выводить красивую 3д. Все параметры (кроме width/height/padding)
 * работают и могут быть использованы для изменения: цвета текста,
 * цвета фона, количества букв.
 *
 * Дополнительный параметр fontSize - задает размер текста.
 *
 * @author Alekseenko Timur <zolter.od@gmail.com>
 * @license GNU General Public License v3
 * @link http://dbhelp.ru
 * @link http://code.google.com/p/3dcaptcha
 */
class Yii3dCaptcha extends CCaptchaAction
{
    /**
     * Размер текста
     * @var <type>
     */
    public $fontsize = 24;

    /**
     * Рендер капчи
     * @param <type> $code
     */
    function renderImage($code)
    {
        if($this->fontFile===null)
        $this->fontFile = dirname(__FILE__).'/views/3dCaptcha/3DCaptcha.ttf';

        $details = imagettfbbox($this->fontsize, 0, $this->fontFile, $code);
        $image2d_x = $details[4] + 4;
        $image2d_y = $this->fontsize * 1.3;

        $bevel = 4;

        $image2d = imagecreatetruecolor($image2d_x, $image2d_y);
        $black = imagecolorallocate($image2d, 0, 0, 0);
        $white = imagecolorallocate($image2d, 255, 255, 255);

        imagefill($image2d, 0, 0, $black);
        imagettftext($image2d, $this->fontsize, 0, 2, $this->fontsize, $white, $this->fontFile, $code);

        $T = $this->cameraTransform(
                array(rand(-90, 90), -200, rand(150, 250)),
                array(0, 0, 0)
            );

        $T = $this->matrixProduct(
                $T,
               $this->viewingTransform(60, 300, 3000)
            );

        $coord = array($image2d_x * $image2d_y);
        $count = 0;
        for ($y = 0; $y < $image2d_y; $y+=2) {
            for ($x = 0; $x < $image2d_x; $x++) {
                $xc = $x - $image2d_x / 2;
                $zc = $y - $image2d_y / 2;
                $yc = -(imagecolorat($image2d, $x, $y) & 0xff) / 256 * $bevel;
                $xyz = array($xc, $yc, $zc, 1);
                $xyz = $this->vectorProduct($xyz, $T);

                $coord[$count] = $xyz;
                $count++;
            }
        }

        $image3d_x = 256;
        $image3d_y = $image3d_x * 9 / 16;
        $image3d = imagecreatetruecolor($image3d_x, $image3d_y);
        $fgcolor = imagecolorallocate($image3d,
        (int)($this->foreColor%0x1000000/0x10000),
        (int)($this->foreColor%0x10000/0x100),
        $this->foreColor%0x100);

        $bgcolor = imagecolorallocate($image3d,
        (int)($this->backColor%0x1000000/0x10000),
        (int)($this->backColor%0x10000/0x100),
        $this->backColor%0x100);

        imageantialias($image3d, true);
        imagefill($image3d, 0, 0, $bgcolor);
        $count = 0;
        $scale = 1.75 - $image2d_x/400;
        for ($y = 0; $y < $image2d_y; $y++) {
            for ($x = 0; $x < $image2d_x; $x++) {
                if ($x > 0) {
                    $x0 = $coord[$count - 1][0] * $scale + $image3d_x / 2;
                    $y0 = $coord[$count - 1][1] * $scale + $image3d_y / 2;
                    $x1 = $coord[$count][0] * $scale + $image3d_x / 2;
                    $y1 = $coord[$count][1] * $scale + $image3d_y / 2;
                    imageline($image3d, $x0, $y0, $x1, $y1, $fgcolor);
                }
                $count++;
            }
        }

        header('Pragma: public');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Content-Transfer-Encoding: binary');
        header("Content-type: image/png");
        imagepng($image3d);
        imagedestroy($image3d);
    }

    function addVector($a, $b) {
        return array($a[0] + $b[0], $a[1] + $b[1], $a[2] + $b[2]);
    }

    function scalarProduct($vector, $scalar) {
        return array($vector[0] * $scalar, $vector[1] * $scalar, $vector[2] * $scalar);
    }

    function dotProduct($a, $b) {
        return ($a[0] * $b[0] + $a[1] * $b[1] + $a[2] * $b[2]);
    }

    function norm($vector) {
        return sqrt($this->dotProduct($vector, $vector));
    }

    function normalize($vector) {
        return $this->scalarProduct($vector, 1 / $this->norm($vector));
    }

    function crossProduct($a, $b) {
        return array(
            ($a[1] * $b[2] - $a[2] * $b[1]),
            ($a[2] * $b[0] - $a[0] * $b[2]),
            ($a[0] * $b[1] - $a[1] * $b[0])
            );
    }

    function vectorProductIndexed($v, $m, $i) {
        return array(
            $v[$i + 0] * $m[0] + $v[$i + 1] * $m[4] + $v[$i + 2] * $m[8] + $v[$i + 3] * $m[12],
            $v[$i + 0] * $m[1] + $v[$i + 1] * $m[5] + $v[$i + 2] * $m[9] + $v[$i + 3] * $m[13],
            $v[$i + 0] * $m[2] + $v[$i + 1] * $m[6] + $v[$i + 2] * $m[10]+ $v[$i + 3] * $m[14],
            $v[$i + 0] * $m[3] + $v[$i + 1] * $m[7] + $v[$i + 2] * $m[11]+ $v[$i + 3] * $m[15]
            );
    }

    function vectorProduct($v, $m) {
        return $this->vectorProductIndexed($v, $m, 0);
    }

    function matrixProduct($a, $b) {
        $o1 = $this->vectorProductIndexed($a, $b, 0);
        $o2 = $this->vectorProductIndexed($a, $b, 4);
        $o3 = $this->vectorProductIndexed($a, $b, 8);
        $o4 = $this->vectorProductIndexed($a, $b, 12);

        return array(
            $o1[0], $o1[1], $o1[2], $o1[3],
            $o2[0], $o2[1], $o2[2], $o2[3],
            $o3[0], $o3[1], $o3[2], $o3[3],
            $o4[0], $o4[1], $o4[2], $o4[3]
            );
    }

    function cameraTransform($C, $A) {
        $w = $this->normalize($this->addVector($C, $this->scalarProduct($A, -1)));
        $y = array(0, 1, 0);
        $u = $this->normalize($this->crossProduct($y, $w));
        $v = $this->crossProduct($w, $u);
        $t = $this->scalarProduct($C, -1);

        return array(
            $u[0], $v[0], $w[0], 0,
            $u[1], $v[1], $w[1], 0,
            $u[2], $v[2], $w[2], 0,
            $this->dotProduct($u, $t), $this->dotProduct($v, $t), $this->dotProduct($w, $t), 1
            );
    }

    function viewingTransform($fov, $n, $f) {
        $fov *= (M_PI / 180);
        $cot = 1 / tan($fov / 2);

        return array(
            $cot,    0,        0,        0,
            0,        $cot,    0,        0,
            0,        0,        ($f + $n) / ($f - $n),        -1,
            0,        0,        2 * $f * $n / ($f - $n),    0
            );
    }
}

Это расширение класса CCaptchaAction и менять в нем ничего не надо!

Yii Framework Blog img http://dbhelp.rucap1-3d Теперь нам надо создать папку где будет находится шрифт для капчи, для этого войдите в protected/components/views и создайте там папку 3dCaptcha. После этого скачайте шрифт 3DCaptcha.ttf (отсюда) и загрузите его в эту папку.

Вот в принципе и всё. Теперь нам осталось только войти в наш контроллер в котором задаются настройки для вашей капчи и изменить название класса. Заходим в ваш контроллер и находим там:

public function actions()
{
    return array(
        'captcha'=>array(
            ….
            'class'=>'CCaptchaAction',
          ….
        ),
    );
}

Yii Framework Blog img http://dbhelp.rucap3-3d Изменяем 'CCaptchaAction' на 'Yii3dCaptcha' и сохраняем файл. Теперь если вы войдете на страницу где у вас выводилась старая капча – вы увидите красивую 3d.

Если вы хотите изменить цвет фона, цвет текста и кол-во символов в капче – прочитайте мою статью «Изменяем нашу капчу».

В следующей статье про капчи я расскажу Вам как заставить капчу говорить русскими буквами. 

Всем спасибо, заходите еще :)



Если хотите опубликовать этот материал у себя - пожалуйста, разместите ссылку на страницу откуда вы его взяли.
Другие yii статьи:

  1. ... "Всех с 2012-м"

  2. Давайте сегодня немного поработаем со всевозможными настройками которые нам предоставляет стандартный класс для работы с капчей (CCaptcha). next Давайте для начала ... "Изменяем нашу каптчу (captcha)"

  3. Подсветка php кода на страницах блога — штука невероятно удобная. Давайте в двух словах разберемся как я подкрутил её у ... "Подсветка синтаксиса"

romanoza

Было сказано: Воскресенье, 19 Апрель 2009

только вот тут еще шумы есть и синусоида
http://www.pict.com/view/313989/0/test

[adm] zolter

Было сказано: Воскресенье, 19 Апрель 2009

Я шумы не ставил потому что вобще тогда непонятно что на капче :)

[guest] Гость

Было сказано: Четверг, 30 Апрель 2009

Супер капча!

[guest] Kros

Было сказано: Пятница, 16 Апрель 2010

капча интересная, но ни в какую не отображается, простым классом - всё норм, а в 3d - есть только кнопка "новый код". в чём может быть дело?
несколько раз всё проверил - всё на местах вроде, версия 1.1.1. Может в ней по-другому нужно?
Такое чувство, что yii просто не видит компонент, пробовал так:
'class'=>'application.components.Yii3dCaptcha'
безрезультатно

[guest] zolter

Было сказано: Пятница, 16 Апрель 2010

Сейчас попробую у себя на 1.1.1 поднять, до этого только на 1.0.х тестил

[adm] zolter

Было сказано: Пятница, 16 Апрель 2010

На 1.1.1 таки не работает. Сейчас разберемся

[adm] zolter

Было сказано: Пятница, 16 Апрель 2010

Не получилось завести, потом еще поковыряю.
Если у кого есть время то узнал что ругается на строчки:


$x0 = $coord[$count - 1][0] * $scale + $image3d_x / 2;
$y0 = $coord[$count - 1][1] * $scale + $image3d_y / 2;
$x1 = $coord[$count][0] * $scale + $image3d_x / 2;
$y1 = $coord[$count][1] * $scale + $image3d_y / 2;


т.е. иногда $count переваливает за размер массива $coord и ругается что нет такого элемента. поэтому надо разобратся почему либо $coord такой маленький создается, либо цикл где count так долго идет.

[guest] Kros

Было сказано: Пятница, 16 Апрель 2010

спасибо что поковырял))), пока простой попользуюсь))

[adm] zolter

Было сказано: Пятница, 16 Апрель 2010

Та не проблема, самому интересно что за фигня :)

[guest] RedDog

Было сказано: Воскресенье, 06 Февраль 2011

Необходимо исправить следующее:
строку $image2d_y = $this->fontsize * 1.3; на $image2d_y = (int)($this->fontsize * 1.3);
строке $coord = array($image2d_x * $image2d_y); на $coord = array();

Привести в соответствие циклы, где обходится массив $coord:
либо строку for ($y = 0; $y < $image2d_y; $y+=2) (первый цикл, в котором происходит заполнение массива $coord) исправить на for ($y = 0; $y < $image2d_y; $y++) (в итоге на капче будут чаще расположены линии),
либо строку for ($y = 0; $y < $image2d_y; $y++) (второй цикл) исправить на строку for ($y = 0; $y < $image2d_y; $y+=2) чтобы $count не выходил за пределы массива

[guest] zolter

Было сказано: Воскресенье, 06 Февраль 2011

Вот за это большое спасибо!

Оставить комментарий


Код:
Имя: