Skip to content
This repository was archived by the owner on Feb 25, 2026. It is now read-only.

Commit 949dde3

Browse files
committed
Initial commit: v3 working
1 parent 6b5deb6 commit 949dde3

10 files changed

Lines changed: 373 additions & 19 deletions

File tree

src/DependencyInjection/Configuration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function getConfigTreeBuilder()
3838

3939
->integerNode('version')->min(2)->max(3)->defaultValue(2)->end()
4040
->booleanNode('hide_badge')->defaultValue(false)->end()
41-
->floatNode('score_threshhold')->defaultValue(0.5)->end()
41+
->floatNode('score_threshhold')->min(0.0)->max(1.0)->defaultValue(0.5)->end()
4242

4343
->integerNode('timeout')->min(0)->defaultNull()->end()
4444
->arrayNode('trusted_roles')->prototype('scalar')->treatNullLike(array())->end()

src/DependencyInjection/EWZRecaptchaExtension.php

100644100755
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace EWZ\Bundle\RecaptchaBundle\DependencyInjection;
44

5+
use EWZ\Bundle\RecaptchaBundle\DependencyInjection\CompilerPass\WidgetCompilerPass;
6+
use EWZ\Bundle\RecaptchaBundle\Resolver\WidgetResolver;
57
use Symfony\Component\Config\FileLocator;
68
use Symfony\Component\DependencyInjection\ContainerBuilder;
79
use Symfony\Component\DependencyInjection\Loader;
@@ -13,6 +15,7 @@
1315
*/
1416
class EWZRecaptchaExtension extends Extension
1517
{
18+
1619
/**
1720
* {@inheritdoc}
1821
*/
@@ -28,20 +31,21 @@ public function load(array $configs, ContainerBuilder $container)
2831
$container->setParameter('ewz_recaptcha.'.$key, $value);
2932
}
3033

31-
$this->registerWidget($container);
34+
$this->registerWidget($container, $config['version']);
3235

3336
if (null !== $config['http_proxy']['host'] && null !== $config['http_proxy']['port']) {
3437
$recaptchaService = $container->findDefinition('ewz_recaptcha.recaptcha');
3538
$recaptchaService->replaceArgument(1, new Reference('ewz_recaptcha.extension.recaptcha.request_method.proxy_post'));
3639
}
40+
3741
}
3842

