| Смотрел я вашу "каптчу", готовил ответ, да все некогда было. Что касается вашей "каптчи". Вы можете привести веский аргумент в пользу полезности вот этого действия:
@imagecreate(40, 20) or die("Cannot Initialize new GD image stream"); ?
Назовите причину, по которой это изображение не будет создано (это не инициализация, это создание ресурса в формате GD). Единственная причина по которой ваше изображение не будет создано, это отсутствие у вас расширения GD, или нерабочее расширение. Но скажите, разве нельзя убедиться в этом загодя? В случае рабочей GD, ресурс не будет создан, если ваша каптча будет иметь размеры что-то 100000х100000x24bit. Думаю такую вы создавать не собираетесь.
Ну откуда эта любовь пугать пользователя непонятными для него сообщениями? Вываливать ошибки срипта своего пользователю, в которых в большей степени вина ваша, это подарок для тех кто сможет вам насолить, и неуважение тех, кому они совсем не нужны.
Далее, вы устанавливаете цвет в палитре ($text_color) - зачем, если он у вас в цикле устанавливается вновь и переопределяется?
$number="$numbern"; - это вообще из бог весь какого учебника. Преобразование в строку это (string), а значит сразу:
$number=(string)rand(1000,9999);
imagestring() - рисует строку встроенным шрифтом, максимальный размер которого не может быть более 5, так что ваш запрос rand(2,10) будет просто проигнорирован для больших значений.
Если запускать ваш код в таком виде, как он тут показан, то он естественно будет нерабочим. Ну и бог с ним, так как это далеко от каптчи, распознать такую, это самому ленивому боту и то не в тягость.
Вы можете взять код каптчи здесь, это куда более сердито, чем ваше. Но, если вы разрешаете сообщения всем пользователям, а не только зарегистрированным, либо хотите установить каптчу на форму добавления сообщений не зависимо от регистрации, то набирать буковки/циферки при этом, это раздражительно для пользователя. В этом случае каптча должна быть "удобной" для пользователя, а это, например, щелкнул по ней и все. В общем каптча на основе координат указанной области. Вот такую в примере и напишем.
Изображение каптчи, это не тег img на странице, а сделаем его как фон для элементов. Так как область щелчка должна легко выделяться для пользователя, то изображение сделаем "умеренно большим", и поместим его не как фоновое изображение одному элементу, а как кусочки этого изображения на несколько элементов. Число и порядок этих кусочков будут случайно меняться, как и их размер. А все эти элементы поместим под элемент формы textarea, и будем показывать только в момент отправки формы.
Всего дается три попытки отправить форму, и в случае неудачи переход на страницу ошибки, иначе отправка формы по указанному адресу.
Для того, чтобы иметь возможность отдать каптчу как фоновое изображение, и как набор спрайтов, каптчу придется готовить и отдавать в два этапа - первый, это подготовка каптчи и выдача клиенту координат позиционирования спрайтов, второй, разбиение каптчи на части с выдачей клиенту. То есть, от клиента будут поступать два запроса на каптчу, а значит нужно временное хранение каптчи после первого этапа. Можно написать свой обработчик, который будет впоследствии проверять и удалять не отработанные каптчи, но лучше хранить каптчу текущей сессии в самой сессии. В этом случае о ней позаботиться мусорщик.
Это страница выдачи формы. Условия выдачи формы определяются установками в сесии.
<?php
session_start();
$_SESSION['start'] = 1;
if(!isset($_SESSION['error'])) $_SESSION['error'] = 3;
if(isset($_POST['x']) && isset($_POST['y']) && $_SESSION['error']) {
$crd = &$_SESSION['coords'];
$x = (int)$_POST['x']; $y = (int)$_POST['y'];
if($x<$crd[0] || $x>$crd[2] || $y<$crd[1] || $y>$crd[3]) {
$_SESSION['error']--;
echo json_encode(array('err'=>$_SESSION['error']));
} else {
$_SESSION['ok'] = 1;
echo json_encode('');
}
exit;
}
if($_SESSION['error']) {
?>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script>
$(function() {
$('form').on('submit', function(){
$('button.sender').remove();
$('#frm div').remove();
$('p.info').remove();
$.post('c_check.php', {crd:1}, function(d){
var im = d.pop(),
x = d.pop(),
h = d.pop(),
w = d.pop(),
W = w*x,
H = h*(d.length/x);
$('<div>').appendTo('#frm')
.css({'width':W+'px',
'height':H+'px',
'top':(($('#frm').height()-H)/2)+'px',
'left':(($('#frm').width()-W)/2)+'px'});
var imp = '';
for(var i=0, k=d.length; i<k; i++)
imp += '<div style="background-position: 0 -'+d[i]+'px"></div>';
$('#frm div').html(imp)
.children('div')
.css({'width':w+'px','height':h+'px',
'background-image':'url(c_check.php?'+im+')'});
$('#frm').after('<p class=info>Щелкните в области выделенной пунктиром</p>')
.find('textarea')
.css({'color':'#aaa','cursor':'default'})
.end()
.one('click', function(e){
$('p.error').remove();
var crd = {x:e.pageX-Math.round($(this).children('div')
.offset().left),
y:e.pageY-Math.round($(this).children('div')
.offset().top)};
$.post(location, crd, function(d){
if(typeof d.err != 'undefined') {
if(d.err>0) {
$('#frm').before('<p class=error>Error!</p>');
$('form').trigger('submit');
} else location.href = 'c_error.php';
} else $('form').off('submit').submit();
}, 'json');
});
}, 'json');
return false;
});
});
</script>
<style>
#frm {
width: 700px;
height: 500px;
background: #fff;
position: relative;
}
#frm div {
position: absolute;
z-index: 10;
}
#frm div div {
display: block;
border: 0;
margin: 0;
position: relative;
float: left;
background-repeat: no-repeat;
}
.error {
color: #f00;
}
textarea {
width: 700px;
height: 500px;
resize: none;
position: absolute;
left: 0;
top: 0;
outline: none;
background: none;
z-index: 50;
}
</style>
</head>
<body>
<div id="frm">
<form action="c_send.php" method="post">
<textarea name="mess"></textarea></div>
<p><button class="sender">Send</button></p>
</form>
</body>
</html>
<?php
}
?>
|
Это непосредственно каптча, файл c_check.php:
<?php
session_start();
if(isset($_SESSION['start'])) {
if(isset($_POST['crd'])) {
$color = array(
'noise'=>array(
array(255,255,0),
array(204,204,0),
array(255,204,51),
array(204,153,0),
array(255,153,0),
array(204,102,0),
array(255,204,153),
array(255,204,204),
array(204,153,153),
array(255,102,153),
array(255,0,153),
array(255,204,255),
array(255,0,255),
array(204,102,255),
array(153,51,255),
array(153,153,255),
array(153,204,255),
array(102,204,255),
array(102,204,204),
array(0,255,255),
array(0,255,204),
array(0,204,153),
array(0,255,102),
array(202,204,102)
),
'coords'=>array(
array(153,102,0),
array(255,0,0),
array(0,0,255),
array(51,153,255),
array(51,153,153),
array(0,102,102),
array(0,153,51),
array(102,153,0),
array(51,102,0),
array(51,153,0),
array(51,102,51),
array(0,102,0),
array(0,102,51),
array(0,153,153),
array(51,102,153),
array(0,51,102),
array(0,0,204),
array(0,0,153),
array(102,0,102),
array(153,0,102),
array(150,0,51),
array(204,0,51),
array(153,0,0),
array(204,0,0)
)
);
$W = 400;
$H = 300;
$density = 10;
$x = rand(5,10);
$y = rand(5,10);
$w = floor($W/$x);
$h = floor($H/$y);
$W = $w*$x;
$H = $h*$y;
$im = imagecreate($W, $H);
$white = imagecolorallocate($im, 255, 255, 255);
imagefill($im,0,0,$white);
for($i=0,$k=$H/$density; $i<$k; $i++) {
shuffle($color['noise']);
$noise = $color['noise'][0];
$noise = imagecolorallocate($im, $noise[0], $noise[1], $noise[2]);
switch(rand(0,1)) {
case 0: imagearc($im, rand(0, $W-$x), rand(0, $H-$y),
rand($w, $W-$x), rand($h, $H-$y),
0, 360, $noise);
break;
case 1: imagerectangle($im, rand(0, $W-$w), rand(0, $H-$h),
rand($w, $W-$w), rand($h, $H-$h), $noise);
}
}
$xp = rand(1, $W-$w-1);
$yp = rand(1, $H-$h-1);
shuffle($color['coords']);
$enter = &$color['coords'][0];
$enter = imagecolorallocate($im, $enter[0], $enter[1], $enter[2]);
$style = array($enter, $enter, $enter, $enter,
$white, $white, $white, $white);
imagesetstyle($im, $style);
imageline($im, $xp, $yp, $xp+$w, $yp, IMG_COLOR_STYLED);
imageline($im, $xp, $yp+$h, $xp+$w, $yp+$h, IMG_COLOR_STYLED);
imageline($im, $xp, $yp, $xp, $yp+$h, IMG_COLOR_STYLED);
imageline($im, $xp+$w, $yp, $xp+$w, $yp+$h, IMG_COLOR_STYLED);
$parts = range(0, ($x*$y-1)*$h, $h);
shuffle($parts);
$_SESSION['coords'] = array($xp, $yp, $xp+$w, $yp+$h);
$_SESSION['captcha'] = array(
'parts'=>$parts,
'pieces'=>array($x, $y),
'img'=>md5(time())
);
ob_start();
imagegif($im);
$_SESSION['captcha']['body'] = ob_get_contents();
ob_end_clean();
imagedestroy($im);
array_push($parts, $w, $h, $x, $_SESSION['captcha']['img']);
echo json_encode($parts);
exit;
}
if(isset($_SESSION['captcha']['img']) && isset($_GET[$_SESSION['captcha']['img']])) {
$im = imagecreatefromstring($_SESSION['captcha']['body']);
$W = imagesx($im);
$H = imagesy($im);
$pieces = &$_SESSION['captcha']['pieces'];
$parts = &$_SESSION['captcha']['parts'];
$w = $W/$pieces[0];
$h = $H/$pieces[1];
$y = 0;
$img = imagecreate($w, $pieces[0]*$pieces[1]*$h);
for($i=0,$k=$pieces[0]*$pieces[1]; $i<$k; $i++) {
if(!($i%$pieces[0]) && $i) $y = $h*($i/$pieces[0]);
$x = $w*($i%$pieces[0]);
imagecopy($img, $im, 0, $parts[$i], $x, $y, $w, $h);
}
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header("Content-Type: image/gif");
imagegif($img);
imagedestroy($im);
imagedestroy($img);
unset($_SESSION['captcha']);
exit;
}
}
?>
|
Файл ошибки попыток, c_error.php:
<?php
session_start();
unset($_SESSION['start']);
unset($_SESSION['coords']);
echo 'Попытки закончились, форма не принята.';
?>
|
Файл приема данных формы, c_send.php:
<?php
session_start();
if(isset($_SESSION['start']) && isset($_SESSION['ok'])) {
unset($_SESSION['start']);
unset($_SESSION['coords']);
unset($_SESSION['error']);
echo 'Форма принята.';
}
?>
|
Можно засечь время когда происходит ответ сервера на запрос клиента и при превышении установленного периода ответа клиента, также интерпретировать это как ошибку.
Саму форму можно тоже отдавать только после некоторого подтверждения, той же каптчи (если это не слишком), при этом не обязательно это будет делать серверная сторона. Форму может готовит клиент, и имена полей формы постоянно менять (случайное именование), которые клиенту сообщает сервер. То есть, так чтобы не было на странице с формой имен набивших искомину: message, add, name, user, и т.п., а что-то типа 3b5e0dfdf70179efdc21b9b4e908a682 и т.п., а соответствия этих имен соответствию полей хранится на сервере.
В общем, пробуйте, комбинируйте это с другими традиционными инструментами и возможностями. | |