Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
GoogleRecaptcha
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
5 / 5
10
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkCaptchaCode
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 fetchUrl
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 isUserIsLoggedIn
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setUserIsLoggedIn
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * The phpMyFAQ Google ReCAPTCHA v3 class.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2023-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2023-02-11
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Captcha;
21
22use phpMyFAQ\Configuration;
23
24class GoogleRecaptcha implements CaptchaInterface
25{
26    private bool $userIsLoggedIn;
27
28    /**
29     * Constructor.
30     */
31    public function __construct(
32        private readonly Configuration $configuration,
33    ) {
34    }
35
36    public function checkCaptchaCode(string $code): bool
37    {
38        if ($this->isUserIsLoggedIn()) {
39            return true;
40        }
41
42        $url = sprintf(
43            'https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s',
44            (string) $this->configuration->get(item: 'security.googleReCaptchaV2SecretKey'),
45            $code,
46        );
47
48        $response = $this->fetchUrl($url);
49
50        if (!is_string($response) || $response === '') {
51            return false;
52        }
53
54        try {
55            $decoded = json_decode($response, associative: true, depth: 512, flags: JSON_THROW_ON_ERROR);
56        } catch (\JsonException) {
57            return false;
58        }
59
60        return is_array($decoded) && ($decoded['success'] ?? false) === true;
61    }
62
63    /**
64     * Fetch the contents of a URL.
65     */
66    protected function fetchUrl(string $url): string|false
67    {
68        $response = false;
69        set_error_handler(static fn() => true);
70        try {
71            $response = file_get_contents($url);
72        } finally {
73            restore_error_handler();
74        }
75
76        return $response;
77    }
78
79    public function isUserIsLoggedIn(): bool
80    {
81        return $this->userIsLoggedIn;
82    }
83
84    public function setUserIsLoggedIn(bool $userIsLoggedIn): GoogleRecaptcha
85    {
86        $this->userIsLoggedIn = $userIsLoggedIn;
87        return $this;
88    }
89}