Skip to content

Commit ac6530b

Browse files
committed
✨ add SymfonyClientRequestMethod
1 parent cac0328 commit ac6530b

6 files changed

Lines changed: 110 additions & 4 deletions

File tree

config/services.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
<argument>%beelab_recaptcha2.secret%</argument>
1111
<argument type="service" id="beelab_recaptcha2.google_recaptcha.request_method" />
1212
</service>
13+
<service id="Beelab\Recaptcha2Bundle\Recaptcha\SymfonyClientRequestMethod" public="false">
14+
<call method="setClient">
15+
<argument type="service" id="Symfony\Contracts\HttpClient\HttpClientInterface" on-invalid="ignore"/>
16+
</call>
17+
</service>
1318
<service id="beelab_recaptcha2.type" class="Beelab\Recaptcha2Bundle\Form\Type\RecaptchaType" public="true">
1419
<argument>%beelab_recaptcha2.site_key%</argument>
1520
<tag name="form.type" alias="beelab_recaptcha2" />

docs/index.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,22 @@ beelab_recaptcha2:
4040
If your PHP environment has restrictions about `file_get_contents()` making HTTP requests,
4141
you can use another `RequestMethod` from Google's Recaptcha library.
4242

43-
Currently, this bundle supports the default `Post` and `CurlPost` methods.
44-
You can use the latter by adding in your `config.yml`:
43+
Currently, this bundle supports the default `Post` and `CurlPost` methods, and an additional
44+
method leveraging the [Symfony HTTP Client][1].
45+
You can define it by adding in your configuration:
4546

4647
```yaml
4748
# config/packages/beelab_recaptcha2.yaml
4849
4950
beelab_recaptcha2:
50-
request_method: curl_post
51+
request_method: curl_post # or http_client
5152
```
5253

5354
Otherwise, the default value `post` will be used.
5455

56+
If you want to use the `http_client` request method, you need to require `symfony/http-client`.
57+
58+
5559
## 3. Usage
5660

5761
In your form, use `Beelab\Recaptcha2Bundle\Form\Type\RecaptchaType` form type, as any other Symfony form type.
@@ -125,3 +129,4 @@ You can add to your `_form_theme.html.twig` file the following lines:
125129
{%- endblock %}
126130
```
127131

132+
[1]: https://symfony.com/doc/current/http_client.html

src/DependencyInjection/BeelabRecaptcha2Extension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Beelab\Recaptcha2Bundle\DependencyInjection;
44

5+
use Beelab\Recaptcha2Bundle\Recaptcha\SymfonyClientRequestMethod;
56
use Symfony\Component\Config\FileLocator;
67
use Symfony\Component\DependencyInjection\ContainerBuilder;
78
use Symfony\Component\DependencyInjection\Extension\Extension;
@@ -30,6 +31,7 @@ private function getRequestMethod(string $requestMethod): string
3031
{
3132
return match ($requestMethod) {
3233
'curl_post' => CurlPost::class,
34+
'http_client' => SymfonyClientRequestMethod::class,
3335
default => Post::class,
3436
};
3537
}

src/DependencyInjection/Configuration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public function getConfigTreeBuilder(): TreeBuilder
1414
$rootNode
1515
->children()
1616
->enumNode('request_method')
17-
->values(['curl_post', 'post'])
17+
->values(['curl_post', 'post', 'http_client'])
1818
->defaultValue('post')
1919
->end()
2020
->scalarNode('site_key')
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Beelab\Recaptcha2Bundle\Recaptcha;
4+
5+
use ReCaptcha\ReCaptcha;
6+
use ReCaptcha\RequestMethod;
7+
use ReCaptcha\RequestParameters;
8+
use Symfony\Contracts\HttpClient\HttpClientInterface;
9+
10+
final class SymfonyClientRequestMethod implements RequestMethod
11+
{
12+
private ?HttpClientInterface $client = null;
13+
14+
public function __construct(
15+
private string $siteVerifyUrl = ReCaptcha::SITE_VERIFY_URL,
16+
) {
17+
}
18+
19+
public function setClient(?HttpClientInterface $client): void
20+
{
21+
$this->client = $client;
22+
}
23+
24+
public function submit(RequestParameters $params): string
25+
{
26+
if (null === $this->client) {
27+
throw new \UnexpectedValueException('Needed service is not injected.');
28+
}
29+
30+
$response = $this->client->request('POST', $this->siteVerifyUrl, [
31+
'body' => $params->toQueryString(),
32+
'headers' => [
33+
'Content-Type' => 'application/x-www-form-urlencoded',
34+
],
35+
]);
36+
37+
if (200 !== $response->getStatusCode()) {
38+
return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}';
39+
}
40+
41+
return $response->getContent();
42+
}
43+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Beelab\Recaptcha2Bundle\Tests\Recaptcha;
4+
5+
use Beelab\Recaptcha2Bundle\Recaptcha\SymfonyClientRequestMethod;
6+
use PHPUnit\Framework\TestCase;
7+
use ReCaptcha\RequestParameters;
8+
use Symfony\Contracts\HttpClient\HttpClientInterface;
9+
use Symfony\Contracts\HttpClient\ResponseInterface;
10+
11+
final class SymfonyClientRequestMethodTest extends TestCase
12+
{
13+
/** @var \PHPUnit\Framework\MockObject\MockObject|HttpClientInterface */
14+
protected $client;
15+
16+
protected function setUp(): void
17+
{
18+
$this->client = $this->createMock(HttpClientInterface::class);
19+
}
20+
21+
public function testServiceNotInjected(): void
22+
{
23+
$method = new SymfonyClientRequestMethod();
24+
$this->expectException(\UnexpectedValueException::class);
25+
26+
$method->submit(new RequestParameters('', ''));
27+
}
28+
29+
public function testRequestFailure(): void
30+
{
31+
$method = new SymfonyClientRequestMethod();
32+
$method->setClient($this->client);
33+
$response = $this->createMock(ResponseInterface::class);
34+
$response->expects($this->once())->method('getStatusCode')->willReturn(404);
35+
$this->client->expects($this->once())->method('request')->willReturn($response);
36+
$content = $method->submit(new RequestParameters('', ''));
37+
self::assertEquals('{"success": false, "error-codes": ["invalid-input-response"]}', $content);
38+
}
39+
40+
public function testRequestSuccess(): void
41+
{
42+
$method = new SymfonyClientRequestMethod();
43+
$method->setClient($this->client);
44+
$response = $this->createMock(ResponseInterface::class);
45+
$response->expects($this->once())->method('getStatusCode')->willReturn(200);
46+
$response->expects($this->once())->method('getResponse')->willReturn('"OK"');
47+
$this->client->expects($this->once())->method('request')->willReturn($response);
48+
$content = $method->submit(new RequestParameters('', ''));
49+
self::assertEquals('"OK"', $content);
50+
}
51+
}

0 commit comments

Comments
 (0)