3943
/**
4044
* Registers the form widget.
4145
*
4246
* @param ContainerBuilder $container
4347
*/
44-
protected function registerWidget(ContainerBuilder $container)
48+
protected function registerWidget(ContainerBuilder $container, int $version)
4549
{
4650
$templatingEngines = $container->hasParameter('templating.engines')
4751
? $container->getParameter('templating.engines')
@@ -58,6 +62,9 @@ protected function registerWidget(ContainerBuilder $container)
5862

5963
if (in_array('twig', $templatingEngines)) {
6064
$formResource = '@EWZRecaptcha/Form/ewz_recaptcha_widget.html.twig';
65+
if (3 === $version) {
66+
$formResource = '@EWZRecaptcha/Form/v3/ewz_recaptcha_widget.html.twig';
67+
}
6168

6269
$container->setParameter('twig.form.resources', array_merge(
6370
$this->getTwigFormResources($container),

src/EWZRecaptchaBundle.php

100644100755
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,19 @@
22

33
namespace EWZ\Bundle\RecaptchaBundle;
44

5+
use EWZ\Bundle\RecaptchaBundle\DependencyInjection\CompilerPass\WidgetCompilerPass;
6+
use EWZ\Bundle\RecaptchaBundle\Resolver\WidgetResolver;
7+
use Symfony\Component\DependencyInjection\ContainerBuilder;
58
use Symfony\Component\HttpKernel\Bundle\Bundle;
69

710
class EWZRecaptchaBundle extends Bundle
811
{
12+
/**
13+
* @param ContainerBuilder $container
14+
*/
15+
public function build(ContainerBuilder $container)
16+
{
17+
parent::build($container);
18+
$container->addCompilerPass(new WidgetCompilerPass());
19+
}
920
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace EWZ\Bundle\RecaptchaBundle\Form\Type;
4+
5+
use EWZ\Bundle\RecaptchaBundle\Validator\Constraints\IsTrueV3;
6+
use Symfony\Component\Form\AbstractType;
7+
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
8+
use Symfony\Component\Form\FormInterface;
9+
use Symfony\Component\Form\FormView;
10+
use Symfony\Component\OptionsResolver\OptionsResolver;
11+
12+
class EWZRecaptchaV3Type extends AbstractType
13+
{
14+
/**
15+
* @var string
16+
*/
17+
private $publicKey;
18+
19+
/**
20+
* @var bool
21+
*/
22+
private $hideBadge;
23+
24+
/**
25+
* @var string
26+
*/
27+
private $apiHost;
28+
29+
/**
30+
* RecaptchaType constructor.
31+
*
32+
* @param string $publicKey
33+
* @param bool $hideBadge
34+
* @param string $apiHost
35+
*/
36+
public function __construct(string $publicKey, bool $hideBadge, string $apiHost = 'www.google.com')
37+
{
38+
$this->publicKey = $publicKey;
39+
$this->hideBadge = $hideBadge;
40+
$this->apiHost = $apiHost;
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function buildView(FormView $view, FormInterface $form, array $options)
47+
{
48+
$view->vars = array_replace($view->vars, [
49+
'ewz_recaptcha_public_key' => $this->publicKey,
50+
'ewz_recaptcha_hide_badge' => $this->hideBadge,
51+
'ewz_recaptcha_apihost' => $this->apiHost,
52+
'script_nonce_csp' => $options['script_nonce_csp'] ?? '',
53+
'action_name' => $options['action_name'] ?? '',
54+
]);
55+
}
56+
57+
/**
58+
* {@inheritdoc}
59+
*/
60+
public function configureOptions(OptionsResolver $resolver)
61+
{
62+
$resolver->setDefaults([
63+
'label' => false,
64+
'mapped' => false,
65+
'constraints' => [
66+
new IsTrueV3()
67+
],
68+
'validation_groups' => [ 'Default' ],
69+
'script_nonce_csp' => '',
70+
'action_name' => 'form',
71+
]);
72+
73+
$resolver->setAllowedTypes('script_nonce_csp', 'string');
74+
$resolver->setAllowedTypes('action_name', 'string');
75+
}
76+
77+
/**
78+
* {@inheritdoc}
79+
*/
80+
public function getParent(): string
81+
{
82+
return HiddenType::class;
83+
}
84+
85+
/**
86+
* {@inheritdoc}
87+
*/
88+
public function getBlockPrefix()
89+
{
90+
return 'ewz_recaptcha';
91+
}
92+
93+
}

src/Interfaces/WidgetInterface.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace EWZ\Bundle\RecaptchaBundle\Interfaces;
4+
5+
use Symfony\Component\DependencyInjection\ContainerBuilder;
6+
7+
interface WidgetInterface
8+
{
9+
public function register(ContainerBuilder $container): void;
10+
public function supports(int $version): bool;
11+
}

src/Resolver/WidgetResolver.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace EWZ\Bundle\RecaptchaBundle\Resolver;
4+
5+
use EWZ\Bundle\RecaptchaBundle\Interfaces\WidgetInterface;
6+
7+
class WidgetResolver
8+
{
9+
/**
10+
* List of all available widgets
11+
* @var WidgetInterface[]
12+
*/
13+
private $widgets = array();
14+
15+
/**
16+
* Add a widget to the list
17+
* @param WidgetInterface $widget
18+
*/
19+
public function addWidget(WidgetInterface $widget): void
20+
{
21+
$this->widgets[] = $widget;
22+
}
23+
24+
/**
25+
*
26+
* @param int $version
27+
* @return WidgetInterface
28+
*/
29+
public function getWidget(int $version): WidgetInterface
30+
{
31+
foreach ($this->widgets as $widget) {
32+
if ($widget->supports($version)) {
33+
return $widget;
34+
}
35+
}
36+
}
37+
38+
}

src/Resources/config/services.yml

100644100755
Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,64 @@ services:
3333
- { name: validator.constraint_validator, alias: 'ewz_recaptcha.true' }
3434

3535
ewz_recaptcha.recaptcha:
36-
class: ReCaptcha\ReCaptcha
37-
public: false
38-
arguments:
39-
- '%ewz_recaptcha.private_key%'
40-
- '@ewz_recaptcha.extension.recaptcha.request_method.post'
36+
class: ReCaptcha\ReCaptcha
37+
public: false
38+
arguments:
39+
- '%ewz_recaptcha.private_key%'
40+
- '@ewz_recaptcha.extension.recaptcha.request_method.post'
4141

4242
ewz_recaptcha.extension.recaptcha.request_method.post:
43-
class: EWZ\Bundle\RecaptchaBundle\Extension\ReCaptcha\RequestMethod\Post
44-
public: false
45-
arguments:
46-
- 'https://%ewz_recaptcha.api_host%'
47-
- '%ewz_recaptcha.timeout%'
43+
class: EWZ\Bundle\RecaptchaBundle\Extension\ReCaptcha\RequestMethod\Post
44+
public: false
45+
arguments:
46+
- 'https://%ewz_recaptcha.api_host%'
47+
- '%ewz_recaptcha.timeout%'
4848

4949
ewz_recaptcha.extension.recaptcha.request_method.proxy_post:
50-
class: EWZ\Bundle\RecaptchaBundle\Extension\ReCaptcha\RequestMethod\ProxyPost
51-
public: false
52-
arguments:
53-
- '%ewz_recaptcha.http_proxy%'
54-
- 'https://%ewz_recaptcha.api_host%'
55-
- '%ewz_recaptcha.timeout%'
50+
class: EWZ\Bundle\RecaptchaBundle\Extension\ReCaptcha\RequestMethod\ProxyPost
51+
public: false
52+
arguments:
53+
- '%ewz_recaptcha.http_proxy%'
54+
- 'https://%ewz_recaptcha.api_host%'
55+
- '%ewz_recaptcha.timeout%'
56+
57+
ewz_recaptcha.v3.form.type:
58+
class: EWZ\Bundle\RecaptchaBundle\Form\Type\EWZRecaptchaV3Type
59+
public: true
60+
arguments:
61+
- '%ewz_recaptcha.public_key%'
62+
- '%ewz_recaptcha.hide_badge%'
63+
- '%ewz_recaptcha.api_host%'
64+
tags:
65+
- { name: form.type }
66+
67+
ewz_recaptcha.validator.v3.true:
68+
class: EWZ\Bundle\RecaptchaBundle\Validator\Constraints\IsTrueValidatorV3
69+
public: true
70+
arguments:
71+
- '%ewz_recaptcha.enabled%'
72+
- '%ewz_recaptcha.private_key%'
73+
- '%ewz_recaptcha.score_threshhold%'
74+
- '@request_stack'
75+
- '@logger'
76+
tags:
77+
- { name: validator.constraint_validator, alias: 'ewz_recaptcha.v3.true' }
78+
79+
80+
ewz_recaptcha.widget_resolver:
81+
class: EWZ\Bundle\RecaptchaBundle\Resolver\WidgetResolver
82+
public: false
83+
tags:
84+
- { name: 'ewz_recaptcha.widget_resolver', alias: EWZ\Bundle\RecaptchaBundle\Resolver\WidgetResolver }
85+
86+
ewz_recaptcha.widget.v2:
87+
class: EWZ\Bundle\RecaptchaBundle\Widget\WidgetV2
88+
public: false
89+
tags:
90+
- { name: widget.form }
91+
92+
ewz_recaptcha.widget.v3:
93+
class: EWZ\Bundle\RecaptchaBundle\Widget\WidgetV3
94+
public: false
95+
tags:
96+
- { name: widget.form }
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{% block ewz_recaptcha_widget %}
2+
{% apply spaceless %}
3+
<script src="https://{{ form.vars.ewz_recaptcha_apihost }}/recaptcha/api.js?render={{ form.vars.ewz_recaptcha_public_key }}"></script>
4+
5+
{% if form.vars.ewz_recaptcha_hide_badge %}
6+
<link rel="stylesheet" href="{{ asset('/bundles/ewz_recaptcha/css/recaptcha.css') }}">
7+
{% endif %}
8+
9+
<script {% if form.vars.script_nonce_csp is defined %}nonce="{{ form.vars.script_nonce_csp }}"{% endif %}>
10+
grecaptcha.ready(function () {
11+
grecaptcha.execute('{{ form.vars.ewz_recaptcha_public_key }}', { action: '{{ form.vars.action_name|default('form') }}' }).then(function (token) {
12+
var recaptchaResponse = document.getElementById('{{ id }}');
13+
recaptchaResponse.value = token;
14+
});
15+
});
16+
</script>
17+
18+
{{ form_label(form) }}
19+
{{ form_widget(form) }}
20+
{% endapply %}
21+
{% endblock %}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace EWZ\Bundle\RecaptchaBundle\Validator\Constraints;
4+
5+
class IsTrueV3 extends IsTrue
6+
{
7+
/**
8+
* {@inheritdoc}
9+
*/
10+
public function validatedBy()
11+
{
12+
return 'ewz_recaptcha.v3.true';
13+
}
14+
}

0 commit comments

Comments
 (0)