Compare commits

...

60 Commits

Author SHA1 Message Date
5778c54ae0 Add missing initialization 2022-02-09 10:19:13 +01:00
9b65966d27 update composer.json 2021-05-28 11:53:54 +02:00
633e30ae47 Add flow registration validation support 2021-05-28 08:08:42 +02:00
Grégoire Passault
2f96c759ab
Merge pull request #223 from Gemorroj/patch-1
php 8 support
2021-04-20 09:20:37 +02:00
Gemorroj
2769e4791a
php 8 support
#222
2021-04-18 12:06:03 +03:00
Gregwar
5e434e1859 gregwar/captcha version bump 2021-01-09 01:01:13 +01:00
Gregwar
df6915eb5e README details 2020-04-28 10:45:36 +02:00
Gregwar
b7685e63a1 README indentation 2020-04-28 10:44:39 +02:00
Gregwar
15bee25e9c Indentation in README 2020-04-28 10:43:03 +02:00
Gregwar
3a4e31473a Adding some note about session_key option for multiple captcha on same
page
2020-04-17 16:13:37 +02:00
Gregwar
fec0ebb2f1 Autowire controller 2020-04-09 10:18:10 +02:00
Grégoire Passault
cdbe566acc
Merge pull request #212 from l-vo/bundle_auto_registration
Fix flex auto-registration
2020-03-02 11:00:37 +01:00
Laurent VOULLEMIER
93f9d3c1a7 Fix flex auto-registration 2020-02-29 20:31:09 +01:00
Gregwar
d4475118d0 Merge branch 'master' of github.com:Gregwar/CaptchaBundle 2020-01-14 11:15:40 +01:00
Gregwar
b8a129fc2a Adding controller class in services.yml 2020-01-14 11:14:42 +01:00
Grégoire Passault
fe4b0dea2b
Merge pull request #210 from alexander-schranz/patch-1
Fix compatibility table
2020-01-14 10:06:23 +01:00
Alexander Schranz
640acadeea
Fix compatibility table 2020-01-13 18:35:47 +01:00
Grégoire Passault
a22ba77f52
Merge pull request #208 from Olaf1989/symfony-5
Fully support Symfony 4 and 5
2020-01-08 16:31:01 +01:00
Olaf
459a108ee6 docs (Readme) update README.md 2020-01-03 01:07:49 +01:00
Olaf
3691a30240 chore (Packages) allow Symfony 4 and PHP 7.1 2020-01-03 01:01:41 +01:00
Olaf
2012808bc6 docs (Readme) update README.md 2020-01-03 00:43:31 +01:00
Olaf
2b62be4af1 fix (Symfony) use Symfony 5 web_path 2020-01-03 00:42:56 +01:00
Olaf
3f25597f94 Merge branch 'symfony-5' of https://github.com/Olaf1989/CaptchaBundle into symfony-5 2020-01-03 00:17:34 +01:00
Olaf
478c64633e fix (ECS) apply coding standard 2020-01-03 00:12:44 +01:00
Olaf
2ed4f74954 chore (ECS) add easy coding standard 2020-01-03 00:09:09 +01:00
Olaf
60c0e04366
Delete composer.lock 2020-01-03 00:05:31 +01:00
Olaf
61a8561dd6 fix (Captcha) allow null value for bypassCode 2020-01-03 00:04:18 +01:00
Olaf
5515c25872 chore (Symfony) use Symfony 5 + strict typing
Update Symfony version to 5
Use strict typing
2019-12-30 00:27:20 +01:00
Gregwar
eeb01bdca5 Typo 2019-12-02 10:24:14 +01:00
Grégoire Passault
678459f58f
Merge pull request #205 from tacman/patch-1
Support Symfony 5, drop support for Symfony 2
2019-12-02 10:23:50 +01:00
tacman
1781a6aa62
Support Symfony 5, drop support for Symfony 2 2019-12-01 08:00:17 -05:00
Grégoire Passault
3ccfdf1c93
Merge pull request #200 from adamwojs/fix_deprecated_tree_builder_root_call
Fixed deprecated TreeBuilder::root method call in SF 4
2019-09-23 15:07:46 +02:00
Grégoire Passault
b703ed1a0c
Merge pull request #201 from adamwojs/fix_deprecated_spacelesss_tag
Removed deprecated spaceless tag
2019-09-23 15:07:20 +02:00
Adam Wójs
25de43ac90 Removed deprecated spaceless tag 2019-09-22 13:59:41 +02:00
Adam Wójs
1b4835eb4d Fixed deprecated TreeBuilder::root method call in SF 4 2019-09-22 13:10:16 +02:00
Grégoire Passault
be1ce45060
Merge pull request #196 from ferdynator/master
Fixes deprecation warnings in Symfony 4.2
2019-08-15 18:04:18 +02:00
Frederik Schubert
0f6cd70920
Fixed deprecation warning 2019-01-23 11:01:56 +01:00
Frederik Schubert
754310f488
Fixed deprecation warning 2019-01-23 11:01:17 +01:00
Grégoire Passault
c2d5468556
Merge pull request #172 from cengizhancaliskan/master
Support of multiple instance
2018-11-20 11:41:34 +01:00
Grégoire Passault
980afdc10a
Merge pull request #187 from mandalor-development/master
Changed template reference
2018-04-24 11:22:05 +02:00
Mandalor-Development
791128c0fd Changed template reference
According to:
https://symfony.com/doc/current/templating.html#referencing-templates-in-a-bundle

