DbHelp.ru
Маленький Yii блог
Комментарии
Denis: По поводу вышеописанной проблемы мой код можно посмот...
Denis: Все сделал с учетом вышеописанных комментов. После доб...
Гость: Спасибо!
Гость: что это за 'path/to/localFile' ??? Слабо было описать ? Я туда под...
Mj: Vic спс большое и конечно же сайту тоже...



Сервер 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. Сегодня обнаружил что мой бывший сокурсник написал свой некий мод на Yii Blog. Исходные коды я не смотрел, но ... "Yii blog new [update]"

  2. С целью легкого интегрирование фреймворка со сторонними библиотеками, разработчики Yii не используют в коде глобальных функций. В Yii все ... "Используем функции быстрого доступа (глобальные функции в Yii)"

  3. Флеш-сообщения используются для того, что бы сохранить в сессии некий текст, и после отображения его пользователю - сразу удалить. Понятие ... "Как работать с флеш-сообщениями"

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

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

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


Код:
Имя: