Compare commits
245 Commits
Author | SHA1 | Date | |
---|---|---|---|
5778c54ae0 | |||
9b65966d27 | |||
633e30ae47 | |||
2f96c759ab | |||
2769e4791a | |||
5e434e1859 | |||
df6915eb5e | |||
b7685e63a1 | |||
15bee25e9c | |||
3a4e31473a | |||
fec0ebb2f1 | |||
cdbe566acc | |||
93f9d3c1a7 | |||
d4475118d0 | |||
b8a129fc2a | |||
fe4b0dea2b | |||
640acadeea | |||
a22ba77f52 | |||
459a108ee6 | |||
3691a30240 | |||
2012808bc6 | |||
2b62be4af1 | |||
3f25597f94 | |||
478c64633e | |||
2ed4f74954 | |||
60c0e04366 | |||
61a8561dd6 | |||
5515c25872 | |||
eeb01bdca5 | |||
678459f58f | |||
1781a6aa62 | |||
3ccfdf1c93 | |||
b703ed1a0c | |||
25de43ac90 | |||
1b4835eb4d | |||
be1ce45060 | |||
0f6cd70920 | |||
754310f488 | |||
c2d5468556 | |||
980afdc10a | |||
791128c0fd | |||
639430383f | |||
8e98c5c0ab | |||
539884cd5d | |||
8ce4adb1b1 | |||
b787a8002e | |||
ba9c0e6166 | |||
25b8840f2a | |||
045ba7e67e | |||
18c85d3a4f | |||
f6c45045f0 | |||
f95a951b26 | |||
e1ed228b8b | |||
0b3495a081 | |||
53c25b2e9a | |||
fcf8c4fd01 | |||
e91cb1a3b7 | |||
1f6c80c326 | |||
f06ff4d2c2 | |||
42cb26794d | |||
e10494a767 | |||
8682eee873 | |||
c508d510ff | |||
d328f215b2 | |||
a26f03cc93 | |||
1862776c9a | |||
c989422a82 | |||
52107b0c32 | |||
e335e2a924 | |||
0971f224f4 | |||
29610bb574 | |||
018fdd3f35 | |||
476530a212 | |||
87d38d98a4 | |||
3cc4b072c3 | |||
0f5e9870f1 | |||
c844cbcdbc | |||
50405a74a1 | |||
84760b0a9f | |||
ef281889ab | |||
8468f93194 | |||
fe1102f5c6 | |||
f1fdfc142f | |||
2e49f50c1a | |||
8d3ee7334f | |||
805e77f24a | |||
adab98ad84 | |||
4040d06508 | |||
448b812f65 | |||
4aba359e71 | |||
8c39274fa4 | |||
232168d408 | |||
6a147a2ea3 | |||
c221d9cb42 | |||
7574ad33f1 | |||
d80ff1959b | |||
694994c3c2 | |||
01429c4486 | |||
6032e5df52 | |||
d77e0b1f2a | |||
a4176fcf74 | |||
909d1a3773 | |||
d6c6de0f83 | |||
ac95e1d4b1 | |||
c844d0a440 | |||
f20fc3c3b7 | |||
0cb34f33b0 | |||
843f367540 | |||
94c5f0a03b | |||
6a522b7043 | |||
3d1383e8ae | |||
62e44cd388 | |||
459901319c | |||
1c542e1164 | |||
dc3e0c6495 | |||
bd208c45a8 | |||
9614209d83 | |||
90088c80fc | |||
06c713fbb4 | |||
162a2a87cd | |||
fe3df8a640 | |||
b87b05e29d | |||
cdf64984b7 | |||
458e7cafef | |||
ce96122ef2 | |||
c4c69b0a47 | |||
6b573121a5 | |||
e34e191d32 | |||
7dd05f5389 | |||
de0d9408d6 | |||
cb45fe2847 | |||
1fec709d05 | |||
35cbc6226c | |||
7c38520965 | |||
277e3baff0 | |||
798f29e635 | |||
f2e07fe6f2 | |||
ab786e3b0e | |||
f206bb66cc | |||
3340af5c43 | |||
bc3fdda50b | |||
b2340539d2 | |||
8199d20092 | |||
8770c50ff0 | |||
721a2b1f3d | |||
e2f1966bfb | |||
2f508ddd07 | |||
2e803e1471 | |||
800643d72c | |||
c09b7245b1 | |||
c3c8904862 | |||
9d4f95e39b | |||
107dbe33e4 | |||
34a84af209 | |||
3ad62e4d0d | |||
a16743a230 | |||
4f46a609ba | |||
b085af6e00 | |||
6b4e7db721 | |||
aef6a785ca | |||
fc72ab4f6c | |||
e3727a975d | |||
9d741e0ac6 | |||
0ab26cac81 | |||
ddab3ef005 | |||
0b08b38cb0 | |||
3f64e064d3 | |||
9578caab10 | |||
7f30646c68 | |||
5efb9a73b5 | |||
daec205870 | |||
1881d292b8 | |||
181b0cd0d0 | |||
c21bbf13c5 | |||
7ca6dacb3b | |||
39e2f390f0 | |||
be90f19f3a | |||
0b56f5cd87 | |||
2d8d9f3b5f | |||
4e13d2d4cb | |||
1fef229c8f | |||
de2ab52bae | |||
6f546713a9 | |||
877e2d740d | |||
5f2afc623c | |||
45e4f72394 | |||
19142f30e4 | |||
6c953ea228 | |||
206869dd67 | |||
7b819872b4 | |||
95342e80a9 | |||
34de9cd753 | |||
83a5a78c16 | |||
42f6e92560 | |||
2e17d9c035 | |||
4bb240be05 | |||
c224b93033 | |||
0108219264 | |||
aed2fa343e | |||
2ea29e0f9a | |||
73b78ec537 | |||
d103614cc7 | |||
95b28d9cec | |||
435cc44f91 | |||
c551ca018e | |||
9e599febb0 | |||
35405aca2e | |||
6b340eb258 | |||
c8c1bd0011 | |||
8d6a628dda | |||
d91b9a9444 | |||
11fe650bde | |||
3794a12e80 | |||
516046a6b5 | |||
b993e4b4cd | |||
fd2b602196 | |||
31d080a9b7 | |||
39532390e1 | |||
c1b702566b | |||
8885e6bcac | |||
beec51e975 | |||
1437f0c7e0 | |||
ce1b590ae9 | |||
ba9e0818a5 | |||
4d7534351d | |||
fef3e306fd | |||
9fc82c8453 | |||
0bd0173551 | |||
a8b45f737d | |||
a41e4dd865 | |||
78e1cee035 | |||
51edd50acc | |||
4c28dd420f | |||
94f6b5277d | |||
141b976479 | |||
22f494e27a | |||
c025ea9b0d | |||
b9f003c9cf | |||
df2f9b3ada | |||
d89a966315 | |||
589e8f0bcb | |||
f9885acde9 | |||
9980108880 | |||
ece2a68170 | |||
8d54bfd598 |
76
Controller/CaptchaController.php
Normal file
76
Controller/CaptchaController.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Gregwar\CaptchaBundle\Controller;
|
||||
|
||||
use Gregwar\CaptchaBundle\Generator\CaptchaGenerator;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Generates a captcha via a URL.
|
||||
*
|
||||
* @author Jeremy Livingston <jeremy.j.livingston@gmail.com>
|
||||
*/
|
||||
class CaptchaController extends AbstractController
|
||||
{
|
||||
/** @var CaptchaGenerator */
|
||||
private $captchaGenerator;
|
||||
|
||||
/** @var array */
|
||||
private $config;
|
||||
|
||||
public function __construct(CaptchaGenerator $captchaGenerator, array $config)
|
||||
{
|
||||
$this->captchaGenerator = $captchaGenerator;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function generateCaptchaAction(Request $request, string $key): Response
|
||||
{
|
||||
$session = $request->getSession();
|
||||
$whitelistKey = $this->config['whitelist_key'];
|
||||
$isOk = false;
|
||||
|
||||
if ($session->has($whitelistKey)) {
|
||||
$keys = $session->get($whitelistKey);
|
||||
if (is_array($keys) && in_array($key, $keys)) {
|
||||
$isOk = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$isOk) {
|
||||
return $this->error($this->config);
|
||||
}
|
||||
|
||||
$persistedOptions = $session->get($key, array());
|
||||
$options = array_merge($this->config, $persistedOptions);
|
||||
|
||||
$phrase = $this->captchaGenerator->getPhrase($options);
|
||||
$this->captchaGenerator->setPhrase($phrase);
|
||||
$persistedOptions['phrase'] = $phrase;
|
||||
$session->set($key, $persistedOptions);
|
||||
|
||||
$response = new Response($this->captchaGenerator->generate($options));
|
||||
$response->headers->set('Content-type', 'image/jpeg');
|
||||
$response->headers->set('Pragma', 'no-cache');
|
||||
$response->headers->set('Cache-Control', 'no-cache');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function error(array $options): Response
|
||||
{
|
||||
$this->captchaGenerator->setPhrase('');
|
||||
|
||||
$response = new Response($this->captchaGenerator->generate($options));
|
||||
$response->setStatusCode(428);
|
||||
$response->headers->set('Content-type', 'image/jpeg');
|
||||
$response->headers->set('Pragma', 'no-cache');
|
||||
$response->headers->set('Cache-Control', 'no-cache');
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Gregwar\CaptchaBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
@ -7,33 +9,45 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
/**
|
||||
* Generates the configuration tree.
|
||||
* @return TreeBuilder
|
||||
*/
|
||||
public function getConfigTreeBuilder()
|
||||
public function getConfigTreeBuilder(): TreeBuilder
|
||||
{
|
||||
$treeBuilder = new TreeBuilder();
|
||||
$rootNode = $treeBuilder->root('gregwar_captcha', 'array');
|
||||
$treeBuilder = new TreeBuilder('gregwar_captcha');
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
|
||||
$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('web_path')->defaultValue('%kernel.project_dir%/public')->end()
|
||||
->scalarNode('gc_freq')->defaultValue(100)->end()
|
||||
->scalarNode('expiration')->defaultValue(60)->end()
|
||||
->scalarNode('quality')->defaultValue(15)->end()
|
||||
->scalarNode('quality')->defaultValue(50)->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()
|
||||
->arrayNode('background_images')->prototype('scalar')->end()->end()
|
||||
->scalarNode('disabled')->defaultValue(false)->end()
|
||||
->scalarNode('ignore_all_effects')->defaultValue(false)->end()
|
||||
->scalarNode('session_key')->defaultValue('captcha')->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Gregwar\CaptchaBundle\DependencyInjection;
|
||||
|
||||
use Exception;
|
||||
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
|
||||
{
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
/**
|
||||
* @param array $configs
|
||||
* @param ContainerBuilder $container
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container): void
|
||||
{
|
||||
|
||||
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||
$loader->load('services.yml');
|
||||
|
||||
@ -20,11 +32,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('@GregwarCaptcha/captcha.html.twig'), $resources));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,278 +1,143 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Gregwar\CaptchaBundle\Generator;
|
||||
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Gregwar\Captcha\CaptchaBuilder;
|
||||
use Gregwar\Captcha\PhraseBuilder;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Gregwar\Captcha\CaptchaBuilderInterface;
|
||||
use Gregwar\Captcha\PhraseBuilderInterface;
|
||||
|
||||
/**
|
||||
* Generates a CAPTCHA image
|
||||
* Uses configuration parameters to call the services that generate captcha images.
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
* @author Jeremy Livingston <jeremy.j.livingston@gmail.com>
|
||||
*/
|
||||
class CaptchaGenerator {
|
||||
class CaptchaGenerator
|
||||
{
|
||||
/** @var RouterInterface */
|
||||
protected $router;
|
||||
|
||||
/** @var CaptchaBuilder */
|
||||
protected $builder;
|
||||
|
||||
/** @var PhraseBuilder */
|
||||
protected $phraseBuilder;
|
||||
|
||||
/** @var ImageFileHandler */
|
||||
protected $imageFileHandler;
|
||||
|
||||
/**
|
||||
* Name of folder for captcha images
|
||||
* @var string
|
||||
* @param RouterInterface $router
|
||||
* @param CaptchaBuilderInterface $builder
|
||||
* @param PhraseBuilderInterface $phraseBuilder
|
||||
* @param ImageFileHandler $imageFileHandler
|
||||
*/
|
||||
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);
|
||||
public function __construct(
|
||||
RouterInterface $router,
|
||||
CaptchaBuilderInterface $builder,
|
||||
PhraseBuilderInterface $phraseBuilder,
|
||||
ImageFileHandler $imageFileHandler
|
||||
) {
|
||||
$this->router = $router;
|
||||
$this->builder = $builder;
|
||||
$this->phraseBuilder = $phraseBuilder;
|
||||
$this->imageFileHandler = $imageFileHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the captcha embeded code
|
||||
*/
|
||||
public function getCode($width = 120, $height = 40)
|
||||
public function getCaptchaCode(array &$options): string
|
||||
{
|
||||
return 'data:image/jpeg;base64,'.base64_encode($this->generate($width, $height));
|
||||
}
|
||||
$this->builder->setPhrase($this->getPhrase($options));
|
||||
|
||||
/**
|
||||
* Creates a captcha image with provided dimensions
|
||||
* and randomly executes a garbage collection
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return string Web path to the created image
|
||||
*/
|
||||
public function getFile($width = 120, $height = 40)
|
||||
{
|
||||
if (mt_rand(1, $this->gcFreq) == 1) {
|
||||
$this->garbageCollection();
|
||||
// Randomly execute garbage collection and returns the image filename
|
||||
if ($options['as_file']) {
|
||||
$this->imageFileHandler->collectGarbage();
|
||||
|
||||
return $this->generate($options);
|
||||
}
|
||||
|
||||
return $this->generate($width, $height, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random number or the next number in the
|
||||
* fingerprint
|
||||
*/
|
||||
public function rand($min, $max)
|
||||
{
|
||||
if (!is_array($this->fingerprint)) {
|
||||
$this->fingerprint = array();
|
||||
// Returns the image generation URL
|
||||
if ($options['as_url']) {
|
||||
return $this->router->generate(
|
||||
'gregwar_captcha.generate_captcha',
|
||||
array('key' => $options['session_key'], 'n' => md5(microtime(true).mt_rand()))
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->use_fingerprint) {
|
||||
$value = current($this->fingerprint);
|
||||
next($this->fingerprint);
|
||||
} else {
|
||||
$value = mt_rand($min, $max);
|
||||
$this->fingerprint[] = $value;
|
||||
}
|
||||
return $value;
|
||||
return 'data:image/jpeg;base64,'.base64_encode($this->generate($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CAPTCHA fingerprint
|
||||
*/
|
||||
public function getFingerprint()
|
||||
public function setPhrase(string $phrase): void
|
||||
{
|
||||
return $this->fingerprint;
|
||||
$this->builder->setPhrase($phrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all images in the configured folder
|
||||
* that are older than 10 minutes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function garbageCollection()
|
||||
public function generate(array &$options): string
|
||||
{
|
||||
$finder = new Finder();
|
||||
$criteria = sprintf('<= now - %s minutes', $this->expiration);
|
||||
$finder->in($this->webPath . '/' . $this->imageFolder)
|
||||
->date($criteria);
|
||||
$this->builder->setDistortion($options['distortion']);
|
||||
|
||||
foreach($finder->files() as $file)
|
||||
{
|
||||
unlink($file->getPathname());
|
||||
}
|
||||
}
|
||||
$this->builder->setMaxFrontLines($options['max_front_lines']);
|
||||
$this->builder->setMaxBehindLines($options['max_behind_lines']);
|
||||
|
||||
/**
|
||||
* Generate the image
|
||||
*/
|
||||
public function generate($width, $height, $createFile = false)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
imagettftext($i, $size, 0, ($width-$txt_width)/2, ($height-$txt_height)/2+$size, $col, $font, $this->value);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
imagesetpixel($out, $x, $y, $p);
|
||||
if (isset($options['text_color']) && $options['text_color']) {
|
||||
if (3 !== count($options['text_color'])) {
|
||||
throw new \RuntimeException('text_color should be an array of r, g and b');
|
||||
}
|
||||
|
||||
// Renders it
|
||||
if (!$createFile) {
|
||||
$color = $options['text_color'];
|
||||
$this->builder->setTextColor($color[0], $color[1], $color[2]);
|
||||
}
|
||||
|
||||
if (isset($options['background_color']) && $options['background_color']) {
|
||||
if (3 !== count($options['background_color'])) {
|
||||
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;
|
||||
|
||||
$this->builder->setBackgroundImages($options['background_images']);
|
||||
$this->builder->setIgnoreAllEffects($options['ignore_all_effects']);
|
||||
|
||||
$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();
|
||||
} 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;
|
||||
}
|
||||
|
||||
return $this->imageFileHandler->saveAsFile($content);
|
||||
}
|
||||
|
||||
protected function getCol($image, $x, $y)
|
||||
public function getPhrase(array &$options): string
|
||||
{
|
||||
$L = imagesx($image);
|
||||
$H = imagesy($image);
|
||||
if ($x<0 || $x>=$L || $y<0 || $y>=$H)
|
||||
return 0xFFFFFF;
|
||||
else return imagecolorat($image, $x, $y);
|
||||
}
|
||||
// Get the phrase that we'll use for this image
|
||||
if ($options['keep_value'] && isset($options['phrase'])) {
|
||||
$phrase = $options['phrase'];
|
||||
} else {
|
||||
$phrase = $this->phraseBuilder->build($options['length'], $options['charset']);
|
||||
$options['phrase'] = $phrase;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
96
Generator/ImageFileHandler.php
Normal file
96
Generator/ImageFileHandler.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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 string $imageFolder
|
||||
* @param string $webPath
|
||||
* @param string $gcFreq
|
||||
* @param string $expiration
|
||||
*/
|
||||
public function __construct(string $imageFolder, string $webPath, string $gcFreq, string $expiration)
|
||||
{
|
||||
$this->imageFolder = $imageFolder;
|
||||
$this->webPath = $webPath;
|
||||
$this->gcFreq = $gcFreq;
|
||||
$this->expiration = $expiration;
|
||||
}
|
||||
|
||||
public function saveAsFile($contents): string
|
||||
{
|
||||
$this->createFolderIfMissing();
|
||||
|
||||
$filename = md5(uniqid()).'.jpg';
|
||||
$filePath = $this->webPath.'/'.$this->imageFolder.'/'.$filename;
|
||||
imagejpeg($contents, $filePath, 15);
|
||||
|
||||
return '/'.$this->imageFolder.'/'.$filename;
|
||||
}
|
||||
|
||||
public function collectGarbage(): bool
|
||||
{
|
||||
if (1 == !mt_rand(1, $this->gcFreq)) {
|
||||
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;
|
||||
}
|
||||
|
||||
protected function createFolderIfMissing(): void
|
||||
{
|
||||
if (!file_exists($this->webPath.'/'.$this->imageFolder)) {
|
||||
mkdir($this->webPath.'/'.$this->imageFolder, 0755);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Gregwar\CaptchaBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright (c) <2011> Grégoire Passault
|
||||
Copyright (c) <2011-2015> 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
|
||||
|
213
README.md
213
README.md
@ -1,164 +1,167 @@
|
||||
Gregwar's CaptchaBundle
|
||||
=====================
|
||||
|
||||
The `GregwarCaptchaBundle` adds support for a "captcha" form type for the
|
||||
Symfony2 form component.
|
||||
[](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
|
||||
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
|
||||
==========================
|
||||
|
||||
| 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 |
|
||||
|
||||
Important note: the master of this repository is containing current development
|
||||
in order to work with Symfony 2.1. If you are using Symfony 2.0 please checkout
|
||||
the 2.0 branch.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
### Step 1: Download the GregwarCaptchaBundle
|
||||
|
||||
Ultimately, the GregwarCaptchaBundle files should be downloaded to the
|
||||
'vendor/bundles/Gregwar/CaptchaBundle' directory.
|
||||
|
||||
You can accomplish this several ways, depending on your personal preference.
|
||||
The first method is the standard Symfony2 method.
|
||||
|
||||
***Using the vendors script***
|
||||
|
||||
Add the following lines to your `deps` file:
|
||||
|
||||
```
|
||||
[GregwarCaptchaBundle]
|
||||
git=git://github.com/Gregwar/CaptchaBundle.git
|
||||
target=/bundles/Gregwar/CaptchaBundle
|
||||
```
|
||||
|
||||
Now, run the vendors script to download the bundle:
|
||||
Use composer require to download and install the package.
|
||||
At the end of the installation, the bundle is automatically registered thanks to the Symfony recipe.
|
||||
|
||||
``` bash
|
||||
$ php bin/vendors install
|
||||
composer require gregwar/captcha-bundle
|
||||
```
|
||||
|
||||
***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
|
||||
```
|
||||
|
||||
***Using Composer***
|
||||
|
||||
Add the following to the "require" section of your `composer.json` file:
|
||||
|
||||
```
|
||||
"gregwar/captcha-bundle": "1.0.0"
|
||||
```
|
||||
|
||||
And update your dependencies
|
||||
|
||||
### 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->registerNamspaces(array(
|
||||
// ...
|
||||
'Gregwar' => __DIR__.'/../vendor/bundles',
|
||||
));
|
||||
```
|
||||
### Step 3: Enable the bundle
|
||||
|
||||
Finally, enable the bundle in the kernel:
|
||||
|
||||
If you don't use flex, register it manually:
|
||||
```php
|
||||
<?php
|
||||
// app/appKernel.php
|
||||
|
||||
public function registerBundles()
|
||||
{
|
||||
$bundles = array(
|
||||
// ...
|
||||
new Gregwar\CaptchaBundle\GregwarCaptchaBundle(),
|
||||
);
|
||||
}
|
||||
// config/bundles.php
|
||||
return [
|
||||
// ...
|
||||
Gregwar\CaptchaBundle\GregwarCaptchaBundle::class => ['all' => true]
|
||||
];
|
||||
```
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Add the following configuration to your `app/config/config.yml`:
|
||||
|
||||
gregwar_captcha: ~
|
||||
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:
|
||||
width: 160
|
||||
height: 50
|
||||
```
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
You can use the "captcha" type in your forms this way:
|
||||
|
||||
```php
|
||||
``` php
|
||||
<?php
|
||||
// ...
|
||||
$builder->add('captcha', 'captcha'); // That's all !
|
||||
// ...
|
||||
use Gregwar\CaptchaBundle\Type\CaptchaType;
|
||||
// ...
|
||||
$builder->add('captcha', CaptchaType::class); // That's all !
|
||||
// ...
|
||||
```
|
||||
|
||||
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")
|
||||
* **web_path**: absolute path to public web folder (default="%kernel.root_dir%/../web")
|
||||
* **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.project_dir%/public')
|
||||
* **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=30)
|
||||
* **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
|
||||
* **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
|
||||
* **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 :
|
||||
|
||||
```php
|
||||
``` php
|
||||
<?php
|
||||
// ...
|
||||
$builder->add('captcha', 'captcha', array(
|
||||
'width' => 200,
|
||||
'height' => 50,
|
||||
'length' => 6,
|
||||
));
|
||||
use Gregwar\CaptchaBundle\Type\CaptchaType;
|
||||
// ...
|
||||
$builder->add('captcha', CaptchaType::class, array(
|
||||
'width' => 200,
|
||||
'height' => 50,
|
||||
'length' => 6,
|
||||
));
|
||||
```
|
||||
|
||||
You can also set these options for your whole application using the `gregwar_captcha`
|
||||
configuration entry in your `config.yml` file:
|
||||
``` yaml
|
||||
gregwar_captcha:
|
||||
width: 200
|
||||
height: 50
|
||||
length: 6
|
||||
```
|
||||
|
||||
gregwar_captcha:
|
||||
width: 200
|
||||
height: 50
|
||||
length: 6
|
||||
Translation
|
||||
===========
|
||||
|
||||
Form theming
|
||||
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 `config/routes.yaml` file:
|
||||
|
||||
``` yaml
|
||||
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:
|
||||
|
||||
``` yaml
|
||||
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:
|
||||
|
||||
```html
|
||||
``` twig
|
||||
{% block captcha_widget %}
|
||||
{% spaceless %}
|
||||
<img src="{{ captcha_code }}" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" />
|
||||
{{ form_widget(form) }}
|
||||
{% spaceless %}
|
||||
{% endspaceless %}
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
|
3
Resources/config/routing/routing.yml
Normal file
3
Resources/config/routing/routing.yml
Normal file
@ -0,0 +1,3 @@
|
||||
gregwar_captcha.generate_captcha:
|
||||
path: /generate-captcha/{key}
|
||||
defaults: { _controller: Gregwar\CaptchaBundle\Controller\CaptchaController::generateCaptchaAction }
|
@ -1,8 +1,58 @@
|
||||
parameters:
|
||||
gregwar_captcha.controller.class: Gregwar\CaptchaBundle\Controller\CaptchaController
|
||||
gregwar_captcha.captcha_type.class: Gregwar\CaptchaBundle\Type\CaptchaType
|
||||
gregwar_captcha.captcha_generator.class: Gregwar\CaptchaBundle\Generator\CaptchaGenerator
|
||||
gregwar_captcha.image_file_handler.class: Gregwar\CaptchaBundle\Generator\ImageFileHandler
|
||||
gregwar_captcha.captcha_builder.class: Gregwar\Captcha\CaptchaBuilder
|
||||
gregwar_captcha.phrase_builder.class: Gregwar\Captcha\PhraseBuilder
|
||||
|
||||
services:
|
||||
# captcha type
|
||||
captcha.type:
|
||||
class: Gregwar\CaptchaBundle\Type\CaptchaType
|
||||
arguments: [ "@session", %gregwar_captcha.config% ]
|
||||
Gregwar\CaptchaBundle\Controller\CaptchaController:
|
||||
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:
|
||||
- '@session'
|
||||
- '@gregwar_captcha.generator'
|
||||
- '@translator'
|
||||
- '%gregwar_captcha.config%'
|
||||
tags:
|
||||
- { name: form.type, alias: captcha }
|
||||
|
||||
gregwar_captcha.generator:
|
||||
class: '%gregwar_captcha.captcha_generator.class%'
|
||||
public: true
|
||||
arguments:
|
||||
- '@router'
|
||||
- '@gregwar_captcha.captcha_builder'
|
||||
- '@gregwar_captcha.phrase_builder'
|
||||
- '@gregwar_captcha.image_file_handler'
|
||||
|
||||
gregwar_captcha.image_file_handler:
|
||||
class: '%gregwar_captcha.image_file_handler.class%'
|
||||
public: true
|
||||
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.captcha_builder.class%'
|
||||
public: true
|
||||
|
||||
gregwar_captcha.phrase_builder:
|
||||
class: '%gregwar_captcha.phrase_builder.class%'
|
||||
public: true
|
||||
|
1
Resources/translations/gregwar_captcha.ar.yml
Normal file
1
Resources/translations/gregwar_captcha.ar.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: تجديد
|
1
Resources/translations/gregwar_captcha.bg.yml
Normal file
1
Resources/translations/gregwar_captcha.bg.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Обнови
|
1
Resources/translations/gregwar_captcha.cs.yml
Normal file
1
Resources/translations/gregwar_captcha.cs.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Obnovit
|
1
Resources/translations/gregwar_captcha.de.yml
Normal file
1
Resources/translations/gregwar_captcha.de.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Erneuern
|
1
Resources/translations/gregwar_captcha.en.yml
Normal file
1
Resources/translations/gregwar_captcha.en.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Renew
|
1
Resources/translations/gregwar_captcha.es.yml
Normal file
1
Resources/translations/gregwar_captcha.es.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Renovar
|
1
Resources/translations/gregwar_captcha.fr.yml
Normal file
1
Resources/translations/gregwar_captcha.fr.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Renouveler
|
1
Resources/translations/gregwar_captcha.it.yml
Normal file
1
Resources/translations/gregwar_captcha.it.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Rinnova
|
1
Resources/translations/gregwar_captcha.nl.yml
Normal file
1
Resources/translations/gregwar_captcha.nl.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Vernieuwen
|
1
Resources/translations/gregwar_captcha.pl.yml
Normal file
1
Resources/translations/gregwar_captcha.pl.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Przeładuj
|
1
Resources/translations/gregwar_captcha.pt_BR.yml
Normal file
1
Resources/translations/gregwar_captcha.pt_BR.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Regerar
|
1
Resources/translations/gregwar_captcha.ro.yml
Normal file
1
Resources/translations/gregwar_captcha.ro.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Reimprospateaza
|
1
Resources/translations/gregwar_captcha.ru.yml
Normal file
1
Resources/translations/gregwar_captcha.ru.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Обновить
|
1
Resources/translations/gregwar_captcha.tr.yml
Normal file
1
Resources/translations/gregwar_captcha.tr.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Yenile
|
1
Resources/translations/gregwar_captcha.uk.yml
Normal file
1
Resources/translations/gregwar_captcha.uk.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: Оновити
|
1
Resources/translations/gregwar_captcha.zh_CN.yml
Normal file
1
Resources/translations/gregwar_captcha.zh_CN.yml
Normal file
@ -0,0 +1 @@
|
||||
Renew: 看不清换一张?
|
1
Resources/translations/validators.ar.yml
Normal file
1
Resources/translations/validators.ar.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: الرمز غير متطابق
|
1
Resources/translations/validators.bg.yml
Normal file
1
Resources/translations/validators.bg.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Грешен код
|
1
Resources/translations/validators.cs.yml
Normal file
1
Resources/translations/validators.cs.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Špatný kontrolní kód
|
1
Resources/translations/validators.de.yml
Normal file
1
Resources/translations/validators.de.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Code stimmt nicht überein
|
1
Resources/translations/validators.en.yml
Normal file
1
Resources/translations/validators.en.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Code does not match
|
1
Resources/translations/validators.es.yml
Normal file
1
Resources/translations/validators.es.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: El valor no coincide
|
1
Resources/translations/validators.fr.yml
Normal file
1
Resources/translations/validators.fr.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Mauvaise valeur pour le code visuel
|
1
Resources/translations/validators.it.yml
Normal file
1
Resources/translations/validators.it.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Il valore non coincide
|
1
Resources/translations/validators.nl.yml
Normal file
1
Resources/translations/validators.nl.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Code komt niet overeen
|
1
Resources/translations/validators.pl.yml
Normal file
1
Resources/translations/validators.pl.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Kod jest niepoprawny
|
1
Resources/translations/validators.pt_BR.yml
Normal file
1
Resources/translations/validators.pt_BR.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Código de verificação inválido
|
1
Resources/translations/validators.ro.yml
Normal file
1
Resources/translations/validators.ro.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Codul de securitate este incorect
|
1
Resources/translations/validators.ru.yml
Normal file
1
Resources/translations/validators.ru.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Неправильный код
|
1
Resources/translations/validators.tr.yml
Normal file
1
Resources/translations/validators.tr.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Kod eşleşmiyor
|
1
Resources/translations/validators.uk.yml
Normal file
1
Resources/translations/validators.uk.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: Невірний код
|
1
Resources/translations/validators.zh_CN.yml
Normal file
1
Resources/translations/validators.zh_CN.yml
Normal file
@ -0,0 +1 @@
|
||||
Bad code value: 验证码不正确
|
@ -1,7 +1,19 @@
|
||||
{% block captcha_widget %}
|
||||
{% spaceless %}
|
||||
<img src="{{ captcha_code }}" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" />
|
||||
{% if is_human %}
|
||||
-
|
||||
{% else %}
|
||||
{% apply spaceless %}
|
||||
<img class="captcha_image" 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 %}
|
||||
{% endapply %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
161
Type/CaptchaType.php
Executable file → Normal file
161
Type/CaptchaType.php
Executable file → Normal file
@ -1,122 +1,143 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Gregwar\CaptchaBundle\Type;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
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\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Gregwar\CaptchaBundle\Validator\CaptchaValidator;
|
||||
use Gregwar\CaptchaBundle\Generator\CaptchaGenerator;
|
||||
use Gregwar\CaptchaBundle\DataTransformer\EmptyTransformer;
|
||||
|
||||
/**
|
||||
* Captcha type
|
||||
* Captcha type.
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
*/
|
||||
class CaptchaType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* Options
|
||||
* @var array
|
||||
*/
|
||||
const SESSION_KEY_PREFIX = '_captcha_';
|
||||
|
||||
/** @var SessionInterface */
|
||||
protected $session;
|
||||
|
||||
/** @var CaptchaGenerator */
|
||||
protected $generator;
|
||||
|
||||
/** @var TranslatorInterface */
|
||||
protected $translator;
|
||||
|
||||
/** @var array */
|
||||
private $options = array();
|
||||
|
||||
/**
|
||||
* Session key
|
||||
* @var string
|
||||
* @param SessionInterface $session
|
||||
* @param CaptchaGenerator $generator
|
||||
* @param TranslatorInterface $translator
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->key = $builder->getForm()->getName();
|
||||
|
||||
$builder->addValidator(
|
||||
new CaptchaValidator($this->session, $this->key)
|
||||
$validator = new CaptchaValidator(
|
||||
$this->translator,
|
||||
$this->session,
|
||||
sprintf('%s%s', self::SESSION_KEY_PREFIX, $options['session_key']),
|
||||
$options['invalid_message'],
|
||||
$options['bypass_code'],
|
||||
$options['humanity'],
|
||||
$options['request']
|
||||
);
|
||||
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, array($validator, 'validate'));
|
||||
}
|
||||
|
||||
public function buildView(FormViewInterface $view, FormInterface $form, array $options)
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$fingerprint = null;
|
||||
|
||||
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']);
|
||||
$sessionKey = sprintf('%s%s', self::SESSION_KEY_PREFIX, $options['session_key']);
|
||||
$isHuman = false;
|
||||
|
||||
if ($options['as_file']) {
|
||||
$captchaCode = $generator->getFile($options['width'], $options['height']);
|
||||
} else {
|
||||
$captchaCode = $generator->getCode($options['width'], $options['height']);
|
||||
if ($options['humanity'] > 0) {
|
||||
$humanityKey = sprintf('%s_humanity', $sessionKey);
|
||||
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']) {
|
||||
$keys = $this->session->get($options['whitelist_key'], array());
|
||||
if (!in_array($sessionKey, $keys)) {
|
||||
$keys[] = $sessionKey;
|
||||
}
|
||||
$this->session->set($options['whitelist_key'], $keys);
|
||||
$options['session_key'] = $sessionKey;
|
||||
}
|
||||
|
||||
$view->addVars(array(
|
||||
'captcha_width' => $options['width'],
|
||||
'captcha_height' => $options['height'],
|
||||
'captcha_code' => $captchaCode,
|
||||
'value' => '',
|
||||
$view->vars = array_merge($view->vars, array(
|
||||
'captcha_width' => $options['width'],
|
||||
'captcha_height' => $options['height'],
|
||||
'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', 'background_color', 'background_images', 'text_color', ) as $key) {
|
||||
$persistOptions[$key] = $options[$key];
|
||||
}
|
||||
|
||||
$this->session->set($sessionKey, $persistOptions);
|
||||
}
|
||||
|
||||
public function setDefaultOptions(OptionsResolverInterface $resolver)
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$this->options['property_path'] = false;
|
||||
$this->options['mapped'] = false;
|
||||
$this->options['request'] = null;
|
||||
$resolver->setDefaults($this->options);
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
public function getParent(): string
|
||||
{
|
||||
return 'text';
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->getBlockPrefix();
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,68 +1,167 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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\Contracts\Translation\TranslatorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Captcha validator
|
||||
* Captcha validator.
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
*/
|
||||
class CaptchaValidator implements FormValidatorInterface
|
||||
class CaptchaValidator
|
||||
{
|
||||
/**
|
||||
* Session
|
||||
*/
|
||||
/** @var SessionInterface */
|
||||
private $session;
|
||||
|
||||
/**
|
||||
* Session key to store the code
|
||||
* Session key to store the code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $key;
|
||||
|
||||
public function __construct(Session $session, $key)
|
||||
{
|
||||
/**
|
||||
* Error message text for non-matching submissions.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $invalidMessage;
|
||||
|
||||
/**
|
||||
* Configuration parameter used to bypass a required code match.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $bypassCode;
|
||||
|
||||
/**
|
||||
* Number of form that the user can submit without captcha.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $humanity;
|
||||
|
||||
/**
|
||||
* Translator.
|
||||
*
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
private $translator;
|
||||
|
||||
/**
|
||||
* Request
|
||||
*
|
||||
* @var Request
|
||||
*/
|
||||
private $req;
|
||||
|
||||
public function __construct(
|
||||
TranslatorInterface $translator,
|
||||
SessionInterface $session,
|
||||
string $key,
|
||||
string $invalidMessage,
|
||||
?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(FormInterface $form)
|
||||
public function validate(FormEvent $event): void
|
||||
{
|
||||
$code = $form->getData();
|
||||
$excepted_code = $this->getExceptedCode();
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
$this->session->remove($this->key);
|
||||
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')));
|
||||
} else {
|
||||
if ($this->humanity > 0) {
|
||||
$this->updateHumanity($this->humanity);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->session->has($this->key.'_fingerprint')) {
|
||||
$this->session->remove($this->key.'_fingerprint');
|
||||
if (null == $this->req || 1 < $this->req->get('flow_registration_step')) {
|
||||
$this->session->remove($this->key);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the codes
|
||||
* Retrieve the humanity.
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
private function niceize($code)
|
||||
protected function getHumanity()
|
||||
{
|
||||
return $this->session->get($this->key.'_humanity', 0);
|
||||
}
|
||||
|
||||
protected function updateHumanity(int $newValue): void
|
||||
{
|
||||
if ($newValue > 0) {
|
||||
$this->session->set($this->key.'_humanity', $newValue);
|
||||
} else {
|
||||
$this->session->remove($this->key.'_humanity');
|
||||
}
|
||||
}
|
||||
|
||||
protected function niceize(string $code): string
|
||||
{
|
||||
return strtr(strtolower($code), 'oil', '01l');
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a match comparison on the provided code and the expected code.
|
||||
*
|
||||
* @param string $code
|
||||
* @param string|null $expectedCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function compare($code, $expectedCode): bool
|
||||
{
|
||||
return null !== $expectedCode && is_string($expectedCode) && $this->niceize($code) == $this->niceize($expectedCode);
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,39 @@
|
||||
{
|
||||
"name": "gregwar/captcha-bundle",
|
||||
"type": "captcha-bundle",
|
||||
"name": "cadoles/captcha",
|
||||
"type": "symfony-bundle",
|
||||
"description": "Captcha bundle",
|
||||
"keywords": ["symfony2", "captcha", "bot", "visual", "code", "security", "spam"],
|
||||
"homepage": "https://github.com/Gregwar/ImageBundle",
|
||||
"keywords": ["symfony2", "symfony", "captcha", "bot", "visual", "code", "security", "spam"],
|
||||
"homepage": "https://github.com/Cadoles/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": ">=7.1.3",
|
||||
"ext-gd": "*",
|
||||
"gregwar/captcha": "^1.1.9",
|
||||
"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": {
|
||||
"psr-0": {
|
||||
"Gregwar\\CaptchaBundle": ""
|
||||
"psr-4": {
|
||||
"Gregwar\\CaptchaBundle\\": "/"
|
||||
}
|
||||
},
|
||||
"target-dir": "Gregwar/CaptchaBundle"
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"require-dev": {
|
||||
"symplify/easy-coding-standard": "^6.1"
|
||||
}
|
||||
}
|
||||
|
138
ecs.yaml
Normal file
138
ecs.yaml
Normal 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: ~
|
Reference in New Issue
Block a user