Captcha component with PhpCaptcha
PhpCaptcha is a library for generating visual and audio CAPTCHAs (completely automated public Turing test to tell computers and humans apart).
Supported Features
1.- Download PhpCaptcha http://www.ejeliot.com/pages/2 and unzip archive in /vendors/phpcaptcha directory
2.- Download Vera font files: http://ftp.gnome.org/pub/GNOME/sources/ttf-bitstream-vera/1.10/ttf-bitstream-vera-1.10.zip and unzip archive in /vendors/phpcaptcha/fonts/
3.- Create Captcha component:
4.- Use it in your Users controller
5.- Display captcha image, in your template view:
Pretty cool, we can reload captcha image if unreadable ;o)
6.- Validate captcha code in your controllers with $this->Captcha->check() method.
With my cake apps I use the improved validation method explained on the bakery. http://bakery.cakephp.org/articles/view/more-improved-advanced-validation
I've added this validation function to validation.php
Download code
And voil�
- Multiple random TrueType fonts
- Character rotation
- Optional chararacter shadow support
- Optional site owner display text
- Random custom background images
- Font size selection
- Greyscale or colour lines and characters
- Character set selection
- Integration of validation function for checking the user entered code with the generated code
NB: The audio CAPTCHA requires the Flite text to speech synthesis engine.
1.- Download PhpCaptcha http://www.ejeliot.com/pages/2 and unzip archive in /vendors/phpcaptcha directory
2.- Download Vera font files: http://ftp.gnome.org/pub/GNOME/sources/ttf-bitstream-vera/1.10/ttf-bitstream-vera-1.10.zip and unzip archive in /vendors/phpcaptcha/fonts/
3.- Create Captcha component:
Component Class:
Download code
<?php
vendor('phpcaptcha'.DS.'php-captcha.inc');
class CaptchaComponent extends Object
{
var $controller;
function startup( &$controller ) {
$this->controller = &$controller;
}
function image(){
$imagesPath = realpath(VENDORS . 'phpcaptcha').'/fonts/';
$aFonts = array(
$imagesPath.'VeraBd.ttf',
$imagesPath.'VeraIt.ttf',
$imagesPath.'Vera.ttf'
);
$oVisualCaptcha = new PhpCaptcha($aFonts, 200, 60);
$oVisualCaptcha->UseColour(true);
//$oVisualCaptcha->SetOwnerText('Source: '.FULL_BASE_URL);
$oVisualCaptcha->SetNumChars(6);
$oVisualCaptcha->Create();
}
function audio(){
$oAudioCaptcha = new AudioPhpCaptcha('/usr/bin/flite', '/tmp/');
$oAudioCaptcha->Create();
}
function check($userCode, $caseInsensitive = true){
if ($caseInsensitive) {
$userCode = strtoupper($userCode);
}
if (!empty($_SESSION[CAPTCHA_SESSION_ID]) && $userCode == $_SESSION[CAPTCHA_SESSION_ID]) {
// clear to prevent re-use
unset($_SESSION[CAPTCHA_SESSION_ID]);
return true;
}
else return false;
}
}
?>
4.- Use it in your Users controller
Controller Class:
Download code
<?php
class UsersController extends AppController
{
...
var $components = array('Captcha');
...
function captcha_image()
{
$this->Captcha->image();
}
function captcha_audio()
{
$this->Captcha->audio();
}
...
}
?>
5.- Display captcha image, in your template view:
View Template:
Download code
<img id="captcha" src="<?php echo $html->url('/users/captcha_image');?>" alt="" />
<a href="javascript:void(0);" onclick="javascript:document.images.captcha.src='<?php echo $html->url('/users/captcha_image');?>?' + Math.round(Math.random(0)*1000)+1">Reload image</a>
Pretty cool, we can reload captcha image if unreadable ;o)
6.- Validate captcha code in your controllers with $this->Captcha->check() method.
With my cake apps I use the improved validation method explained on the bakery. http://bakery.cakephp.org/articles/view/more-improved-advanced-validation
I've added this validation function to validation.php
Download code
function validateCaptcha($fieldName, $params){
$caseInsensitive = true;
$val = $this->data[$this->name][$fieldName];
if ($caseInsensitive) {
$val = strtoupper($val);
}
//php-captcha.inc.php
if(!defined('CAPTCHA_SESSION_ID'))
define('CAPTCHA_SESSION_ID', 'php_captcha');
if (!empty($_SESSION[CAPTCHA_SESSION_ID]) && $val == $_SESSION[CAPTCHA_SESSION_ID]) {
// clear to prevent re-use
unset($_SESSION[CAPTCHA_SESSION_ID]);
return true;
}
return false;
}
And voil�
Comments
Comment
1 Nice Work
Here are a few things i noticed:
on line 14 of captcha.php:
$imagesPath = realpath(VENDORS . 'phpcaptcha').'/fonts/';if your vendors path is inside your app directory, you need something like this:
$imagesPath = APP . 'vendors' . DS . 'phpcaptcha'.'/fonts/';also, for the validation, i changed:
function validateCaptcha($fieldName, $params){$caseInsensitive = true;
to :
function validateCaptcha($fieldName, $caseInsensitive = true){since the
$paramsvar is not used in the function, and it's handy to have the case sensitivity as a parameter.Also, for anyone using this who is also using sessions (either Cake's or your own), be sure to look at the vendor file php-captcha.inc and comment out line 46, where the script calls
session_start(), or you will definitely experience some session problems.Thanks for the work, Spout.
dd
Question
2 I cant get it to work
when i am trying to debug by accesing directly to the url /users/captcha_image says that the view is missing.
what should i do?
Comment
3 I get it to work
i shold recompile my php to support gd libraries, and be carefull with the imagesPath directory...
very great component! thanks Spout.
i realize that after debug step by step the vendor captcha php.
Bug
4 FireFox is unable to work
Thank!!
Question
5 How to add error message that captcha you typed is wrong
Ok, i was able to make this work.
Although, i am a bit clueless. On how will i add an error message on the template itself. Here are sample of my codes.
Controller Class:
<?php
...
if (empty($this->data))
{
$this->render();
}
else
{
if ($this->Email->save($this->data))
{
if ($this->Captcha->check($this->data['Email']['userCode'])) {
// Flash that it has been sent
$this->flash('Your email has been sent.','/emails');
}
}
else
{
$this->set('errorMessage', 'Please correct errors below.');
$this->render();
}
}
...
?>
I was able to validate the usual fields i added:
View Template:
...
<label>Name:</label>
<?php echo $html->input('Email/name', array('class' => 'input', 'style' => 'width: 200px;')); ?>
<?php echo $html->tagErrorMsg('Email/name', 'Name is required.'); ?>
<label>Message:</label>
<?php echo $html->textarea('Email/message', array('rows'=>'10', 'cols'=>'40')); ?>
<?php echo $html->tagErrorMsg('Email/message', 'Message is required.'); ?>
<label>Captcha:</label>
<?php echo $html->input('Email/userCode', array('class' => 'input', 'style' => 'width: 200px;')); ?>
<?php echo $html->tagErrorMsg('Email/userCode', 'Captcha is required.'); ?>
...
which is name and message and captcha (if its empty only).
But to show that what you typed is wrong, i don't know what to do.
Please help!
Comment
6 Captcha with CakePHP 1.2
Model Class:
<?php
$validate = array(
'captcha' => array (
'validateCaptcha' => array (
'rule' => 'validateCaptcha',
'message' => 'Your captcha error message'
)
)
);
function validateCaptcha($check) {
if (!defined('CAPTCHA_SESSION_ID')) {
define('CAPTCHA_SESSION_ID', 'php_captcha');
}
if (!empty ($_SESSION[CAPTCHA_SESSION_ID]) && $check == $_SESSION[CAPTCHA_SESSION_ID]) {
unset ($_SESSION[CAPTCHA_SESSION_ID]);
return true;
}
return false;
}
?>
Note that the field $validate has been declared in my child model class called "Post" and the method validateCaptcha is in the parent AppModel class.
Question
7 reload image not working in Internet Explorer
Can any body suggest me actualy where i am wrong
Waiting for a reply
Question
8 Implementation
Your help is greatly appreciated.
Thank you.
Comment
9 v1.2
seems like the response headers are already sent and php-captcha.inc.php kannt set the mime-type header to image/jpg
Comment
10 solved
der was an space after my closing ?> tag in captcha.php which caused the headers to be send...
:-)
Comment
11 great component
p.s. it seems I'm not able to rate anything on Bakery, as soon as this issue is fixed I'll give it a 5 :-)
Question
12 Problem to get this working
phpcaptcha works fine on mine server as standalone. so all libs a working properly.
if i want to use it within my controller, i just got an "broken-image" in IE or the url in Firefox.
when viewing the source i noticed, that there are html-elements.. looks like some parts of my layout...
maybe this causes the failure?
i got absolutely no idea why my layout is used.
even a
Controller Class:
<?phpfunction captcha_image()
{
$this->layout = FALSE;
$this->Captcha->image();
}
?>
won't work.. any ideas on that?
Question
13 additional info
THREE BLANK SPACES and i can't explain where they are from. even with no layout - these spaces are there..
but guess what: they are on every page generated within cakephp. on every site 3 blank spaces at the beginning. anyone has an idea which scripts generates them? maybe because of them already html output has been startet and the image could be displayed.
thx in advance for every help ( if i get some ^^ )
Question
14 Cant download Component
Comment
15 Working on 1.2
Component Class:
<?php
App::import('Vendor','PhpCaptcha' ,array('file'=>'phpcaptcha/php-captcha.php'));
class CaptchaComponent extends Object
{
var $controller;
function startup( &$controller ) {
$this->controller = &$controller;
}
function image(){
$imagesPath = APP . 'vendors' . DS . 'phpcaptcha'.'/fonts/';
$aFonts = array(
$imagesPath.'VeraBd.ttf',
$imagesPath.'VeraIt.ttf',
$imagesPath.'Vera.ttf'
);
$oVisualCaptcha = new PhpCaptcha($aFonts, 200, 60);
$oVisualCaptcha->UseColour(true);
//$oVisualCaptcha->SetOwnerText('Source: '.FULL_BASE_URL);
//$oVisualCaptcha->SetNumChars(6);
$oVisualCaptcha->Create();
}
function audio(){
$oAudioCaptcha = new AudioPhpCaptcha('/usr/bin/flite', '/tmp/');
$oAudioCaptcha->Create();
}
function check($userCode, $caseInsensitive = true){
if ($caseInsensitive) {
$userCode = strtoupper($userCode);
}
if (!empty($_SESSION[CAPTCHA_SESSION_ID]) && $userCode == $_SESSION[CAPTCHA_SESSION_ID]) {
// clear to prevent re-use
unset($_SESSION[CAPTCHA_SESSION_ID]);
return true;
}
else return false;
}
}
?>
This is action which renders captcha:
function captcha_image()
{
Configure::write('debug',0);
$this->layout = null;
$this->Captcha2->image();
$this->render();
}