Настройка 3d капчи на Yii
Рубрика: Работаем с капчей (captcha)
18 Апр. 2009Эта статья устарела т.к. была написана для yii версии 1.0.х; Если вы используете более новую версию - у вас могут возникнуть ошибки из-за несовместимости. Обычно ответы на все вопросы работы на 1.1.х были описаны в комментариях ниже статьи.
Честно сказать не думал что когда то буду ковырять капчу более чем на уровне настройки, но не тут то было. Спасибо romanoza за ссылку на 3d капчу которая сразу меня заинтересовала.
Посидел я пару минут в прочтении комментариев на хабре и обнаружил ссылку на либсу с исходным кодом http://code.google.com/p/3dcaptcha/. (Именно её я решил прикрутить к Yii). Если вам вдруг захотелось такую капчу на сайте – я расскажу как её прикрутить, но для начала вы должны убедиться что:
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 и менять в нем ничего не надо! Теперь нам надо создать папку где будет находится шрифт для капчи, для этого войдите в protected/components/views и создайте там папку 3dCaptcha. После этого скачайте шрифт 3DCaptcha.ttf (отсюда) и загрузите его в эту папку.
Вот в принципе и всё. Теперь нам осталось только войти в наш контроллер в котором задаются настройки для вашей капчи и изменить название класса. Заходим в ваш контроллер и находим там:
public function actions()
{
return array(
'captcha'=>array(
….
'class'=>'CCaptchaAction',
….
),
);
}
Изменяем 'CCaptchaAction' на 'Yii3dCaptcha' и сохраняем файл. Теперь если вы войдете на страницу где у вас выводилась старая капча – вы увидите красивую 3d.
Если вы хотите изменить цвет фона, цвет текста и кол-во символов в капче – прочитайте мою статью «Изменяем нашу капчу».
В следующей статье про капчи я расскажу Вам как заставить капчу говорить русскими буквами.
Всем спасибо, заходите еще :)
Если хотите опубликовать этот материал у себя - пожалуйста, разместите ссылку на страницу откуда вы его взяли.
- Ох много времени я потратил на настройку своего блога на новом хостинге. Получилось так что я взял вдс с isp ... "Проблемы с ЧПУ yii + isp"
- Эта статья устарела т.к. была написана для yii версии 1.0.х; Если вы используете более новую версию - у вас могут ... "Урок 8 : Подключаем дизайн"
- В данном переводе раскрыта идея как закрыть сайт от гостей. т.е. пользователи должны войти в систему прежде чем смогут увидеть ... "Закрываем сайт от лишних глаз"

romanoza
Было сказано: Воскресенье, 19 Апрель 2009
только вот тут еще шумы есть и синусоида
http://www.pict.com/view/313989/0/test

[adm] zolter
Было сказано: Воскресенье, 19 Апрель 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
Не получилось завести, потом еще поковыряю.
Если у кого есть время то узнал что ругается на строчки:
$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
спасибо что поковырял))), пока простой попользуюсь))

[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 не выходил за пределы массива


