104 Commits

Author SHA1 Message Date
f2e07fe6f2 Merge branch 'master' of github.com:Gregwar/CaptchaBundle 2014-03-19 16:15:10 +01:00
ab786e3b0e Updating to v1.0.11 2014-03-19 16:14:56 +01:00
f206bb66cc Merge pull request #80 from gondo/master
added Czech translation
2014-03-06 17:23:06 +01:00
3340af5c43 Create validators.cs.yml 2014-03-06 16:24:41 +01:00
bc3fdda50b Rename gregwar_captcha.cz.yml to gregwar_captcha.cs.yml 2014-03-06 16:22:57 +01:00
b2340539d2 Create gregwar_captcha.cz.yml 2014-03-06 16:22:28 +01:00
8199d20092 Merge branch 'master' of github.com:Gregwar/CaptchaBundle 2014-02-14 11:29:31 +01:00
8770c50ff0 Updating gregwar/captcha to v1.0.10 2014-02-14 11:29:03 +01:00
721a2b1f3d Merge pull request #78 from verschoof/translation/english
Added English translation
2014-02-14 10:36:48 +01:00
e2f1966bfb Merge pull request #77 from verschoof/translation/dutch
Added Dutch translation
2014-02-14 10:35:57 +01:00
2f508ddd07 Added English translation 2014-02-14 10:26:58 +01:00
2e803e1471 Added Dutch translation 2014-02-14 10:24:32 +01:00
800643d72c Merge pull request #74 from regisnew/master
[ADD]
2013-11-25 02:48:00 -08:00
c09b7245b1 [ADD]
- translate validators to Brazilian portuguese.
2013-11-25 08:44:26 -02:00
c3c8904862 Merge pull request #73 from regisnew/master
[ADD]
2013-11-23 02:20:39 -08:00
9d4f95e39b [ADD]
- Brazilian Portuguese translate
2013-11-22 19:52:20 -02:00
107dbe33e4 Adding disabled global option (fixes #72) 2013-11-19 10:51:58 +01:00
6b4e7db721 Merge branch 'master' of github.com:Gregwar/CaptchaBundle 2013-09-21 13:45:18 +02:00
aef6a785ca Merge pull request #69 from oktron/master
Add german translation
2013-09-06 08:01:56 -07:00
fc72ab4f6c Add german translation 2013-09-06 16:59:19 +02:00
e3727a975d Merge branch 'master' of github.com:Gregwar/CaptchaBundle 2013-08-14 09:45:48 +02:00
9d741e0ac6 v1.0.8 2013-08-14 09:45:35 +02:00
0ab26cac81 Merge pull request #67 from sergeylunev/master
Add translation to russian.
2013-08-08 05:38:09 -07:00
ddab3ef005 Add translation to russian. 2013-08-08 16:31:34 +04:00
0b08b38cb0 Enhancing documentation about font option (fixes #66) 2013-08-07 15:46:23 +02:00
3f64e064d3 Mergin & fixing text_color & background_color (fixes #57) 2013-08-07 11:18:16 +02:00
9578caab10 Merge remote-tracking branch 'color/master' 2013-08-07 11:08:16 +02:00
7f30646c68 Moving the "reload without as_url" exception to catch more cases (see 2013-08-07 10:57:56 +02:00
5efb9a73b5 Updating Captcha to v1.0.7 2013-07-24 22:54:51 +02:00
daec205870 Adjust text_color execption line indention. 2013-04-29 10:17:04 +08:00
1881d292b8 Fixed text_color and background_color validation exception. 2013-04-29 10:13:58 +08:00
181b0cd0d0 Added text_color option. 2013-04-29 08:50:17 +08:00
c21bbf13c5 Adding interpolation option 2013-04-24 18:11:08 +02:00
7ca6dacb3b Fixing background color 2013-04-22 00:41:07 +02:00
39e2f390f0 Merge commit 'be90f19'
Conflicts:
	DependencyInjection/Configuration.php
	Generator/CaptchaGenerator.php
	composer.json
2013-04-22 00:35:49 +02:00
be90f19f3a Adding backgroundColor option (fixes #55) 2013-04-22 00:32:40 +02:00
0b56f5cd87 Merge pull request #53 from bftanase/master
Options to limit the number of lines to draw on captcha text
2013-03-21 04:42:41 -07:00
2d8d9f3b5f options to fine tune captcha image: max_front_lines, max_behind_lines 2013-03-13 12:02:41 +02:00
4e13d2d4cb Moving translation logic to the validator (fixes for 2.2, see #52) 2013-03-03 13:54:31 +01:00
1fef229c8f Merge pull request #51 from dreipunktnull/master
[TASK] Update deprecated call
2013-02-25 02:47:05 -08:00
de2ab52bae Added spanish translations 2013-02-25 09:51:34 +01:00
6f546713a9 Merge pull request #50 from palmasev/master
Spanish translations
2013-02-21 03:58:49 -08:00
877e2d740d Added spanish translations 2013-02-21 11:42:36 +01:00
5f2afc623c Changed deprecated default option 2013-02-13 18:29:40 +01:00
45e4f72394 [CaptchaType] Adding length to the persisted options to work with as_url
(see #49)
2013-02-12 11:23:22 +01:00
19142f30e4 Merge pull request #48 from silentinet/master
Italian translations
2013-02-05 05:43:37 -08:00
6c953ea228 - Italian translations 2013-02-05 14:28:58 +01:00
206869dd67 Merge pull request #47 from bftanase/master
Translation for romania
2013-02-03 04:50:03 -08:00
7b819872b4 translations for romanian 2013-02-03 11:58:11 +02:00
95342e80a9 Avoiding check that as_url is globally set (fixes #46) 2013-01-23 19:58:45 +01:00
34de9cd753 Merge branch 'master' of github.com:Gregwar/CaptchaBundle 2013-01-21 17:29:31 +01:00
83a5a78c16 Updating LICENSE dates 2013-01-21 17:29:05 +01:00
42f6e92560 Cleaning session usage to keep options during as_url (fixes #41) 2013-01-21 15:18:36 +01:00
2e17d9c035 Improving quality and image default size 2013-01-21 15:18:19 +01:00
4bb240be05 Updating gregwar/captcha to v1.0.1 2013-01-21 14:09:52 +01:00
c224b93033 Adding the "distortion" option 2013-01-21 14:04:53 +01:00
0108219264 Merge branch 'master' of github.com:Gregwar/CaptchaBundle 2013-01-21 14:01:51 +01:00
aed2fa343e Depending on gregwar/captcha (see #42) 2013-01-21 14:01:31 +01:00
2ea29e0f9a Merge pull request #44 from efeencheung/master
Add 2 zh_CN thans-unit in validators
2013-01-21 05:01:12 -08:00
73b78ec537 add Renew trans-unit 2013-01-08 16:43:32 +08:00
d103614cc7 add simple chinese translation 2013-01-08 16:10:26 +08:00
95b28d9cec Merge branch 'master' of github.com:Gregwar/CaptchaBundle 2013-01-04 08:32:42 +01:00
435cc44f91 Renaming view variable to image_id to avoid collision with the field id (see #43) 2013-01-04 08:31:59 +01:00
c551ca018e Removing useless "use" 2012-12-27 13:05:56 +01:00
9e599febb0 Removing useless "use" 2012-12-27 13:04:44 +01:00
35405aca2e Adds an option to renew the code (fixes #43) 2012-12-25 20:07:00 +01:00
6b340eb258 Adding french translation & info in the README 2012-12-25 19:48:25 +01:00
c8c1bd0011 [Configuration] Defaulting keep_value to false 2012-12-25 19:17:25 +01:00
8d6a628dda [composer.json] Fixing homepage 2012-12-08 00:10:04 +01:00
d91b9a9444 [composer.json] Adding Jeremy Livingston's e-mail 2012-12-05 10:16:57 +01:00
11fe650bde Adding option "humanity" (fixes #40) 2012-12-04 12:20:23 +01:00
3794a12e80 [Generator] Renaming, and separating the PhraseBuilder 2012-12-04 11:51:47 +01:00
516046a6b5 Merge remote-tracking branch 'jeremy/logicsplit'
Conflicts:
	Generator/CaptchaGenerator.php
	Resources/config/services.yml
2012-12-04 11:42:35 +01:00
b993e4b4cd [composer.json] Adding Jeremy Livingston to authors list 2012-12-04 11:37:21 +01:00
fd2b602196 Fixing the exception throwing and message 2012-12-04 11:35:36 +01:00
31d080a9b7 Fixing session key, removing valid_keys which was painful to transport
the whitelist in the session (see #36)
2012-12-04 11:33:54 +01:00
39532390e1 [Controller] Setting Content-type to image/jpeg 2012-12-04 10:57:52 +01:00
c1b702566b Merge remote-tracking branch 'remotes/jeremy/urlgeneration'
Conflicts:
	DependencyInjection/GregwarCaptchaExtension.php
2012-12-04 10:51:08 +01:00
8885e6bcac Add class documentation 2012-12-03 19:02:43 -05:00
beec51e975 Merge remote branch 'origin/urlgeneration' into logicsplit 2012-12-03 18:56:00 -05:00
1437f0c7e0 Remove caching of form name for key 2012-12-03 18:54:39 -05:00
ce1b590ae9 Separate generation concerns into different services 2012-12-03 18:48:32 -05:00
ba9e0818a5 Add "valid_keys" documentation 2012-12-03 15:01:46 -05:00
4d7534351d Update documentation 2012-12-03 14:57:38 -05:00
fef3e306fd Optimize formatting for Symfony coding standards 2012-12-03 14:55:11 -05:00
9fc82c8453 Add key parameter to URL generation method. 2012-12-03 14:49:17 -05:00
0bd0173551 CS 2012-12-02 13:17:04 +01:00
a8b45f737d [view] Adding empty "alt" to ass HTML5 validator (fixes #37) 2012-12-02 13:12:12 +01:00
a41e4dd865 Add URL generation method and update to Symfony 2.1 standards 2012-11-13 22:33:36 -05:00
78e1cee035 [README] Fixing endspaceless (fixes #35) 2012-10-27 16:15:05 +02:00
51edd50acc [README] Changing composer (fixes #33) 2012-08-21 13:20:32 +02:00
4c28dd420f Merge pull request #32 from fabienpomerol/patch-1
Fixed Typo
2012-08-11 02:26:40 -07:00
94f6b5277d Fixed Typo
typo
2012-08-11 12:24:15 +03:00
141b976479 Merge pull request #29 from piscis/patch-1
Fix Bundle for symfony 2.1 beta4
2012-07-24 14:17:21 -07:00
22f494e27a Merge pull request #30 from piscis/patch-2
Fixing call to undefined methode
2012-07-24 14:14:43 -07:00
c025ea9b0d Fixing call to undefined methode 2012-07-24 16:31:51 +03:00
b9f003c9cf Fix Bundle for symfony 2.1 beta4 2012-07-24 00:02:37 +03:00
df2f9b3ada Merge pull request #28 from rjmunro/patch-1
Switch suggested git URL to http:// instead of git://
2012-07-04 03:42:01 -07:00
d89a966315 Switch suggested git URL to http:// instead of git://
A git url of git://github.com/Gregwar/CaptchaBundle.git didn't work for me.

http:// is more reliable, and is used by all the deps in the symfony standard edition  http://github.com/symfony/symfony-standard/blob/2.0/deps
2012-07-04 11:16:45 +02:00
589e8f0bcb Merge pull request #27 from jeremylivingston/bypass-code
Add bypass_code configuration parameter
2012-06-30 09:37:42 -07:00
f9885acde9 Add bypass_code configuration parameter to force captcha validation for development/automation. 2012-06-30 12:18:05 -04:00
9980108880 [README] Clarifying 2.0 (fixes #26) 2012-06-29 19:21:16 +02:00
ece2a68170 Merge pull request #25 from jeremylivingston/messageconfig
Add invalid_message configuration option
2012-06-29 07:53:28 -07:00
8d54bfd598 Add invalid_message configuration option 2012-06-29 10:23:05 -04:00
35 changed files with 652 additions and 346 deletions

View File

@ -0,0 +1,59 @@
<?php
namespace Gregwar\CaptchaBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/**
* Generates a captcha via a URL
*
* @author Jeremy Livingston <jeremy.j.livingston@gmail.com>
*/
class CaptchaController extends Controller
{
/**
* Action that is used to generate the captcha, save its code, and stream the image
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param string $key
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function generateCaptchaAction(Request $request, $key)
{
$options = $this->container->getParameter('gregwar_captcha.config');
$session = $this->get('session');
$whitelistKey = $options['whitelist_key'];
$isOk = false;
if ($session->has($whitelistKey)) {
$keys = $session->get($whitelistKey);
if (is_array($keys) && in_array($key, $keys)) {
$isOk = true;
}
}
if (!$isOk) {
throw $this->createNotFoundException('Unable to generate a captcha via an URL with this session key.');
}
/* @var \Gregwar\CaptchaBundle\Generator\CaptchaGenerator $generator */
$generator = $this->container->get('gregwar_captcha.generator');
$persistedOptions = $session->get($key, array());
$options = array_merge($options, $persistedOptions);
$phrase = $generator->getPhrase($options);
$generator->setPhrase($phrase);
$persistedOptions['phrase'] = $phrase;
$session->set($key, $persistedOptions);
$response = new Response($generator->generate($options));
$response->headers->set('Content-type', 'image/jpeg');
return $response;
}
}

View File

@ -9,30 +9,45 @@ class Configuration implements ConfigurationInterface
{
/**
* Generates the configuration tree.
*
* @return TreeBuilder
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('gregwar_captcha', 'array');
$rootNode = $treeBuilder->root('gregwar_captcha');
$rootNode
->addDefaultsIfNotSet()
->children()
->scalarNode('length')->defaultValue(5)->end()
->scalarNode('width')->defaultValue(120)->end()
->scalarNode('height')->defaultValue(40)->end()
->scalarNode('width')->defaultValue(130)->end()
->scalarNode('height')->defaultValue(50)->end()
->scalarNode('font')->defaultValue(__DIR__.'/../Generator/Font/captcha.ttf')->end()
->scalarNode('keep_value')->defaultValue(true)->end()
->scalarNode('keep_value')->defaultValue(false)->end()
->scalarNode('charset')->defaultValue('abcdefhjkmnprstuvwxyz23456789')->end()
->scalarNode('as_file')->defaultValue(false)->end()
->scalarNode('as_url')->defaultValue(false)->end()
->scalarNode('reload')->defaultValue(false)->end()
->scalarNode('image_folder')->defaultValue('captcha')->end()
->scalarNode('web_path')->defaultValue('%kernel.root_dir%/../web')->end()
->scalarNode('gc_freq')->defaultValue(100)->end()
->scalarNode('expiration')->defaultValue(60)->end()
->scalarNode('quality')->defaultValue(15)->end()
->scalarNode('quality')->defaultValue(30)->end()
->scalarNode('invalid_message')->defaultValue('Bad code value')->end()
->scalarNode('bypass_code')->defaultValue(null)->end()
->scalarNode('whitelist_key')->defaultValue('captcha_whitelist_key')->end()
->scalarNode('humanity')->defaultValue(0)->end()
->scalarNode('distortion')->defaultValue(true)->end()
->scalarNode('max_front_lines')->defaultValue(null)->end()
->scalarNode('max_behind_lines')->defaultValue(null)->end()
->scalarNode('interpolation')->defaultValue(true)->end()
->arrayNode('text_color')->prototype('scalar')->end()->end()
->arrayNode('background_color')->prototype('scalar')->end()->end()
->scalarNode('disabled')->defaultValue(false)->end()
->end()
;
return $treeBuilder;
}
}

View File

@ -4,15 +4,22 @@ namespace Gregwar\CaptchaBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
/**
* Extension used to load the configuration, set parameters, and initialize the captcha view
*
* @author Gregwar <g.passault@gmail.com>
*/
class GregwarCaptchaExtension extends Extension
{
/**
* @param array $configs
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
@ -20,11 +27,13 @@ class GregwarCaptchaExtension extends Extension
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('gregwar_captcha.config', $config);
$container->setParameter('gregwar_captcha.config.image_folder', $config['image_folder']);
$container->setParameter('gregwar_captcha.config.web_path', $config['web_path']);
$container->setParameter('gregwar_captcha.config.gc_freq', $config['gc_freq']);
$container->setParameter('gregwar_captcha.config.expiration', $config['expiration']);
$container->setParameter('gregwar_captcha.config.whitelist_key', $config['whitelist_key']);
$resources = $container->getParameter('twig.form.resources');
$container->setParameter('twig.form.resources',array_merge(array('GregwarCaptchaBundle::captcha.html.twig'), $resources));
$container->setParameter('twig.form.resources', array_merge(array('GregwarCaptchaBundle::captcha.html.twig'), $resources));
}
}

View File

@ -3,276 +3,161 @@
namespace Gregwar\CaptchaBundle\Generator;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\RouterInterface;
use Gregwar\Captcha\CaptchaBuilderInterface;
use Gregwar\Captcha\PhraseBuilderInterface;
/**
* Generates a CAPTCHA image
*/
class CaptchaGenerator {
/**
* Name of folder for captcha images
* @var string
*/
public $imageFolder;
/**
* Absolute path to public web folder
* @var string
*/
public $webPath;
/**
* Frequence of garbage collection in fractions of 1
* @var int
*/
public $gcFreq;
/**
* Captcha Font
* @var string
*/
public $font;
/**
* Maximum age of images in minutes
* @var int
*/
public $expiration;
/**
* Random fingerprint
* Useful to be able to regenerate exactly the same image
* @var array
*/
public $fingerprint;
/**
* Should fingerprint be used ?
* @var boolean
*/
public $use_fingerprint;
/**
* The captcha code
* @var string
*/
public $value;
/**
* Captcha quality
* @var int
*/
public $quality;
public function __construct($value, $imageFolder, $webPath, $gcFreq, $expiration, $font, $fingerprint, $quality)
{
$this->value = $value;
$this->imageFolder = $imageFolder;
$this->webPath = $webPath;
$this->gcFreq = intval($gcFreq);
$this->expiration = intval($expiration);
$this->font = $font;
$this->fingerprint = $fingerprint;
$this->use_fingerprint = (bool)$fingerprint;
$this->quality = intval($quality);
}
/**
* Get the captcha embeded code
*/
public function getCode($width = 120, $height = 40)
{
return 'data:image/jpeg;base64,'.base64_encode($this->generate($width, $height));
}
/**
* Creates a captcha image with provided dimensions
* and randomly executes a garbage collection
* Uses configuration parameters to call the services that generate captcha images
*
* @param int $width
* @param int $height
* @return string Web path to the created image
* @author Gregwar <g.passault@gmail.com>
* @author Jeremy Livingston <jeremy.j.livingston@gmail.com>
*/
public function getFile($width = 120, $height = 40)
{
if (mt_rand(1, $this->gcFreq) == 1) {
$this->garbageCollection();
}
class CaptchaGenerator
{
/**
* @var \Symfony\Component\Routing\RouterInterface
*/
protected $router;
return $this->generate($width, $height, true);
/**
* @var CaptchaBuilder
*/
protected $builder;
/**
* @var PhraseBuilder
*/
protected $phraseBuilder;
/**
* @var ImageFileHandler
*/
protected $imageFileHandler;
/**
* @param \Symfony\Component\Routing\RouterInterface $router
* @param CaptchaBuilderInterface $builder
* @param ImageFileHandlerInterface $imageFileHandler
*/
public function __construct(RouterInterface $router, CaptchaBuilderInterface $builder, PhraseBuilderInterface $phraseBuilder, ImageFileHandler $imageFileHandler)
{
$this->router = $router;
$this->builder = $builder;
$this->phraseBuilder = $phraseBuilder;
$this->imageFileHandler = $imageFileHandler;
}
/**
* Returns a random number or the next number in the
* fingerprint
*/
public function rand($min, $max)
{
if (!is_array($this->fingerprint)) {
$this->fingerprint = array();
}
if ($this->use_fingerprint) {
$value = current($this->fingerprint);
next($this->fingerprint);
} else {
$value = mt_rand($min, $max);
$this->fingerprint[] = $value;
}
return $value;
}
/**
* Get the CAPTCHA fingerprint
*/
public function getFingerprint()
{
return $this->fingerprint;
}
/**
* Deletes all images in the configured folder
* that are older than 10 minutes
* Get the captcha URL, stream, or filename that will go in the image's src attribute
*
* @return void
* @param $key
* @param array $options
*
* @return array
*/
public function garbageCollection()
public function getCaptchaCode(array &$options)
{
$finder = new Finder();
$criteria = sprintf('<= now - %s minutes', $this->expiration);
$finder->in($this->webPath . '/' . $this->imageFolder)
->date($criteria);
$this->builder->setPhrase($this->getPhrase($options));
foreach($finder->files() as $file)
{
unlink($file->getPathname());
// Randomly execute garbage collection and returns the image filename
if ($options['as_file']) {
$this->imageFileHandler->collectGarbage();
return $this->generate($options);
}
// Returns the image generation URL
if ($options['as_url']) {
return $this->router->generate('gregwar_captcha.generate_captcha', array('key' => $options['session_key']));
}
return 'data:image/jpeg;base64,' . base64_encode($this->generate($options));
}
/**
* Generate the image
* Sets the phrase to the builder
*/
public function generate($width, $height, $createFile = false)
public function setPhrase($phrase)
{
$i = imagecreatetruecolor($width,$height);
$col = imagecolorallocate($i, $this->rand(0,110), $this->rand(0,110), $this->rand(0,110));
imagefill($i, 0, 0, 0xFFFFFF);
// Draw random lines
for ($t=0; $t<10; $t++) {
$tcol = imagecolorallocate($i, 100+$this->rand(0,150), 100+$this->rand(0,150), 100+$this->rand(0,150));
$Xa = $this->rand(0, $width);
$Ya = $this->rand(0, $height);
$Xb = $this->rand(0, $width);
$Yb = $this->rand(0, $height);
imageline($i, $Xa, $Ya, $Xb, $Yb, $tcol);
$this->builder->setPhrase($phrase);
}
// Write CAPTCHA text
$size = $width/strlen($this->value);
$font = $this->font;
$box = imagettfbbox($size, 0, $font, $this->value);
$txt_width = $box[2] - $box[0];
$txt_height = $box[1] - $box[7];
/**
* @param string $key
* @param array $options
*
* @return string
*/
public function generate(array &$options)
{
$this->builder->setDistortion($options['distortion']);
imagettftext($i, $size, 0, ($width-$txt_width)/2, ($height-$txt_height)/2+$size, $col, $font, $this->value);
$this->builder->setMaxFrontLines($options['max_front_lines']);
$this->builder->setMaxBehindLines($options['max_behind_lines']);
// Distort the image
$X = $this->rand(0, $width);
$Y = $this->rand(0, $height);
$Phase=$this->rand(0,10);
$Scale = 1.3 + $this->rand(0,10000)/30000;
$Amp=1+$this->rand(0,1000)/1000;
$out = imagecreatetruecolor($width, $height);
for ($x=0; $x<$width; $x++)
for ($y=0; $y<$height; $y++) {
$Vx=$x-$X;
$Vy=$y-$Y;
$Vn=sqrt($Vx*$Vx+$Vy*$Vy);
if ($Vn!=0) {
$Vn2=$Vn+4*sin($Vn/8);
$nX=$X+($Vx*$Vn2/$Vn);
$nY=$Y+($Vy*$Vn2/$Vn);
} else {
$nX=$X;
$nY=$Y;
}
$nY = $nY+$Scale*sin($Phase + $nX*0.2);
$p = $this->bilinearInterpolate($nX-floor($nX), $nY-floor($nY),
$this->getCol($i,floor($nX),floor($nY)),
$this->getCol($i,ceil($nX),floor($nY)),
$this->getCol($i,floor($nX),ceil($nY)),
$this->getCol($i,ceil($nX),ceil($nY)));
if ($p==0) {
$p=0xFFFFFF;
if (isset($options['text_color']) && $options['text_color']) {
if (count($options['text_color']) !== 3) {
throw new \RuntimeException('text_color should be an array of r, g and b');
}
imagesetpixel($out, $x, $y, $p);
$color = $options['text_color'];
$this->builder->setTextColor($color[0], $color[1], $color[2]);
}
// Renders it
if (!$createFile) {
if (isset($options['background_color']) && $options['background_color']) {
if (count($options['background_color']) !== 3) {
throw new \RuntimeException('background_color should be an array of r, g and b');
}
$color = $options['background_color'];
$this->builder->setBackgroundColor($color[0], $color[1], $color[2]);
}
$this->builder->setInterpolation($options['interpolation']);
$fingerprint = isset($options['fingerprint']) ? $options['fingerprint'] : null;
$content = $this->builder->build(
$options['width'],
$options['height'],
$options['font'],
$fingerprint
)->getGd();
if ($options['keep_value']) {
$options['fingerprint'] = $this->builder->getFingerprint();
}
if (!$options['as_file']) {
ob_start();
imagejpeg($out, null, $this->quality);
imagejpeg($content, null, $options['quality']);
return ob_get_clean();
}
return $this->imageFileHandler->saveAsFile($content);
}
/**
* @param string $key
* @param array $options
*
* @return string
*/
public function getPhrase(array &$options)
{
// Get the phrase that we'll use for this image
if ($options['keep_value'] && isset($options['phrase'])) {
$phrase = $options['phrase'];
} else {
// Check if folder exists and create it if not
if (!file_exists($this->webPath . '/' . $this->imageFolder)) {
mkdir($this->webPath . '/' . $this->imageFolder, 0755);
}
$filename = md5(uniqid()) . '.jpg';
$filepath = $this->webPath . '/' . $this->imageFolder . '/' . $filename;
imagejpeg($out, $filepath, 15);
return '/' . $this->imageFolder . '/' . $filename;
}
$phrase = $this->phraseBuilder->build($options['length'], $options['charset']);
$options['phrase'] = $phrase;
}
protected function getCol($image, $x, $y)
{
$L = imagesx($image);
$H = imagesy($image);
if ($x<0 || $x>=$L || $y<0 || $y>=$H)
return 0xFFFFFF;
else return imagecolorat($image, $x, $y);
}
protected function getRGB($col) {
return array(
(int)($col >> 16) & 0xff,
(int)($col >> 8) & 0xff,
(int)($col) & 0xff,
);
}
function bilinearInterpolate($x, $y, $nw, $ne, $sw, $se)
{
list($r0, $g0, $b0) = $this->getRGB($nw);
list($r1, $g1, $b1) = $this->getRGB($ne);
list($r2, $g2, $b2) = $this->getRGB($sw);
list($r3, $g3, $b3) = $this->getRGB($se);
$cx = 1.0 - $x;
$cy = 1.0 - $y;
$m0 = $cx * $r0 + $x * $r1;
$m1 = $cx * $r2 + $x * $r3;
$r = (int)($cy * $m0 + $y * $m1);
$m0 = $cx * $g0 + $x * $g1;
$m1 = $cx * $g2 + $x * $g3;
$g = (int)($cy * $m0 + $y * $m1);
$m0 = $cx * $b0 + $x * $b1;
$m1 = $cx * $b2 + $x * $b3;
$b = (int)($cy * $m0 + $y * $m1);
return ($r << 16) | ($g << 8) | $b;
return $phrase;
}
}

View File

@ -0,0 +1,106 @@
<?php
namespace Gregwar\CaptchaBundle\Generator;
use Symfony\Component\Finder\Finder;
/**
* Handles actions related to captcha image files including saving and garbage collection
*
* @author Gregwar <g.passault@gmail.com>
* @author Jeremy Livingston <jeremy@quizzle.com>
*/
class ImageFileHandler
{
/**
* Name of folder for captcha images
* @var string
*/
protected $imageFolder;
/**
* Absolute path to public web folder
* @var string
*/
protected $webPath;
/**
* Frequency of garbage collection in fractions of 1
* @var int
*/
protected $gcFreq;
/**
* Maximum age of images in minutes
* @var int
*/
protected $expiration;
/**
* @param $imageFolder
* @param $webPath
* @param $gcFreq
* @param $expiration
*/
public function __construct($imageFolder, $webPath, $gcFreq, $expiration)
{
$this->imageFolder = $imageFolder;
$this->webPath = $webPath;
$this->gcFreq = $gcFreq;
$this->expiration = $expiration;
}
/**
* Saves the provided image content as a file
*
* @param string $contents
*
* @return string
*/
public function saveAsFile($contents)
{
$this->createFolderIfMissing();
$filename = md5(uniqid()) . '.jpg';
$filePath = $this->webPath . '/' . $this->imageFolder . '/' . $filename;
imagejpeg($contents, $filePath, 15);
return '/' . $this->imageFolder . '/' . $filename;
}
/**
* Randomly runs garbage collection on the image directory
*
* @return bool
*/
public function collectGarbage()
{
if (!mt_rand(1, $this->gcFreq) == 1) {
return false;
}
$this->createFolderIfMissing();
$finder = new Finder();
$criteria = sprintf('<= now - %s minutes', $this->expiration);
$finder->in($this->webPath . '/' . $this->imageFolder)
->date($criteria);
foreach($finder->files() as $file) {
unlink($file->getPathname());
}
return true;
}
/**
* Creates the folder if it doesn't exist
*/
protected function createFolderIfMissing()
{
if (!file_exists($this->webPath . '/' . $this->imageFolder)) {
mkdir($this->webPath . '/' . $this->imageFolder, 0755);
}
}
}

View File

@ -1,4 +1,4 @@
Copyright (c) <2011> Grégoire Passault
Copyright (c) <2011-2013> Grégoire Passault
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -25,8 +25,9 @@ Add the following lines to your `deps` file:
```
[GregwarCaptchaBundle]
git=git://github.com/Gregwar/CaptchaBundle.git
git=http://github.com/Gregwar/CaptchaBundle.git
target=/bundles/Gregwar/CaptchaBundle
version=origin/2.0 <- add this if you are using Symfony 2.0
```
Now, run the vendors script to download the bundle:
@ -49,7 +50,7 @@ $ git submodule update --init
Add the following to the "require" section of your `composer.json` file:
```
"gregwar/captcha-bundle": "1.0.0"
"gregwar/captcha-bundle": "dev-master"
```
And update your dependencies
@ -64,7 +65,7 @@ Now you will need to add the `Gregwar` namespace to your autoloader:
<?php
// app/autoload.php
$loader->registerNamspaces(array(
$loader->registerNamespaces(array(
// ...
'Gregwar' => __DIR__.'/../vendor/bundles',
));
@ -105,27 +106,43 @@ You can use the "captcha" type in your forms this way:
// ...
```
Note that the generated image will be embeded in the HTML document, to avoid dealing
with route and subrequests.
Note that the generated image will, by default, be embedded in the HTML document
to avoid dealing with route and subrequests.
Options
=======
You can define the following type option :
You can define the following configuration options globally:
* **width**: the width of the captcha image (default=120)
* **height**: the height of the captcha image (default=40)
* **length**: the length of the captcha (number of chars, default 5)
* **quality**: jpeg quality of captchas (default=15)
* **charset**: the charset used for code generation (default=abcdefhjkmnprstuvwxyz23456789)
* **font**: the font to use (default=Generator/Font/captcha.ttf)
* **keep_value**: the value will be the same until the form is posted, even if the page is refreshed (default=true)
* **as_file**: if set to true an image file will be created instead of embedding to please IE6/7 (default=false)
* **image_folder**: name of folder for captcha images relative to public web folder in case **as_file** ist set to true (default="captcha")
* **image_folder**: name of folder for captcha images relative to public web folder in case **as_file** is set to true (default="captcha")
* **web_path**: absolute path to public web folder (default="%kernel.root_dir%/../web")
* **gc_freq**: frequency of garbage collection in fractions of 1 (default=100)
* **expiration**: maximum lifetime of captcha image files in minutes (default=60)
You can define the following configuration options globally or on the CaptchaType itself:
* **width**: the width of the captcha image (default=120)
* **height**: the height of the captcha image (default=40)
* **disabled**: disable globally the CAPTCHAs (can be useful in dev environment), it will
still appear but won't be editable and won't be checked
* **length**: the length of the captcha (number of chars, default 5)
* **quality**: jpeg quality of captchas (default=15)
* **charset**: the charset used for code generation (default=abcdefhjkmnprstuvwxyz23456789)
* **font**: the font to use (default is random among some pre-provided fonts), this should be an absolute path
* **keep_value**: the value will be the same until the form is posted, even if the page is refreshed (default=true)
* **as_file**: if set to true an image file will be created instead of embedding to please IE6/7 (default=false)
* **as_url**: if set to true, a URL will be used in the image tag and will handle captcha generation. This can be used in a multiple-server environment and support IE6/7 (default=false)
* **invalid_message**: error message displayed when an non-matching code is submitted (default="Bad code value", see the translation section for more information)
* **bypass_code**: code that will always validate the captcha (default=null)
* **whitelist_key**: the session key to use for keep the session keys that can be used for captcha storage, when using as_url (default=captcha_whitelist_key)
* **reload**: adds a link to reload the code
* **humanity**: number of extra forms that the user can submit after a correct validation, if set to a value different of 0, only 1 over (1+humanity) forms will contain a CAPTCHA (default=0, i.e each form will contain the CAPTCHA)
* **distortion**: enable or disable the distortion on the image (default=true, enabled)
* **max_front_lines**, **max_behind_lines**: the maximum number of lines to draw on top/behind the image. `0` will draw no lines; `null` will use the default algorithm (the
number of lines depends on the size of the image). (default=null)
* **background_color**: sets the background color, if you want to force it, this should be an array of r,g &b, for instance [255, 255, 255] will force the background to be white
* **interpolation**: enable or disable the interpolation on the captcha
Example :
```php
@ -146,10 +163,30 @@ configuration entry in your `config.yml` file:
height: 50
length: 6
Form theming
Translation
===========
The messages are using the translator, you can either change the `invalid_message` option or translate it. Any contribution about the language is welcome !
As URL
============
To use a URL to generate a captcha image, you must add the bundle's routing configuration to your app/routing.yml file:
gregwar_captcha_routing:
resource: "@GregwarCaptchaBundle/Resources/config/routing/routing.yml"
This will use the bundle's route of "/generate-captcha/{key}" to handle the generation. If this route conflicts with an application route, you can prefix the bundle's routes when you import:
gregwar_captcha_routing:
resource: "@GregwarCaptchaBundle/Resources/config/routing/routing.yml"
prefix: /_gcb
Since the session key is transported in the URL, it's also added in another session array, under the `whitelist_key` key, for security reasons
Form Theming
============
The widget support the standard symfony theming, see the [documentation](http://symfony.com/doc/current/book/forms.html#form-theming) for details on how to accomplish this.
The widget support the standard Symfony theming, see the [documentation](http://symfony.com/doc/current/book/forms.html#form-theming) for details on how to accomplish this.
The default rendering is:
@ -158,7 +195,7 @@ The default rendering is:
{% spaceless %}
<img src="{{ captcha_code }}" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" />
{{ form_widget(form) }}
{% spaceless %}
{% endspaceless %}
{% endblock %}
```

View File

@ -0,0 +1,3 @@
gregwar_captcha.generate_captcha:
pattern: /generate-captcha/{key}
defaults: { _controller: GregwarCaptchaBundle:Captcha:generateCaptcha }

View File

@ -1,8 +1,32 @@
services:
# captcha type
captcha.type:
class: Gregwar\CaptchaBundle\Type\CaptchaType
arguments: [ "@session", %gregwar_captcha.config% ]
arguments:
- @session
- @gregwar_captcha.generator
- @translator
- %gregwar_captcha.config%
tags:
- { name: form.type, alias: captcha }
gregwar_captcha.generator:
class: Gregwar\CaptchaBundle\Generator\CaptchaGenerator
arguments:
- @router
- @gregwar_captcha.captcha_builder
- @gregwar_captcha.phrase_builder
- @gregwar_captcha.image_file_handler
gregwar_captcha.image_file_handler:
class: Gregwar\CaptchaBundle\Generator\ImageFileHandler
arguments:
- %gregwar_captcha.config.image_folder%
- %gregwar_captcha.config.web_path%
- %gregwar_captcha.config.gc_freq%
- %gregwar_captcha.config.expiration%
gregwar_captcha.captcha_builder:
class: Gregwar\Captcha\CaptchaBuilder
gregwar_captcha.phrase_builder:
class: Gregwar\Captcha\PhraseBuilder

View File

@ -0,0 +1 @@
Renew: Obnovit

View File

@ -0,0 +1 @@
Renew: Erneuern

View File

@ -0,0 +1 @@
Renew: Renew

View File

@ -0,0 +1 @@
Renew: Renovar

View File

@ -0,0 +1 @@
Renew: Renouveler

View File

@ -0,0 +1 @@
Renew: Rinnova

View File

@ -0,0 +1 @@
Renew: Vernieuwen

View File

@ -0,0 +1 @@
Renew: Regerar

View File

@ -0,0 +1 @@
Renew: Reimprospateaza

View File

@ -0,0 +1 @@
Renew: Обновить

View File

@ -0,0 +1 @@
Renew: 看不清换一张?

View File

@ -0,0 +1 @@
Bad code value: Špatný kontrolní kód

View File

@ -0,0 +1 @@
Bad code value: Code stimmt nicht überein

View File

@ -0,0 +1 @@
Bad code value: Code does not match

View File

@ -0,0 +1 @@
Bad code value: El valor no coincide

View File

@ -0,0 +1 @@
Bad code value: Mauvaise valeur pour le code visuel

View File

@ -0,0 +1 @@
Bad code value: Il valore non coincide

View File

@ -0,0 +1 @@
Bad code value: Code komt niet overeen

View File

@ -0,0 +1 @@
Bad code value: Código de verificação inválido

View File

@ -0,0 +1 @@
Bad code value: Codul de securitate este incorect

View File

@ -0,0 +1 @@
Bad code value: Неправильный код

View File

@ -0,0 +1 @@
Bad code value: 验证码不正确

View File

@ -1,7 +1,20 @@
{% block captcha_widget %}
{% if is_human %}
-
{% else %}
{% spaceless %}
<img src="{{ captcha_code }}" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" />
<img id="{{ image_id }}" src="{{ captcha_code }}" alt="" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" />
{% if reload %}
<script type="text/javascript">
function reload_{{ image_id }}() {
var img = document.getElementById('{{ image_id }}');
img.src = '{{ captcha_code }}?n=' + (new Date()).getTime();
}
</script>
<a class="captcha_reload" href="javascript:reload_{{ image_id }}();">{{ 'Renew'|trans({}, 'gregwar_captcha') }}</a>
{% endif %}
{{ form_widget(form) }}
{% endspaceless %}
{% endif %}
{% endblock %}

144
Type/CaptchaType.php Executable file → Normal file
View File

@ -2,20 +2,17 @@
namespace Gregwar\CaptchaBundle\Type;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormViewInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Translation\TranslatorInterface;
use Gregwar\CaptchaBundle\Validator\CaptchaValidator;
use Gregwar\CaptchaBundle\Generator\CaptchaGenerator;
use Gregwar\CaptchaBundle\DataTransformer\EmptyTransformer;
/**
* Captcha type
@ -24,6 +21,27 @@ use Gregwar\CaptchaBundle\DataTransformer\EmptyTransformer;
*/
class CaptchaType extends AbstractType
{
/**
* @var \Symfony\Component\HttpFoundation\Session\SessionInterface
*/
protected $session;
/**
* The session key
* @var string
*/
protected $key = null;
/**
* @var \Gregwar\CaptchaBundle\Generator\CaptchaGenerator
*/
protected $generator;
/**
* @var TranslatorInterface
*/
protected $translator;
/**
* Options
* @var array
@ -31,92 +49,108 @@ class CaptchaType extends AbstractType
private $options = array();
/**
* Session key
* @var string
* @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
* @param \Gregwar\CaptchaBundle\Generator\CaptchaGenerator $generator
* @param array $options
*/
private $key = 'captcha';
public function __construct(Session $session, $config)
public function __construct(SessionInterface $session, CaptchaGenerator $generator, TranslatorInterface $translator, $options)
{
$this->session = $session;
$this->options = $config;
$this->generator = $generator;
$this->translator = $translator;
$this->options = $options;
}
/**
* @param \Symfony\Component\Form\FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->key = $builder->getForm()->getName();
$this->key = 'gcb_'.$builder->getForm()->getName();
$builder->addValidator(
new CaptchaValidator($this->session, $this->key)
$validator = new CaptchaValidator(
$this->translator,
$this->session,
$this->key,
$options['invalid_message'],
$options['bypass_code'],
$options['humanity']
);
$builder->addEventListener(FormEvents::POST_BIND, array($validator, 'validate'));
}
public function buildView(FormViewInterface $view, FormInterface $form, array $options)
/**
* @param \Symfony\Component\Form\FormView $view
* @param \Symfony\Component\Form\FormInterface $form
* @param array $options
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$fingerprint = null;
$isHuman = false;
if ($options['keep_value'] && $this->session->has($this->key.'_fingerprint')) {
$fingerprint = $this->session->get($this->key.'_fingerprint');
if ($options['reload'] && !$options['as_url']) {
throw new \InvalidArgumentException('GregwarCaptcha: The reload option cannot be set without as_url, see the README for more information');
}
$generator = new CaptchaGenerator($this->generateCaptchaValue(),
$options['image_folder'],
$options['web_path'],
$options['gc_freq'],
$options['expiration'],
$options['font'],
$fingerprint,
$options['quality']);
if ($options['as_file']) {
$captchaCode = $generator->getFile($options['width'], $options['height']);
} else {
$captchaCode = $generator->getCode($options['width'], $options['height']);
if ($options['humanity'] > 0) {
$humanityKey = $this->key.'_humanity';
if ($this->session->get($humanityKey, 0) > 0) {
$isHuman = true;
}
}
if ($options['keep_value']) {
$this->session->set($this->key.'_fingerprint', $generator->getFingerprint());
if ($options['as_url']) {
$key = $this->key;
$keys = $this->session->get($options['whitelist_key'], array());
if (!in_array($key, $keys)) {
$keys[] = $key;
}
$this->session->set($options['whitelist_key'], $keys);
$options['session_key'] = $this->key;
}
$view->addVars(array(
$view->vars = array_merge($view->vars, array(
'captcha_width' => $options['width'],
'captcha_height' => $options['height'],
'captcha_code' => $captchaCode,
'reload' => $options['reload'],
'image_id' => uniqid('captcha_'),
'captcha_code' => $this->generator->getCaptchaCode($options),
'value' => '',
'is_human' => $isHuman
));
$persistOptions = array();
foreach (array('phrase', 'width', 'height', 'distortion', 'length', 'quality') as $key) {
$persistOptions[$key] = $options[$key];
}
$this->session->set($this->key, $persistOptions);
}
/**
* @param \Symfony\Component\OptionsResolver\OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$this->options['property_path'] = false;
$this->options['mapped'] = false;
$resolver->setDefaults($this->options);
}
/**
* @return string
*/
public function getParent()
{
return 'text';
}
/**
* @return string
*/
public function getName()
{
return 'captcha';
}
private function generateCaptchaValue()
{
if (!$this->options['keep_value'] || !$this->session->has($this->key)) {
$value = '';
$chars = str_split($this->options['charset']);
for ($i=0; $i<$this->options['length']; $i++) {
$value.= $chars[array_rand($chars)];
}
$this->session->set($this->key, $value);
} else {
$value = $this->session->get($this->key);
}
return $value;
}
}

View File

@ -2,20 +2,20 @@
namespace Gregwar\CaptchaBundle\Validator;
use Symfony\Component\Form\FormValidatorInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Translation\TranslatorInterface;
/**
* Captcha validator
*
* @author Gregwar <g.passault@gmail.com>
*/
class CaptchaValidator implements FormValidatorInterface
class CaptchaValidator
{
/**
* Session
* @var \Symfony\Component\HttpFoundation\Session\SessionInterface
*/
private $session;
@ -24,45 +24,139 @@ class CaptchaValidator implements FormValidatorInterface
*/
private $key;
public function __construct(Session $session, $key)
/**
* Error message text for non-matching submissions
*/
private $invalidMessage;
/**
* Configuration parameter used to bypass a required code match
*/
private $bypassCode;
/**
* Number of form that the user can submit without captcha
* @var int
*/
private $humanity;
/**
* Translator
* @var TranslatorInterface
*/
private $translator;
/**
* @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
* @param string $key
* @param string $invalidMessage
* @param string|null $bypassCode
*/
public function __construct(TranslatorInterface $translator, SessionInterface $session, $key, $invalidMessage, $bypassCode, $humanity)
{
$this->translator = $translator;
$this->session = $session;
$this->key = $key;
$this->invalidMessage = $invalidMessage;
$this->bypassCode = $bypassCode;
$this->humanity = $humanity;
}
public function validate(FormInterface $form)
/**
* @param FormEvent $event
*/
public function validate(FormEvent $event)
{
$code = $form->getData();
$excepted_code = $this->getExceptedCode();
$form = $form = $event->getForm();
if (!($code && $excepted_code && is_string($code) && is_string($excepted_code)
&& $this->niceize($code) == $this->niceize($excepted_code))) {
$form->addError(new FormError('Bad code value'));
$code = $form->getData();
$expectedCode = $this->getExpectedCode();
if ($this->humanity > 0) {
$humanity = $this->getHumanity();
if ($humanity > 0) {
$this->updateHumanity($humanity-1);
return;
}
}
if (!($code && is_string($code) && ($this->compare($code, $expectedCode) || $this->compare($code, $this->bypassCode)))) {
$form->addError(new FormError($this->translator->trans($this->invalidMessage, array(), 'validators')));
} else {
if ($this->humanity > 0) {
$this->updateHumanity($this->humanity);
}
}
$this->session->remove($this->key);
if ($this->session->has($this->key.'_fingerprint')) {
$this->session->remove($this->key.'_fingerprint');
if ($this->session->has($this->key . '_fingerprint')) {
$this->session->remove($this->key . '_fingerprint');
}
}
/**
* Retrieve the excepted CAPTCHA code
* Retrieve the expected CAPTCHA code
*
* @return mixed|null
*/
private function getExceptedCode()
protected function getExpectedCode()
{
if ($this->session->has($this->key)) {
return $this->session->get($this->key);
$options = $this->session->get($this->key, array());
if (is_array($options) && isset($options['phrase'])) {
return $options['phrase'];
}
return null;
}
/**
* Retreive the humanity
*
* @return mixed|null
*/
protected function getHumanity()
{
return $this->session->get($this->key . '_humanity', 0);
}
/**
* Updates the humanity
*/
protected function updateHumanity($newValue)
{
if ($newValue > 0) {
$this->session->set($this->key . '_humanity', $newValue);
} else {
$this->session->remove($this->key . '_humanity');
}
return null;
}
/**
* Process the codes
*
* @param $code
*
* @return string
*/
private function niceize($code)
protected function niceize($code)
{
return strtr(strtolower($code), 'oil', '01l');
}
/**
* Run a match comparison on the provided code and the expected code
*
* @param $code
* @param $expectedCode
*
* @return bool
*/
protected function compare($code, $expectedCode)
{
return ($expectedCode && is_string($expectedCode) && $this->niceize($code) == $this->niceize($expectedCode));
}
}

View File

@ -3,17 +3,22 @@
"type": "captcha-bundle",
"description": "Captcha bundle",
"keywords": ["symfony2", "captcha", "bot", "visual", "code", "security", "spam"],
"homepage": "https://github.com/Gregwar/ImageBundle",
"homepage": "https://github.com/Gregwar/CaptchaBundle",
"license": "MIT",
"authors": [
{
"name": "Grégoire Passault",
"email": "g.passault@gmail.com",
"homepage": "http://www.gregwar.com/"
},
{
"name": "Jeremy Livingston",
"email": "jeremy.j.livingston@gmail.com"
}
],
"require": {
"php": ">=5.3.0"
"php": ">=5.3.0",
"gregwar/captcha": "v1.0.11"
},
"autoload": {
"psr-0": {