VerifyReCaptcha::verify()   A
last analyzed

Complexity

Conditions 5
Paths 8

Size

Total Lines 33
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 22
c 1
b 0
f 0
nc 8
nop 2
dl 0
loc 33
rs 9.2568
1
<?php
2
/**
3
 * Copyright (c) 2019. Volodymyr Hryvinskyi.  All rights reserved.
4
 * @author: <mailto:[email protected]>
5
 * @github: <https://github.com/hryvinskyi>
6
 */
7
8
declare(strict_types=1);
9
10
namespace Hryvinskyi\InvisibleCaptcha\Model\ReCaptcha;
11
12
use Hryvinskyi\InvisibleCaptcha\Model\ReCaptcha\Validators\ValidatorInterface;
13
use Hryvinskyi\InvisibleCaptcha\Model\ReCaptcha\Validators\ValidatorList;
14
15
/**
16
 * Class VerifyVerifyReCaptcha
17
 */
18
class VerifyReCaptcha
19
{
20
    /**
21
     * URL for reCAPTCHA site verify API
22
     *
23
     * @const string
24
     */
25
    const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
26
27
    /**
28
     * Invalid JSON received
29
     *
30
     * @const string
31
     */
32
    const E_INVALID_JSON = 'invalid-json';
33
34
    /**
35
     * Not a success, but no error codes received!
36
     *
37
     * @const string
38
     */
39
    const E_UNKNOWN_ERROR = 'unknown-error';
40
41
    /**
42
     * Bad validator
43
     *
44
     * @const string
45
     */
46
    const BAD_VALIDATOR = 'bad-validator';
47
48
    /**
49
     * @var string
50
     */
51
    private $hostname;
52
53
    /**
54
     * @var string
55
     */
56
    private $action;
57
58
    /**
59
     * @var float
60
     */
61
    private $threshold;
62
63
    /**
64
     * @var int
65
     */
66
    private $timeoutSeconds;
67
68
    /**
69
     * Shared secret for the site.
70
     * @var string
71
     */
72
    private $secret;
73
74
    /**
75
     * Method used to communicate with service. Defaults to POST request.
76
     *
77
     * @var RequestMethodInterface
78
     */
79
    private $requestMethod;
80
81
    /**
82
     * @var RequestParameters
83
     */
84
    private $requestParameters;
85
86
    /**
87
     * @var ValidatorList
88
     */
89
    private $verifyValidatorList = [];
90
91
    /**
92
     * VerifyReCaptcha constructor.
93
     *
94
     * @param RequestMethodInterface $requestMethod
95
     * @param RequestParameters $requestParameters
96
     * @param ValidatorList $verifyValidatorList
97
     */
98
    public function __construct(
99
        RequestMethodInterface $requestMethod,
100
        RequestParameters $requestParameters,
101
        ValidatorList $verifyValidatorList
102
    ) {
103
        $this->requestMethod = $requestMethod;
104
        $this->requestParameters = $requestParameters;
105
        $this->verifyValidatorList = $verifyValidatorList;
106
    }
107
108
    /**
109
     * Calls the reCAPTCHA siteverify API to verify whether the user passes
110
     * CAPTCHA test and additionally runs any specified additional checks
111
     *
112
     * @param string $response The user response token provided by reCAPTCHA, verifying the user on your site.
113
     * @param string $remoteIp The end user's IP address.
114
     *
115
     * @return Response
116
     */
117
    public function verify($response, $remoteIp = null): Response
118
    {
119
        $params = $this->requestParameters
120
            ->setSecret($this->getSecret())
121
            ->setResponse((string)$response)
122
            ->setRemoteIp($remoteIp);
123
124
        $answer = $this->requestMethod->submit(self::SITE_VERIFY_URL, $params);
125
        $initialResponse = Response::fromJson($answer);
126
        $validationErrors = [];
127
128
        foreach ($this->verifyValidatorList as $item) {
129
            if (!$item instanceof ValidatorInterface) {
130
                $validationErrors[] = self::BAD_VALIDATOR;
131
                continue;
132
            }
133
134
            if ($error = $item->validate($this, $initialResponse)) {
135
                $validationErrors[] = $error;
136
            }
137
        }
138
139
        if (empty($validationErrors)) {
140
            return $initialResponse;
141
        }
142
143
        return new Response(
144
            false,
145
            array_merge($initialResponse->getErrorCodes(), $validationErrors),
146
            $initialResponse->getHostname(),
147
            $initialResponse->getChallengeTs(),
148
            $initialResponse->getScore(),
149
            $initialResponse->getAction()
150
        );
151
    }
152
153
    /**
154
     * @return string
155
     */
156
    public function getSecret(): string
157
    {
158
        return $this->secret;
159
    }
160
161
    /**
162
     * @param string $secret
163
     *
164
     * @return VerifyReCaptcha
165
     */
166
    public function setSecret(string $secret): VerifyReCaptcha
167
    {
168
        $this->secret = $secret;
169
170
        return $this;
171
    }
172
173
    /**
174
     * Provide a hostname to match against in verify()
175
     * This should be without a protocol or trailing slash, e.g. www.google.com
176
     *
177
     * @param string $hostname Expected hostname
178
     *
179
     * @return VerifyReCaptcha
180
     */
181
    public function setExpectedHostname($hostname): VerifyReCaptcha
182
    {
183
        $this->hostname = $hostname;
184
185
        return $this;
186
    }
187
188
    /**
189
     * @return string
190
     */
191
    public function getExpectedHostname(): ?string
192
    {
193
        return $this->hostname;
194
    }
195
196
    /**
197
     * Provide an action to match against in verify()
198
     * This should be set per page.
199
     *
200
     * @param string $action Expected action
201
     *
202
     * @return VerifyReCaptcha
203
     */
204
    public function setExpectedAction($action): VerifyReCaptcha
205
    {
206
        $this->action = $action;
207
208
        return $this;
209
    }
210
211
    /**
212
     * @return string
213
     */
214
    public function getExpectedAction(): ?string
215
    {
216
        return $this->action;
217
    }
218
219
    /**
220
     * Provide a threshold to meet or exceed in verify()
221
     * Threshold should be a float between 0 and 1 which will be tested as response >= threshold.
222
     *
223
     * @param float $threshold Expected threshold
224
     *
225
     * @return VerifyReCaptcha
226
     */
227
    public function setScoreThreshold($threshold): VerifyReCaptcha
228
    {
229
        $this->threshold = floatval($threshold);
230
231
        return $this;
232
    }
233
234
    /**
235
     * @return float|null
236
     */
237
    public function getScoreThreshold(): ?float
238
    {
239
        return $this->threshold;
240
    }
241
242
    /**
243
     * Provide a timeout in seconds to test against the challenge timestamp in verify()
244
     *
245
     * @param int $timeoutSeconds Expected hostname
246
     *
247
     * @return VerifyReCaptcha
248
     */
249
    public function setChallengeTimeout(int $timeoutSeconds): VerifyReCaptcha
250
    {
251
        $this->timeoutSeconds = $timeoutSeconds;
252
253
        return $this;
254
    }
255
256
    /**
257
     * @return int
258
     */
259
    public function getChallengeTimeout(): ?int
260
    {
261
        return $this->timeoutSeconds;
262
    }
263
}
264