Fixed error:
[critical] Uncaught PHP Exception Twig_Error_Loader: "Unable to find template "GregwarCaptchaBundle::captcha.html.twig" (looked into: /[path]/templates, /[path]/templates, /[path]/vendor/symfony/twig-bridge/Resources/views/Form)." at /[path]/templates/form.html.twig line 17
2018-02-28 15:15:55 +01:00
Grégoire Passault
639430383f
Merge pull request #180 from andreybolonin/master
add sf 4.0 support
2017-12-28 12:21:03 +01:00
andreybolonin
8e98c5c0ab add public services 2017-12-28 12:30:19 +02:00
andreybolonin
539884cd5d add sf 4.0 support 2017-11-27 18:08:24 +02:00
Gregwar
8ce4adb1b1 Adding background_images in persisted options (fixes #175) 2017-10-02 11:12:17 +02:00
Gregwar
b787a8002e Rising default quality to 50 2017-09-27 12:01:02 +02:00
Cengizhan Çalışkan
ba9c0e6166 Change session key for as_url option 2017-03-27 22:32:00 +03:00
Cengizhan Çalışkan
25b8840f2a Support of multiple instance 2017-03-19 18:40:34 +03:00
Grégoire Passault
045ba7e67e Merge pull request #150 from dprolife/patch-1
Update composer.json
2017-02-23 10:26:22 +01:00
Gregwar
18c85d3a4f donate 2016-10-24 12:05:50 +02:00
Gregwar
f6c45045f0 Adding class captcha_image on the <img> captcha tag (see #160) 2016-09-21 10:36:55 +02:00
Gregwar
f95a951b26 Detail 2016-08-05 20:07:20 +02:00
Grégoire Passault
e1ed228b8b Merge pull request #168 from linnaea/patch-1
Quote parameter references in services.yml
2016-05-30 12:28:28 +02:00
Linnaea Von Lavia
0b3495a081 Quote parameter references in services.yml
Not quoting a scalar starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.
2016-05-28 22:40:00 +08:00
Grégoire Passault
53c25b2e9a Merge pull request #163 from hackzilla/master
Update CaptchaType.php
2016-02-02 23:40:33 +01:00
Daniel Platt
fcf8c4fd01 Update CaptchaType.php
add getBlockPrefix for Symfony3 compatibility.
2016-02-02 22:15:18 +00:00
Grégoire Passault
e91cb1a3b7 Merge pull request #139 from RicoVZ/patch-1
Replaced usage of "pattern" with "path"
2015-12-30 19:19:50 +01:00
dprolife
1f6c80c326 Update composer.json
Fix symfony requirements
2015-12-17 22:51:31 +01:00
Gregwar
f06ff4d2c2 Clarification 2015-12-13 12:12:45 +01:00
RicoVZ
ef281889ab Replaced usage of "pattern" with "path"
As described in issue #135
2015-08-18 09:48:41 +02:00
14 changed files with 449 additions and 372 deletions

View File

@ -1,32 +1,37 @@
<?php <?php
declare(strict_types=1);
namespace Gregwar\CaptchaBundle\Controller; namespace Gregwar\CaptchaBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Gregwar\CaptchaBundle\Generator\CaptchaGenerator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
* Generates a captcha via a URL * Generates a captcha via a URL.
* *
* @author Jeremy Livingston <jeremy.j.livingston@gmail.com> * @author Jeremy Livingston <jeremy.j.livingston@gmail.com>
*/ */
class CaptchaController extends Controller class CaptchaController extends AbstractController
{ {
/** /** @var CaptchaGenerator */
* Action that is used to generate the captcha, save its code, and stream the image private $captchaGenerator;
*
* @param string $key /** @var array */
* private $config;
* @return Response
* public function __construct(CaptchaGenerator $captchaGenerator, array $config)
* @throws NotFoundHttpException
*/
public function generateCaptchaAction($key)
{ {
$options = $this->container->getParameter('gregwar_captcha.config'); $this->captchaGenerator = $captchaGenerator;
$session = $this->get('session'); $this->config = $config;
$whitelistKey = $options['whitelist_key']; }
public function generateCaptchaAction(Request $request, string $key): Response
{
$session = $request->getSession();
$whitelistKey = $this->config['whitelist_key'];
$isOk = false; $isOk = false;
if ($session->has($whitelistKey)) { if ($session->has($whitelistKey)) {
@ -37,21 +42,18 @@ class CaptchaController extends Controller
} }
if (!$isOk) { if (!$isOk) {
return $this->error($options); return $this->error($this->config);
} }
/* @var \Gregwar\CaptchaBundle\Generator\CaptchaGenerator $generator */
$generator = $this->container->get('gregwar_captcha.generator');
$persistedOptions = $session->get($key, array()); $persistedOptions = $session->get($key, array());
$options = array_merge($options, $persistedOptions); $options = array_merge($this->config, $persistedOptions);
$phrase = $generator->getPhrase($options); $phrase = $this->captchaGenerator->getPhrase($options);
$generator->setPhrase($phrase); $this->captchaGenerator->setPhrase($phrase);
$persistedOptions['phrase'] = $phrase; $persistedOptions['phrase'] = $phrase;
$session->set($key, $persistedOptions); $session->set($key, $persistedOptions);
$response = new Response($generator->generate($options)); $response = new Response($this->captchaGenerator->generate($options));
$response->headers->set('Content-type', 'image/jpeg'); $response->headers->set('Content-type', 'image/jpeg');
$response->headers->set('Pragma', 'no-cache'); $response->headers->set('Pragma', 'no-cache');
$response->headers->set('Cache-Control', 'no-cache'); $response->headers->set('Cache-Control', 'no-cache');
@ -59,20 +61,11 @@ class CaptchaController extends Controller
return $response; return $response;
} }
/** private function error(array $options): Response
* Returns an empty image with status code 428 Precondition Required
*
* @param array $options
*
* @return Response
*/
protected function error($options)
{ {
/* @var \Gregwar\CaptchaBundle\Generator\CaptchaGenerator $generator */ $this->captchaGenerator->setPhrase('');
$generator = $this->container->get('gregwar_captcha.generator');
$generator->setPhrase('');
$response = new Response($generator->generate($options)); $response = new Response($this->captchaGenerator->generate($options));
$response->setStatusCode(428); $response->setStatusCode(428);
$response->headers->set('Content-type', 'image/jpeg'); $response->headers->set('Content-type', 'image/jpeg');
$response->headers->set('Pragma', 'no-cache'); $response->headers->set('Pragma', 'no-cache');

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Gregwar\CaptchaBundle\DependencyInjection; namespace Gregwar\CaptchaBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\Builder\TreeBuilder;
@ -7,15 +9,10 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface class Configuration implements ConfigurationInterface
{ {
/** public function getConfigTreeBuilder(): TreeBuilder
* Generates the configuration tree.
*
* @return TreeBuilder
*/
public function getConfigTreeBuilder()
{ {
$treeBuilder = new TreeBuilder(); $treeBuilder = new TreeBuilder('gregwar_captcha');
$rootNode = $treeBuilder->root('gregwar_captcha'); $rootNode = $treeBuilder->getRootNode();
$rootNode $rootNode
->addDefaultsIfNotSet() ->addDefaultsIfNotSet()
@ -30,10 +27,10 @@ class Configuration implements ConfigurationInterface
->scalarNode('as_url')->defaultValue(false)->end() ->scalarNode('as_url')->defaultValue(false)->end()
->scalarNode('reload')->defaultValue(false)->end() ->scalarNode('reload')->defaultValue(false)->end()
->scalarNode('image_folder')->defaultValue('captcha')->end() ->scalarNode('image_folder')->defaultValue('captcha')->end()
->scalarNode('web_path')->defaultValue('%kernel.root_dir%/../web')->end() ->scalarNode('web_path')->defaultValue('%kernel.project_dir%/public')->end()
->scalarNode('gc_freq')->defaultValue(100)->end() ->scalarNode('gc_freq')->defaultValue(100)->end()
->scalarNode('expiration')->defaultValue(60)->end() ->scalarNode('expiration')->defaultValue(60)->end()
->scalarNode('quality')->defaultValue(30)->end() ->scalarNode('quality')->defaultValue(50)->end()
->scalarNode('invalid_message')->defaultValue('Bad code value')->end() ->scalarNode('invalid_message')->defaultValue('Bad code value')->end()
->scalarNode('bypass_code')->defaultValue(null)->end() ->scalarNode('bypass_code')->defaultValue(null)->end()
->scalarNode('whitelist_key')->defaultValue('captcha_whitelist_key')->end() ->scalarNode('whitelist_key')->defaultValue('captcha_whitelist_key')->end()
@ -47,6 +44,7 @@ class Configuration implements ConfigurationInterface
->arrayNode('background_images')->prototype('scalar')->end()->end() ->arrayNode('background_images')->prototype('scalar')->end()->end()
->scalarNode('disabled')->defaultValue(false)->end() ->scalarNode('disabled')->defaultValue(false)->end()
->scalarNode('ignore_all_effects')->defaultValue(false)->end() ->scalarNode('ignore_all_effects')->defaultValue(false)->end()
->scalarNode('session_key')->defaultValue('captcha')->end()
->end() ->end()
; ;

View File

@ -1,24 +1,29 @@
<?php <?php
declare(strict_types=1);
namespace Gregwar\CaptchaBundle\DependencyInjection; namespace Gregwar\CaptchaBundle\DependencyInjection;
use Exception;
use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\FileLocator;
/** /**
* Extension used to load the configuration, set parameters, and initialize the captcha view * Extension used to load the configuration, set parameters, and initialize the captcha view.
* *
* @author Gregwar <g.passault@gmail.com> * @author Gregwar <g.passault@gmail.com>
*/ */
class GregwarCaptchaExtension extends Extension class GregwarCaptchaExtension extends Extension
{ {
/** /**
* @param array $configs * @param array $configs
* @param ContainerBuilder $container * @param ContainerBuilder $container
*
* @throws Exception
*/ */
public function load(array $configs, ContainerBuilder $container) public function load(array $configs, ContainerBuilder $container): void
{ {
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml'); $loader->load('services.yml');
@ -34,6 +39,6 @@ class GregwarCaptchaExtension extends Extension
$container->setParameter('gregwar_captcha.config.whitelist_key', $config['whitelist_key']); $container->setParameter('gregwar_captcha.config.whitelist_key', $config['whitelist_key']);
$resources = $container->getParameter('twig.form.resources'); $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('@GregwarCaptcha/captcha.html.twig'), $resources));
} }
} }

View File

@ -1,48 +1,40 @@
<?php <?php
declare(strict_types=1);
namespace Gregwar\CaptchaBundle\Generator; namespace Gregwar\CaptchaBundle\Generator;
use Gregwar\Captcha\CaptchaBuilder; use Gregwar\Captcha\CaptchaBuilder;
use Gregwar\Captcha\PhraseBuilder; use Gregwar\Captcha\PhraseBuilder;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Routing\RouterInterface;
use Gregwar\Captcha\CaptchaBuilderInterface; use Gregwar\Captcha\CaptchaBuilderInterface;
use Gregwar\Captcha\PhraseBuilderInterface; use Gregwar\Captcha\PhraseBuilderInterface;
/** /**
* Uses configuration parameters to call the services that generate captcha images * Uses configuration parameters to call the services that generate captcha images.
* *
* @author Gregwar <g.passault@gmail.com> * @author Gregwar <g.passault@gmail.com>
* @author Jeremy Livingston <jeremy.j.livingston@gmail.com> * @author Jeremy Livingston <jeremy.j.livingston@gmail.com>
*/ */
class CaptchaGenerator class CaptchaGenerator
{ {
/** /** @var RouterInterface */
* @var RouterInterface
*/
protected $router; protected $router;
/** /** @var CaptchaBuilder */
* @var CaptchaBuilder
*/
protected $builder; protected $builder;
/** /** @var PhraseBuilder */
* @var PhraseBuilder
*/
protected $phraseBuilder; protected $phraseBuilder;
/** /** @var ImageFileHandler */
* @var ImageFileHandler
*/
protected $imageFileHandler; protected $imageFileHandler;
/** /**
* @param RouterInterface $router * @param RouterInterface $router
* @param CaptchaBuilderInterface $builder * @param CaptchaBuilderInterface $builder
* @param PhraseBuilderInterface $phraseBuilder * @param PhraseBuilderInterface $phraseBuilder
* @param ImageFileHandler $imageFileHandler * @param ImageFileHandler $imageFileHandler
*/ */
public function __construct( public function __construct(
RouterInterface $router, RouterInterface $router,
@ -50,20 +42,13 @@ class CaptchaGenerator
PhraseBuilderInterface $phraseBuilder, PhraseBuilderInterface $phraseBuilder,
ImageFileHandler $imageFileHandler ImageFileHandler $imageFileHandler
) { ) {
$this->router = $router; $this->router = $router;
$this->builder = $builder; $this->builder = $builder;
$this->phraseBuilder = $phraseBuilder; $this->phraseBuilder = $phraseBuilder;
$this->imageFileHandler = $imageFileHandler; $this->imageFileHandler = $imageFileHandler;
} }
/** public function getCaptchaCode(array &$options): string
* Get the captcha URL, stream, or filename that will go in the image's src attribute
*
* @param array $options
*
* @return array
*/
public function getCaptchaCode(array &$options)
{ {
$this->builder->setPhrase($this->getPhrase($options)); $this->builder->setPhrase($this->getPhrase($options));
@ -76,27 +61,21 @@ class CaptchaGenerator
// Returns the image generation URL // Returns the image generation URL
if ($options['as_url']) { if ($options['as_url']) {
return $this->router->generate('gregwar_captcha.generate_captcha', return $this->router->generate(
array('key' => $options['session_key'], 'n' => md5(microtime(true).mt_rand()))); 'gregwar_captcha.generate_captcha',
array('key' => $options['session_key'], 'n' => md5(microtime(true).mt_rand()))
);
} }
return 'data:image/jpeg;base64,' . base64_encode($this->generate($options)); return 'data:image/jpeg;base64,'.base64_encode($this->generate($options));
} }
/** public function setPhrase(string $phrase): void
* Sets the phrase to the builder
*/
public function setPhrase($phrase)
{ {
$this->builder->setPhrase($phrase); $this->builder->setPhrase($phrase);
} }
/** public function generate(array &$options): string
* @param array $options
*
* @return string
*/
public function generate(array &$options)
{ {
$this->builder->setDistortion($options['distortion']); $this->builder->setDistortion($options['distortion']);
@ -104,7 +83,7 @@ class CaptchaGenerator
$this->builder->setMaxBehindLines($options['max_behind_lines']); $this->builder->setMaxBehindLines($options['max_behind_lines']);
if (isset($options['text_color']) && $options['text_color']) { if (isset($options['text_color']) && $options['text_color']) {
if (count($options['text_color']) !== 3) { if (3 !== count($options['text_color'])) {
throw new \RuntimeException('text_color should be an array of r, g and b'); throw new \RuntimeException('text_color should be an array of r, g and b');
} }
@ -113,7 +92,7 @@ class CaptchaGenerator
} }
if (isset($options['background_color']) && $options['background_color']) { if (isset($options['background_color']) && $options['background_color']) {
if (count($options['background_color']) !== 3) { if (3 !== count($options['background_color'])) {
throw new \RuntimeException('background_color should be an array of r, g and b'); throw new \RuntimeException('background_color should be an array of r, g and b');
} }
@ -149,12 +128,7 @@ class CaptchaGenerator
return $this->imageFileHandler->saveAsFile($content); return $this->imageFileHandler->saveAsFile($content);
} }
/** public function getPhrase(array &$options): string
* @param array $options
*
* @return string
*/
public function getPhrase(array &$options)
{ {
// Get the phrase that we'll use for this image // Get the phrase that we'll use for this image
if ($options['keep_value'] && isset($options['phrase'])) { if ($options['keep_value'] && isset($options['phrase'])) {
@ -163,7 +137,7 @@ class CaptchaGenerator
$phrase = $this->phraseBuilder->build($options['length'], $options['charset']); $phrase = $this->phraseBuilder->build($options['length'], $options['charset']);
$options['phrase'] = $phrase; $options['phrase'] = $phrase;
} }
return $phrase; return $phrase;
} }
} }

View File

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

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Gregwar\CaptchaBundle; namespace Gregwar\CaptchaBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\HttpKernel\Bundle\Bundle;

153
README.md
View File

@ -1,113 +1,66 @@
Gregwar's CaptchaBundle Gregwar's CaptchaBundle
===================== =====================
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YUXRLWHQSWS6L)
The `GregwarCaptchaBundle` adds support for a captcha form type for the The `GregwarCaptchaBundle` adds support for a captcha form type for the
Symfony form component. Symfony form component.
It uses [gregwar/captcha](https://github.com/Gregwar/Captcha) as captcha generator, which is a separate standalone library that can be used for none-symfony projects.
Compatibility with Symfony Compatibility with Symfony
========================== ==========================
If you are using Symfony `< 2.8`, you should use version `1.*` | CaptchaBundle | Symfony | PHP |
|:---------------:|:---------:|:--------:|
| 2.1.* | 4.* - 5.* | >= 7.1 |
| 2.0.* | 2.8 - 3.* | >= 5.3.9 |
| 1.* | 2.1 - 2.7 | >= 5.3.0 |
If you are using SYmfony `>= 2.8`, you should use version `2.*`
Installation Installation
============ ============
### Step 1: Download the GregwarCaptchaBundle ### Step 1: Download the GregwarCaptchaBundle
Ultimately, the GregwarCaptchaBundle files should be downloaded to the Use composer require to download and install the package.
'vendor/bundles/Gregwar/CaptchaBundle' directory. At the end of the installation, the bundle is automatically registered thanks to the Symfony recipe.
You can accomplish this several ways, depending on your personal preference.
The first method is the standard Symfony method.
***Using Composer***
Use composer require to download and install the package.
``` bash ``` bash
composer require gregwar/captcha-bundle composer require gregwar/captcha-bundle
``` ```
***Using the vendors script*** If you don't use flex, register it manually:
Add the following lines to your `deps` file:
```
[GregwarCaptchaBundle]
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:
``` bash
$ php bin/vendors install
```
***Using submodules***
If you prefer instead to use git submodules, then run the following:
``` bash
$ git submodule add git://github.com/Gregwar/CaptchaBundle.git vendor/bundles/Gregwar/CaptchaBundle
$ git submodule update --init
```
### Step 2: Configure the Autoloader
If you use composer, you can skip this step.
Now you will need to add the `Gregwar` namespace to your autoloader:
``` php
<?php
// app/autoload.php
$loader->registerNamespaces(array(
// ...
'Gregwar' => __DIR__.'/../vendor/bundles',
));
```
### Step 3: Enable the bundle
Finally, enable the bundle in the kernel:
```php ```php
<?php <?php
// app/appKernel.php // config/bundles.php
return [
public function registerBundles() // ...
{ Gregwar\CaptchaBundle\GregwarCaptchaBundle::class => ['all' => true]
$bundles = array( ];
// ...
new Gregwar\CaptchaBundle\GregwarCaptchaBundle(),
);
}
``` ```
Configuration Configuration
============= =============
Add the following configuration to your `app/config/config.yml`: If you need to customize the global bundle configuration, you can create a `/config/packages/gregwar_captcha.yaml` file with your configuration:
``` yaml
gregwar_captcha: ~ gregwar_captcha:
width: 160
height: 50
```
Usage Usage
===== =====
You can use the "captcha" type in your forms this way: You can use the "captcha" type in your forms this way:
```php ``` php
<?php <?php
use Gregwar\CaptchaBundle\Type\CaptchaType; use Gregwar\CaptchaBundle\Type\CaptchaType;
// ... // ...
$builder->add('captcha', CaptchaType::class); // That's all ! $builder->add('captcha', CaptchaType::class); // That's all !
// If you're using php<5.5, you can use instead: // ...
$builder->add('captcha', 'Gregwar\CaptchaBundle\Type\CaptchaType');
// ...
``` ```
Note that the generated image will, by default, be embedded in the HTML document Note that the generated image will, by default, be embedded in the HTML document
@ -119,7 +72,7 @@ Options
You can define the following configuration options globally: You can define the following configuration options globally:
* **image_folder**: name of folder for captcha images relative to public web folder in case **as_file** is 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") * **web_path**: absolute path to public web folder (default='%kernel.project_dir%/public')
* **gc_freq**: frequency of garbage collection in fractions of 1 (default=100) * **gc_freq**: frequency of garbage collection in fractions of 1 (default=100)
* **expiration**: maximum lifetime of captcha image files in minutes (default=60) * **expiration**: maximum lifetime of captcha image files in minutes (default=60)
@ -148,27 +101,29 @@ number of lines depends on the size of the image). (default=null)
* **background_images**: Sets custom user defined images as the captcha background (1 image is selected randomly). It is recommended to turn off all the effects on the image (ignore_all_effects). The full paths to the images must be passed. * **background_images**: Sets custom user defined images as the captcha background (1 image is selected randomly). It is recommended to turn off all the effects on the image (ignore_all_effects). The full paths to the images must be passed.
* **interpolation**: enable or disable the interpolation on the captcha * **interpolation**: enable or disable the interpolation on the captcha
* **ignore_all_effects**: Recommended to use when setting background images, will disable all image effects. * **ignore_all_effects**: Recommended to use when setting background images, will disable all image effects.
* **session_key**, if you want to host multiple CAPTCHA on the same page, you might have different session keys to ensure proper storage of the clear phrase for those different forms
Example : Example :
```php ``` php
<?php <?php
use Gregwar\CaptchaBundle\Type\CaptchaType; use Gregwar\CaptchaBundle\Type\CaptchaType;
// ... // ...
$builder->add('captcha', CaptchaType::class, array( $builder->add('captcha', CaptchaType::class, array(
'width' => 200, 'width' => 200,
'height' => 50, 'height' => 50,
'length' => 6, 'length' => 6,
)); ));
``` ```
You can also set these options for your whole application using the `gregwar_captcha` You can also set these options for your whole application using the `gregwar_captcha`
configuration entry in your `config.yml` file: configuration entry in your `config.yml` file:
``` yaml
gregwar_captcha: gregwar_captcha:
width: 200 width: 200
height: 50 height: 50
length: 6 length: 6
```
Translation Translation
=========== ===========
@ -177,16 +132,20 @@ The messages are using the translator, you can either change the `invalid_messag
As URL 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: To use a URL to generate a captcha image, you must add the bundle's routing configuration to your `config/routes.yaml` file:
gregwar_captcha_routing: ``` yaml
resource: "@GregwarCaptchaBundle/Resources/config/routing/routing.yml" 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: 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: ``` yaml
resource: "@GregwarCaptchaBundle/Resources/config/routing/routing.yml" gregwar_captcha_routing:
prefix: /_gcb 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 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
@ -197,7 +156,7 @@ The widget support the standard Symfony theming, see the [documentation](http://
The default rendering is: The default rendering is:
```html ``` twig
{% block captcha_widget %} {% block captcha_widget %}
{% spaceless %} {% spaceless %}
<img src="{{ captcha_code }}" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" /> <img src="{{ captcha_code }}" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" />

View File

@ -1,3 +1,3 @@
gregwar_captcha.generate_captcha: gregwar_captcha.generate_captcha:
pattern: /generate-captcha/{key} path: /generate-captcha/{key}
defaults: { _controller: GregwarCaptchaBundle:Captcha:generateCaptcha } defaults: { _controller: Gregwar\CaptchaBundle\Controller\CaptchaController::generateCaptchaAction }

View File

@ -1,4 +1,5 @@
parameters: parameters:
gregwar_captcha.controller.class: Gregwar\CaptchaBundle\Controller\CaptchaController
gregwar_captcha.captcha_type.class: Gregwar\CaptchaBundle\Type\CaptchaType gregwar_captcha.captcha_type.class: Gregwar\CaptchaBundle\Type\CaptchaType
gregwar_captcha.captcha_generator.class: Gregwar\CaptchaBundle\Generator\CaptchaGenerator gregwar_captcha.captcha_generator.class: Gregwar\CaptchaBundle\Generator\CaptchaGenerator
gregwar_captcha.image_file_handler.class: Gregwar\CaptchaBundle\Generator\ImageFileHandler gregwar_captcha.image_file_handler.class: Gregwar\CaptchaBundle\Generator\ImageFileHandler
@ -6,18 +7,33 @@ parameters:
gregwar_captcha.phrase_builder.class: Gregwar\Captcha\PhraseBuilder gregwar_captcha.phrase_builder.class: Gregwar\Captcha\PhraseBuilder
services: services:
captcha.type: Gregwar\CaptchaBundle\Controller\CaptchaController:
class: %gregwar_captcha.captcha_type.class% public: true
alias: 'gregwar_captcha.controller'
gregwar_captcha.controller:
class: '%gregwar_captcha.controller.class%'
public: true
arguments:
- '@gregwar_captcha.generator'
- '%gregwar_captcha.config%'
autowire: true
# captcha.type:
gregwar_captcha.type:
class: '%gregwar_captcha.captcha_type.class%'
public: true
arguments: arguments:
- '@session' - '@session'
- '@gregwar_captcha.generator' - '@gregwar_captcha.generator'
- '@translator' - '@translator'
- %gregwar_captcha.config% - '%gregwar_captcha.config%'
tags: tags:
- { name: form.type, alias: captcha } - { name: form.type, alias: captcha }
gregwar_captcha.generator: gregwar_captcha.generator:
class: %gregwar_captcha.captcha_generator.class% class: '%gregwar_captcha.captcha_generator.class%'
public: true
arguments: arguments:
- '@router' - '@router'
- '@gregwar_captcha.captcha_builder' - '@gregwar_captcha.captcha_builder'
@ -25,15 +41,18 @@ services:
- '@gregwar_captcha.image_file_handler' - '@gregwar_captcha.image_file_handler'
gregwar_captcha.image_file_handler: gregwar_captcha.image_file_handler:
class: %gregwar_captcha.image_file_handler.class% class: '%gregwar_captcha.image_file_handler.class%'
public: true
arguments: arguments:
- %gregwar_captcha.config.image_folder% - '%gregwar_captcha.config.image_folder%'
- %gregwar_captcha.config.web_path% - '%gregwar_captcha.config.web_path%'
- %gregwar_captcha.config.gc_freq% - '%gregwar_captcha.config.gc_freq%'
- %gregwar_captcha.config.expiration% - '%gregwar_captcha.config.expiration%'
gregwar_captcha.captcha_builder: gregwar_captcha.captcha_builder:
class: %gregwar_captcha.captcha_builder.class% class: '%gregwar_captcha.captcha_builder.class%'
public: true
gregwar_captcha.phrase_builder: gregwar_captcha.phrase_builder:
class: %gregwar_captcha.phrase_builder.class% class: '%gregwar_captcha.phrase_builder.class%'
public: true

View File

@ -2,8 +2,8 @@
{% if is_human %} {% if is_human %}
- -
{% else %} {% else %}
{% spaceless %} {% apply spaceless %}
<img id="{{ image_id }}" src="{{ captcha_code }}" alt="" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" /> <img class="captcha_image" id="{{ image_id }}" src="{{ captcha_code }}" alt="" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" />
{% if reload %} {% if reload %}
<script type="text/javascript"> <script type="text/javascript">
function reload_{{ image_id }}() { function reload_{{ image_id }}() {
@ -14,6 +14,6 @@
<a class="captcha_reload" href="javascript:reload_{{ image_id }}();">{{ 'Renew'|trans({}, 'gregwar_captcha') }}</a> <a class="captcha_reload" href="javascript:reload_{{ image_id }}();">{{ 'Renew'|trans({}, 'gregwar_captcha') }}</a>
{% endif %} {% endif %}
{{ form_widget(form) }} {{ form_widget(form) }}
{% endspaceless %} {% endapply %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,46 +1,40 @@
<?php <?php
declare(strict_types=1);
namespace Gregwar\CaptchaBundle\Type; namespace Gregwar\CaptchaBundle\Type;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use Gregwar\CaptchaBundle\Validator\CaptchaValidator; use Gregwar\CaptchaBundle\Validator\CaptchaValidator;
use Gregwar\CaptchaBundle\Generator\CaptchaGenerator; use Gregwar\CaptchaBundle\Generator\CaptchaGenerator;
/** /**
* Captcha type * Captcha type.
* *
* @author Gregwar <g.passault@gmail.com> * @author Gregwar <g.passault@gmail.com>
*/ */
class CaptchaType extends AbstractType class CaptchaType extends AbstractType
{ {
/** const SESSION_KEY_PREFIX = '_captcha_';
* @var SessionInterface
*/ /** @var SessionInterface */
protected $session; protected $session;
/** /** @var CaptchaGenerator */
* @var CaptchaGenerator
*/
protected $generator; protected $generator;
/** /** @var TranslatorInterface */
* @var TranslatorInterface
*/
protected $translator; protected $translator;
/** /** @var array */
* Options
* @var array
*/
private $options = array(); private $options = array();
/** /**
@ -51,10 +45,10 @@ class CaptchaType extends AbstractType
*/ */
public function __construct(SessionInterface $session, CaptchaGenerator $generator, TranslatorInterface $translator, $options) public function __construct(SessionInterface $session, CaptchaGenerator $generator, TranslatorInterface $translator, $options)
{ {
$this->session = $session; $this->session = $session;
$this->generator = $generator; $this->generator = $generator;
$this->translator = $translator; $this->translator = $translator;
$this->options = $options; $this->options = $options;
} }
/** /**
@ -65,13 +59,14 @@ class CaptchaType extends AbstractType
$validator = new CaptchaValidator( $validator = new CaptchaValidator(
$this->translator, $this->translator,
$this->session, $this->session,
sprintf('gcb_%s', $builder->getForm()->getName()), sprintf('%s%s', self::SESSION_KEY_PREFIX, $options['session_key']),
$options['invalid_message'], $options['invalid_message'],
$options['bypass_code'], $options['bypass_code'],
$options['humanity'] $options['humanity'],
$options['request']
); );
$event = \Symfony\Component\HttpKernel\Kernel::VERSION >= 2.3 ? FormEvents::POST_SUBMIT : FormEvents::POST_BIND;
$builder->addEventListener($event, array($validator, 'validate')); $builder->addEventListener(FormEvents::POST_SUBMIT, array($validator, 'validate'));
} }
/** /**
@ -83,8 +78,8 @@ class CaptchaType extends AbstractType
throw new \InvalidArgumentException('GregwarCaptcha: The reload option cannot be set without as_url, see the README for more information'); throw new \InvalidArgumentException('GregwarCaptcha: The reload option cannot be set without as_url, see the README for more information');
} }
$sessionKey = sprintf('gcb_%s', $form->getName()); $sessionKey = sprintf('%s%s', self::SESSION_KEY_PREFIX, $options['session_key']);
$isHuman = false; $isHuman = false;
if ($options['humanity'] > 0) { if ($options['humanity'] > 0) {
$humanityKey = sprintf('%s_humanity', $sessionKey); $humanityKey = sprintf('%s_humanity', $sessionKey);
@ -103,17 +98,18 @@ class CaptchaType extends AbstractType
} }
$view->vars = array_merge($view->vars, array( $view->vars = array_merge($view->vars, array(
'captcha_width' => $options['width'], 'captcha_width' => $options['width'],
'captcha_height' => $options['height'], 'captcha_height' => $options['height'],
'reload' => $options['reload'], 'reload' => $options['reload'],
'image_id' => uniqid('captcha_'), 'image_id' => uniqid('captcha_'),
'captcha_code' => $this->generator->getCaptchaCode($options), 'captcha_code' => $this->generator->getCaptchaCode($options),
'value' => '', 'value' => '',
'is_human' => $isHuman 'is_human' => $isHuman,
)); ));
$persistOptions = array(); $persistOptions = array();
foreach (array('phrase', 'width', 'height', 'distortion', 'length', 'quality', 'background_color', 'text_color') as $key) { foreach (array('phrase', 'width', 'height', 'distortion', 'length',
'quality', 'background_color', 'background_images', 'text_color', ) as $key) {
$persistOptions[$key] = $options[$key]; $persistOptions[$key] = $options[$key];
} }
@ -126,31 +122,21 @@ class CaptchaType extends AbstractType
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$this->options['mapped'] = false; $this->options['mapped'] = false;
$this->options['request'] = null;
$resolver->setDefaults($this->options); $resolver->setDefaults($this->options);
} }
/** public function getParent(): string
* {@inheritdoc}
* BC for SF < 2.7
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{ {
$this->configureOptions($resolver); return TextType::class;
} }
/** public function getName(): string
* @return string
*/
public function getParent()
{ {
// Not using ::class to support Symfony 2.8 w/ php<5.5 return $this->getBlockPrefix();
return 'Symfony\Component\Form\Extension\Core\Type\TextType';
} }
/** public function getBlockPrefix(): string
* @return string
*/
public function getName()
{ {
return 'captcha'; return 'captcha';
} }

View File

@ -1,73 +1,86 @@
<?php <?php
declare(strict_types=1);
namespace Gregwar\CaptchaBundle\Validator; namespace Gregwar\CaptchaBundle\Validator;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\HttpFoundation\Request;
/** /**
* Captcha validator * Captcha validator.
* *
* @author Gregwar <g.passault@gmail.com> * @author Gregwar <g.passault@gmail.com>
*/ */
class CaptchaValidator class CaptchaValidator
{ {
/** /** @var SessionInterface */
* @var SessionInterface
*/
private $session; private $session;
/** /**
* Session key to store the code * Session key to store the code.
*
* @var string
*/ */
private $key; private $key;
/** /**
* Error message text for non-matching submissions * Error message text for non-matching submissions.
*
* @var string
*/ */
private $invalidMessage; private $invalidMessage;
/** /**
* Configuration parameter used to bypass a required code match * Configuration parameter used to bypass a required code match.
*
* @var string
*/ */
private $bypassCode; private $bypassCode;
/** /**
* Number of form that the user can submit without captcha * Number of form that the user can submit without captcha.
*
* @var int * @var int
*/ */
private $humanity; private $humanity;
/** /**
* Translator * Translator.
*
* @var TranslatorInterface * @var TranslatorInterface
*/ */
private $translator; private $translator;
/** /**
* @param TranslatorInterface $translator * Request
* @param SessionInterface $session *
* @param string $key * @var Request
* @param string $invalidMessage
* @param string $bypassCode
* @param int $humanity
*/ */
public function __construct(TranslatorInterface $translator, SessionInterface $session, $key, $invalidMessage, $bypassCode, $humanity) private $req;
{
$this->translator = $translator; public function __construct(
$this->session = $session; TranslatorInterface $translator,
$this->key = $key; SessionInterface $session,
$this->invalidMessage = $invalidMessage; string $key,
$this->bypassCode = (string)$bypassCode; string $invalidMessage,
$this->humanity = $humanity; ?string $bypassCode,
int $humanity,
?Request $req
) {
$this->translator = $translator;
$this->session = $session;
$this->key = $key;
$this->invalidMessage = $invalidMessage;
$this->bypassCode = $bypassCode;
$this->humanity = $humanity;
$this->req = $req;
} }
/** public function validate(FormEvent $event): void
* @param FormEvent $event
*/
public function validate(FormEvent $event)
{ {
$form = $event->getForm(); $form = $event->getForm();
@ -77,12 +90,13 @@ class CaptchaValidator
if ($this->humanity > 0) { if ($this->humanity > 0) {
$humanity = $this->getHumanity(); $humanity = $this->getHumanity();
if ($humanity > 0) { if ($humanity > 0) {
$this->updateHumanity($humanity-1); $this->updateHumanity($humanity - 1);
return; return;
} }
} }
if (!($code !== null && is_string($code) && ($this->compare($code, $expectedCode) || $this->compare($code, $this->bypassCode)))) { if (!(null !== $code && is_string($code) && ($this->compare($code, $expectedCode) || $this->compare($code, $this->bypassCode)))) {
$form->addError(new FormError($this->translator->trans($this->invalidMessage, array(), 'validators'))); $form->addError(new FormError($this->translator->trans($this->invalidMessage, array(), 'validators')));
} else { } else {
if ($this->humanity > 0) { if ($this->humanity > 0) {
@ -90,15 +104,16 @@ class CaptchaValidator
} }
} }
$this->session->remove($this->key); if (null == $this->req || 1 < $this->req->get('flow_registration_step')) {
$this->session->remove($this->key);
if ($this->session->has($this->key . '_fingerprint')) { if ($this->session->has($this->key.'_fingerprint')) {
$this->session->remove($this->key . '_fingerprint'); $this->session->remove($this->key.'_fingerprint');
}
} }
} }
/** /**
* Retrieve the expected CAPTCHA code * Retrieve the expected CAPTCHA code.
* *
* @return mixed|null * @return mixed|null
*/ */
@ -114,51 +129,39 @@ class CaptchaValidator
} }
/** /**
* Retrieve the humanity * Retrieve the humanity.
* *
* @return mixed|null * @return mixed|null
*/ */
protected function getHumanity() protected function getHumanity()
{ {
return $this->session->get($this->key . '_humanity', 0); return $this->session->get($this->key.'_humanity', 0);
} }
/** protected function updateHumanity(int $newValue): void
* Updates the humanity
*/
protected function updateHumanity($newValue)
{ {
if ($newValue > 0) { if ($newValue > 0) {
$this->session->set($this->key . '_humanity', $newValue); $this->session->set($this->key.'_humanity', $newValue);
} else { } else {
$this->session->remove($this->key . '_humanity'); $this->session->remove($this->key.'_humanity');
} }
return null;
} }
/** protected function niceize(string $code): string
* Process the codes
*
* @param $code
*
* @return string
*/
protected function niceize($code)
{ {
return strtr(strtolower($code), 'oil', '01l'); return strtr(strtolower($code), 'oil', '01l');
} }
/** /**
* Run a match comparison on the provided code and the expected code * Run a match comparison on the provided code and the expected code.
* *
* @param $code * @param string $code
* @param $expectedCode * @param string|null $expectedCode
* *
* @return bool * @return bool
*/ */
protected function compare($code, $expectedCode) protected function compare($code, $expectedCode): bool
{ {
return ($expectedCode !== null && is_string($expectedCode) && $this->niceize($code) == $this->niceize($expectedCode)); return null !== $expectedCode && is_string($expectedCode) && $this->niceize($code) == $this->niceize($expectedCode);
} }
} }

View File

@ -1,9 +1,9 @@
{ {
"name": "gregwar/captcha-bundle", "name": "cadoles/captcha",
"type": "captcha-bundle", "type": "symfony-bundle",
"description": "Captcha bundle", "description": "Captcha bundle",
"keywords": ["symfony2", "captcha", "bot", "visual", "code", "security", "spam"], "keywords": ["symfony2", "symfony", "captcha", "bot", "visual", "code", "security", "spam"],
"homepage": "https://github.com/Gregwar/CaptchaBundle", "homepage": "https://github.com/Cadoles/CaptchaBundle",
"license": "MIT", "license": "MIT",
"authors": [ "authors": [
{ {
@ -17,14 +17,23 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.9", "php": ">=7.1.3",
"gregwar/captcha": "~1.1", "ext-gd": "*",
"symfony/framework-bundle": "~2.1|~3.0", "gregwar/captcha": "^1.1.9",
"symfony/form": "~2.1|~3.0" "symfony/form": "~4.0|~5.0",
"symfony/framework-bundle": "~4.0|~5.0",
"symfony/translation": "~4.0|^5.0",
"twig/twig": "^2.10|^3.0"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Gregwar\\CaptchaBundle\\": "/" "Gregwar\\CaptchaBundle\\": "/"
} }
},
"config": {
"sort-packages": true
},
"require-dev": {
"symplify/easy-coding-standard": "^6.1"
} }
} }

138
ecs.yaml Normal file
View File

@ -0,0 +1,138 @@
parameters:
exclude_files:
- 'vendor/*'
- 'LICENSE'
- 'README.md'
services:
# PSR1
PhpCsFixer\Fixer\Basic\EncodingFixer: ~
PhpCsFixer\Fixer\PhpTag\FullOpeningTagFixer: ~
PhpCsFixer\Fixer\NamespaceNotation\BlankLineAfterNamespaceFixer: ~
PhpCsFixer\Fixer\ControlStructure\ElseifFixer: ~
PhpCsFixer\Fixer\FunctionNotation\FunctionDeclarationFixer: ~
PhpCsFixer\Fixer\Whitespace\IndentationTypeFixer: ~
PhpCsFixer\Fixer\Whitespace\LineEndingFixer: ~
PhpCsFixer\Fixer\Casing\ConstantCaseFixer: ~
PhpCsFixer\Fixer\Casing\LowercaseKeywordsFixer: ~
PhpCsFixer\Fixer\FunctionNotation\MethodArgumentSpaceFixer:
ensure_fully_multiline: true
PhpCsFixer\Fixer\ControlStructure\NoBreakCommentFixer: ~
PhpCsFixer\Fixer\PhpTag\NoClosingTagFixer: ~
PhpCsFixer\Fixer\FunctionNotation\NoSpacesAfterFunctionNameFixer: ~
PhpCsFixer\Fixer\Whitespace\NoSpacesInsideParenthesisFixer: ~
PhpCsFixer\Fixer\Whitespace\NoTrailingWhitespaceFixer: ~
PhpCsFixer\Fixer\Comment\NoTrailingWhitespaceInCommentFixer: ~
PhpCsFixer\Fixer\Whitespace\SingleBlankLineAtEofFixer: ~
PhpCsFixer\Fixer\ClassNotation\SingleClassElementPerStatementFixer:
elements:
- 'property'
PhpCsFixer\Fixer\Import\SingleImportPerStatementFixer: ~
PhpCsFixer\Fixer\Import\SingleLineAfterImportsFixer: ~
PhpCsFixer\Fixer\ControlStructure\SwitchCaseSemicolonToColonFixer: ~
PhpCsFixer\Fixer\ControlStructure\SwitchCaseSpaceFixer: ~
PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer: ~
PhpCsFixer\Fixer\Basic\BracesFixer:
allow_single_line_closure: true
PhpCsFixer\Fixer\PhpTag\BlankLineAfterOpeningTagFixer: ~
PhpCsFixer\Fixer\Operator\ConcatSpaceFixer:
spacing: none
PhpCsFixer\Fixer\Operator\NewWithBracesFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocAlignFixer:
tags:
- method
- param
- property
- return
- throws
- type
- var
PhpCsFixer\Fixer\Operator\BinaryOperatorSpacesFixer: ~
PhpCsFixer\Fixer\Operator\IncrementStyleFixer: ~
PhpCsFixer\Fixer\Operator\UnaryOperatorSpacesFixer: ~
PhpCsFixer\Fixer\Whitespace\BlankLineBeforeStatementFixer: ~
PhpCsFixer\Fixer\CastNotation\CastSpacesFixer: ~
PhpCsFixer\Fixer\LanguageConstruct\DeclareEqualNormalizeFixer: ~
PhpCsFixer\Fixer\FunctionNotation\FunctionTypehintSpaceFixer: ~
PhpCsFixer\Fixer\Comment\SingleLineCommentStyleFixer:
comment_types:
- hash
PhpCsFixer\Fixer\ControlStructure\IncludeFixer: ~
PhpCsFixer\Fixer\CastNotation\LowercaseCastFixer: ~
PhpCsFixer\Fixer\ClassNotation\ClassAttributesSeparationFixer:
elements:
- method
PhpCsFixer\Fixer\Casing\NativeFunctionCasingFixer: ~
PhpCsFixer\Fixer\ClassNotation\NoBlankLinesAfterClassOpeningFixer: ~
PhpCsFixer\Fixer\Phpdoc\NoBlankLinesAfterPhpdocFixer: ~
PhpCsFixer\Fixer\Comment\NoEmptyCommentFixer: ~
PhpCsFixer\Fixer\Phpdoc\NoEmptyPhpdocFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocSeparationFixer: ~
PhpCsFixer\Fixer\Semicolon\NoEmptyStatementFixer: ~
PhpCsFixer\Fixer\Whitespace\NoExtraBlankLinesFixer:
tokens:
- curly_brace_block
- extra
- parenthesis_brace_block
- square_brace_block
- throw
- use
PhpCsFixer\Fixer\NamespaceNotation\NoLeadingNamespaceWhitespaceFixer: ~
PhpCsFixer\Fixer\ArrayNotation\NoMultilineWhitespaceAroundDoubleArrowFixer: ~
PhpCsFixer\Fixer\CastNotation\NoShortBoolCastFixer: ~
PhpCsFixer\Fixer\Semicolon\NoSinglelineWhitespaceBeforeSemicolonsFixer: ~
PhpCsFixer\Fixer\Whitespace\NoSpacesAroundOffsetFixer: ~
PhpCsFixer\Fixer\ControlStructure\NoTrailingCommaInListCallFixer: ~
PhpCsFixer\Fixer\ArrayNotation\NoTrailingCommaInSinglelineArrayFixer: ~
PhpCsFixer\Fixer\ArrayNotation\TrailingCommaInMultilineArrayFixer: ~
PhpCsFixer\Fixer\ControlStructure\NoUnneededControlParenthesesFixer: ~
PhpCsFixer\Fixer\ArrayNotation\NoWhitespaceBeforeCommaInArrayFixer: ~
PhpCsFixer\Fixer\Whitespace\NoWhitespaceInBlankLineFixer: ~
PhpCsFixer\Fixer\ArrayNotation\NormalizeIndexBraceFixer: ~
PhpCsFixer\Fixer\Operator\ObjectOperatorWithoutWhitespaceFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocAnnotationWithoutDotFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocIndentFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocInlineTagFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocNoAccessFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocNoEmptyReturnFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocNoPackageFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocNoUselessInheritdocFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocReturnSelfReferenceFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocScalarFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocSingleLineVarSpacingFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocSummaryFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocToCommentFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocTrimFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocTypesFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocVarWithoutNameFixer: ~
PhpCsFixer\Fixer\FunctionNotation\ReturnTypeDeclarationFixer: ~
PhpCsFixer\Fixer\ClassNotation\SelfAccessorFixer: ~
PhpCsFixer\Fixer\CastNotation\ShortScalarCastFixer: ~
PhpCsFixer\Fixer\StringNotation\SingleQuoteFixer: ~
PhpCsFixer\Fixer\Semicolon\SpaceAfterSemicolonFixer: ~
PhpCsFixer\Fixer\Operator\StandardizeNotEqualsFixer: ~
PhpCsFixer\Fixer\Operator\TernaryOperatorSpacesFixer: ~
PhpCsFixer\Fixer\ArrayNotation\TrimArraySpacesFixer: ~
PhpCsFixer\Fixer\ArrayNotation\WhitespaceAfterCommaInArrayFixer: ~
PhpCsFixer\Fixer\ClassNotation\ClassDefinitionFixer:
singleLine: true
PhpCsFixer\Fixer\Casing\MagicConstantCasingFixer: ~
PhpCsFixer\Fixer\Alias\NoMixedEchoPrintFixer:
use: echo
PhpCsFixer\Fixer\Import\NoLeadingImportSlashFixer: ~
PhpCsFixer\Fixer\Import\NoUnusedImportsFixer: ~
PhpCsFixer\Fixer\PhpUnit\PhpUnitFqcnAnnotationFixer: ~
PhpCsFixer\Fixer\Phpdoc\PhpdocNoAliasTagFixer: ~
PhpCsFixer\Fixer\ClassNotation\ProtectedToPrivateFixer: ~
PhpCsFixer\Fixer\NamespaceNotation\SingleBlankLineBeforeNamespaceFixer: ~
# new since PHP-CS-Fixer 2.6
PhpCsFixer\Fixer\ControlStructure\NoUnneededCurlyBracesFixer: ~
PhpCsFixer\Fixer\ClassNotation\NoUnneededFinalMethodFixer: ~
PhpCsFixer\Fixer\Semicolon\SemicolonAfterInstructionFixer: ~
PhpCsFixer\Fixer\ControlStructure\YodaStyleFixer: ~
# new since 2.11
PhpCsFixer\Fixer\Operator\StandardizeIncrementFixer: ~