Passed
Branch master (47c941)
by Roberto
05:04
created

ReCaptchaBuilder::validate()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 55
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 8.4551

Importance

Changes 7
Bugs 0 Features 1
Metric Value
cc 8
eloc 32
c 7
b 0
f 1
nc 10
nop 1
dl 0
loc 55
rs 8.1635
ccs 21
cts 26
cp 0.8077
crap 8.4551

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Copyright (c) 2017 - present
5
 * LaravelGoogleRecaptcha - ReCaptchaBuilder.php
6
 * author: Roberto Belotti - [email protected]
7
 * web : robertobelotti.com, github.com/biscolab
8
 * Initial version created on: 12/9/2018
9
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
10
 */
11
12
namespace Biscolab\ReCaptcha;
13
14
use Illuminate\Support\Arr;
15
16
/**
17
 * Class ReCaptchaBuilder
18
 * @package Biscolab\ReCaptcha
19
 */
20
class ReCaptchaBuilder
21
{
22
23
    /**
24
     * @var string
25
     */
26
    const DEFAULT_API_VERSION = 'v2';
27
28
    /**
29
     * @var int
30
     */
31
    const DEFAULT_CURL_TIMEOUT = 10;
32
33
    /**
34
     * @var string
35
     */
36
    const DEFAULT_ONLOAD_JS_FUNCTION = 'biscolabOnloadCallback';
37
38
    /**
39
     * @var string
40
     */
41
    const DEFAULT_RECAPTCHA_RULE_NAME = 'recaptcha';
42
43
    /**
44
     * @var string
45
     */
46
    const DEFAULT_RECAPTCHA_FIELD_NAME = 'g-recaptcha-response';
47
48
    /**
49
     * @var string
50
     */
51
    const DEFAULT_RECAPTCHA_API_DOMAIN = 'www.google.com';
52
53
    /**
54
     * The Site key
55
     * please visit https://developers.google.com/recaptcha/docs/start
56
     * @var string
57
     */
58
    protected $api_site_key;
59
60
    /**
61
     * The Secret key
62
     * please visit https://developers.google.com/recaptcha/docs/start
63
     * @var string
64
     */
65
    protected $api_secret_key;
66
67
    /**
68
     * The chosen ReCAPTCHA version
69
     * please visit https://developers.google.com/recaptcha/docs/start
70
     * @var string
71
     */
72
    protected $version;
73
74
    /**
75
     * Whether is true the ReCAPTCHA is inactive
76
     * @var boolean
77
     */
78
    protected $skip_by_ip = false;
79
80
    /**
81
     * The API domain (default: retrieved from config file)
82
     * @var string
83
     */
84
    protected $api_domain = '';
85
86
    /**
87
     * The API request URI
88
     * @var string
89
     */
90
    protected $api_url = '';
91
92
    /**
93
     * The URI of the API Javascript file to embed in you pages
94
     * @var string
95
     */
96
    protected $api_js_url = '';
97
98
    /**
99
     * ReCaptchaBuilder constructor.
100
     *
101
     * @param string      $api_site_key
102
     * @param string      $api_secret_key
103
     * @param null|string $version
104
     */
105 46
    public function __construct(
106
        string $api_site_key,
107
        string $api_secret_key,
108
        ?string $version = self::DEFAULT_API_VERSION
109
    ) {
110
111 46
        $this->setApiSiteKey($api_site_key);
112 46
        $this->setApiSecretKey($api_secret_key);
113 46
        $this->setVersion($version);
0 ignored issues
show
Bug introduced by
It seems like $version can also be of type null; however, parameter $version of Biscolab\ReCaptcha\ReCaptchaBuilder::setVersion() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

113
        $this->setVersion(/** @scrutinizer ignore-type */ $version);
Loading history...
114 46
        $this->setSkipByIp($this->skipByIp());
115 46
        $this->setApiDomain();
116 46
        $this->setApiUrls();
117 46
    }
118
119
    /**
120
     * @param string $api_site_key
121
     *
122
     * @return ReCaptchaBuilder
123
     */
124 46
    public function setApiSiteKey(string $api_site_key): ReCaptchaBuilder
125
    {
126
127 46
        $this->api_site_key = $api_site_key;
128
129 46
        return $this;
130
    }
131
132
    /**
133
     * @param string $api_secret_key
134
     *
135
     * @return ReCaptchaBuilder
136
     */
137 46
    public function setApiSecretKey(string $api_secret_key): ReCaptchaBuilder
138
    {
139
140 46
        $this->api_secret_key = $api_secret_key;
141
142 46
        return $this;
143
    }
144
145
    /**
146
     * @return int
147
     */
148 4
    public function getCurlTimeout(): int
149
    {
150
151 4
        return config('recaptcha.curl_timeout', self::DEFAULT_CURL_TIMEOUT);
152
    }
153
154
    /**
155
     * @param string $version
156
     *
157
     * @return ReCaptchaBuilder
158
     */
159 46
    public function setVersion(string $version): ReCaptchaBuilder
160
    {
161
162 46
        $this->version = $version;
163
164 46
        return $this;
165
    }
166
167
    /**
168
     * @return string
169
     */
170 1
    public function getVersion(): string
171
    {
172
173 1
        return $this->version;
174
    }
175
176
    /**
177
     * @param bool $skip_by_ip
178
     *
179
     * @return ReCaptchaBuilder
180
     */
181 46
    public function setSkipByIp(bool $skip_by_ip): ReCaptchaBuilder
182
    {
183
184 46
        $this->skip_by_ip = $skip_by_ip;
185
186 46
        return $this;
187
    }
188
189
    /**
190
     * @param null|string $api_domain
191
     *
192
     * @return ReCaptchaBuilder
193
     */
194 46
    public function setApiDomain(?string $api_domain = null): ReCaptchaBuilder
195
    {
196
197 46
        $this->api_domain = $api_domain ?? config('recaptcha.api_domain', self::DEFAULT_RECAPTCHA_API_DOMAIN);
198
199 46
        return $this;
200
    }
201
202
    /**
203
     * @return string
204
     */
205 2
    public function getApiDomain(): string
206
    {
207
208 2
        return $this->api_domain;
209
    }
210
211
    /**
212
     * @return ReCaptchaBuilder
213
     */
214 46
    public function setApiUrls(): ReCaptchaBuilder
215
    {
216
217 46
        $this->api_url = 'https://' . $this->api_domain . '/recaptcha/api/siteverify';
218 46
        $this->api_js_url = 'https://' . $this->api_domain . '/recaptcha/api.js';
219
220 46
        return $this;
221
    }
222
223
    /**
224
     * @return array|mixed
225
     */
226 46
    public function getIpWhitelist()
227
    {
228
229 46
        $whitelist = config('recaptcha.skip_ip', []);
230
231 46
        if (!is_array($whitelist)) {
232 4
            $whitelist = explode(',', $whitelist);
233
        }
234
235
        $whitelist = array_map(function ($item) {
236
237 4
            return trim($item);
238 46
        }, $whitelist);
239
240 46
        return $whitelist;
241
    }
242
243
    /**
244
     * Checks whether the user IP address is among IPs "to be skipped"
245
     *
246
     * @return boolean
247
     */
248 46
    public function skipByIp(): bool
249
    {
250
251 46
        return (in_array(request()->ip(), $this->getIpWhitelist()));
252
    }
253
254
    /**
255
     * Write script HTML tag in you HTML code
256
     * Insert before </head> tag
257
     *
258
     * @param array|null $configuration
259
     *
260
     * @return string
261
     * @throws \Exception
262
     */
263 6
    public function htmlScriptTagJsApi(?array $configuration = []): string
264
    {
265
266 6
        $query = [];
267 6
        $html = '';
268
269
        // Language: "hl" parameter
270
        // resources $configuration parameter overrides default language
271 6
        $language = Arr::get($configuration, 'lang');
272 6
        if (!$language) {
273 5
            $language = config('recaptcha.default_language', null);
274
        }
275 6
        if ($language) {
276 2
            Arr::set($query, 'hl', $language);
277
        }
278
279
        // Onload JS callback function: "onload" parameter
280
        // "render" parameter set to "explicit"
281 6
        if (config('recaptcha.explicit', null) && $this->version === 'v2') {
282 2
            Arr::set($query, 'render', 'explicit');
283 2
            Arr::set($query, 'onload', self::DEFAULT_ONLOAD_JS_FUNCTION);
284
285
            /** @scrutinizer ignore-call */
286 2
            $html = $this->getOnLoadCallback();
287
        }
288
289
        // Create query string
290 5
        $query = ($query) ? '?' . http_build_query($query) : "";
291 5
        $html .= "<script src=\"" . $this->api_js_url .  $query . "\" async defer></script>";
292
293 5
        return $html;
294
    }
295
296
    /**
297
     * Call out to reCAPTCHA and process the response
298
     *
299
     * @param string $response
300
     *
301
     * @return boolean|array
302
     */
303 3
    public function validate($response)
304
    {
305
306 3
        if ($this->skip_by_ip) {
307 2
            if ($this->returnArray()) {
308
                // Add 'skip_by_ip' field to response
309
                return [
310 1
                    'skip_by_ip' => true,
311
                    'score'      => 0.9,
312
                    'success'    => true
313
                ];
314
            }
315
316 1
            return true;
317
        }
318
319 1
        $params = http_build_query([
320 1
            'secret'   => $this->api_secret_key,
321 1
            'remoteip' => request()->getClientIp(),
322 1
            'response' => $response,
323
        ]);
324
325 1
        $url = $this->api_url . '?' . $params;
326
327 1
        if (function_exists('curl_version')) {
328
329 1
            $curl = curl_init($url);
330 1
            curl_setopt($curl, CURLOPT_HEADER, false);
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

330
            curl_setopt(/** @scrutinizer ignore-type */ $curl, CURLOPT_HEADER, false);
Loading history...
331 1
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
332 1
            curl_setopt($curl, CURLOPT_TIMEOUT, $this->getCurlTimeout());
333 1
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
334 1
            $curl_response = curl_exec($curl);
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

334
            $curl_response = curl_exec(/** @scrutinizer ignore-type */ $curl);
Loading history...
335
        } else {
336
            $curl_response = file_get_contents($url);
337
        }
338
339 1
        if (is_null($curl_response) || empty($curl_response)) {
340
            if ($this->returnArray()) {
341
                // Add 'error' field to response
342
                return [
343
                    'error'   => 'cURL response empty',
344
                    'score'   => 0.1,
345
                    'success' => false
346
                ];
347
            }
348
349
            return false;
350
        }
351 1
        $response = json_decode(trim($curl_response), true);
352
353 1
        if ($this->returnArray()) {
354 1
            return $response;
355
        }
356
357
        return $response['success'];
358
    }
359
360
    /**
361
     * @return string
362
     */
363 1
    public function getApiSiteKey(): string
364
    {
365
366 1
        return $this->api_site_key;
367
    }
368
369
    /**
370
     * @return string
371
     */
372 1
    public function getApiSecretKey(): string
373
    {
374
375 1
        return $this->api_secret_key;
376
    }
377
378
    /**
379
     * @return bool
380
     */
381 3
    protected function returnArray(): bool
382
    {
383
384 3
        return ($this->version == 'v3');
385
    }
386
}
387