Настройка 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.
Если вы хотите изменить цвет фона, цвет текста и кол-во символов в капче – прочитайте мою статью «Изменяем нашу капчу».
В следующей статье про капчи я расскажу Вам как заставить капчу говорить русскими буквами.
Всем спасибо, заходите еще :)
Если хотите опубликовать этот материал у себя - пожалуйста, разместите ссылку на страницу откуда вы его взяли.
- Уже достаточно давно мною была написана маленькая система управления сайтом для Творческого объединения Art of Art. Изначально все страницы сайта ... "artofart.ru - пишем cms на yii"
- Рано или позно каждый программист подходит к моменту написания собственной CMS или системы управления сайтами. Несмотря на обильное кол-во опенсорс ... "Идея проекта [OzYii]"
- На самом деле Yii является отличным инструментом для создания приложения на PHP. Первое с чем сталкивается разработчик на Yii ... "Kohana хелперы в Yii"

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


