Passed
Pull Request — master (#43)
by
unknown
06:15
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 6
Bugs 0 Features 1
Metric Value
cc 8
eloc 32
c 6
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
 * Copyright (c) 2017 - present
4
 * LaravelGoogleRecaptcha - ReCaptchaBuilder.php
5
 * author: Roberto Belotti - [email protected]
6
 * web : robertobelotti.com, github.com/biscolab
7
 * Initial version created on: 12/9/2018
8
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
9
 */
10
11
namespace Biscolab\ReCaptcha;
12
13
use Illuminate\Support\Arr;
14
15
/**
16
 * Class ReCaptchaBuilder
17
 * @package Biscolab\ReCaptcha
18
 */
19
class ReCaptchaBuilder
20
{
21
22
    /**
23
     * @var string
24
     */
25
    const DEFAULT_API_VERSION = 'v2';
26
27
    /**
28
     * @var int
29
     */
30
    const DEFAULT_CURL_TIMEOUT = 10;
31
32
    /**
33
     * @var string
34
     */
35
    const DEFAULT_ONLOAD_JS_FUNCTION = 'biscolabOnloadCallback';
36
37
    /**
38
     * @var string
39
     */
40
    const DEFAULT_RECAPTCHA_RULE_NAME = 'recaptcha';
41
42
    /**
43
     * @var string
44
     */
45
    const DEFAULT_RECAPTCHA_FIELD_NAME = 'g-recaptcha-response';
46
47
    /**
48
     * The Site key
49
     * please visit https://developers.google.com/recaptcha/docs/start
50
     * @var string
51
     */
52
    protected $api_site_key;
53
54
    /**
55
     * The Secret key
56
     * please visit https://developers.google.com/recaptcha/docs/start
57
     * @var string
58
     */
59
    protected $api_secret_key;
60
61
    /**
62
     * The chosen ReCAPTCHA version
63
     * please visit https://developers.google.com/recaptcha/docs/start
64
     * @var string
65
     */
66
    protected $version;
67
68
    /**
69
     * Whether is true the ReCAPTCHA is inactive
70
     * @var boolean
71
     */
72
    protected $skip_by_ip = false;
73
74
    /**
75
     * The API request URI
76
     */
77
    protected $api_url = 'https://www.google.com/recaptcha/api/siteverify';
78
79
    /**
80
     * ReCaptchaBuilder constructor.
81
     *
82
     * @param string      $api_site_key
83
     * @param string      $api_secret_key
84
     * @param null|string $version
85
     */
86 43
    public function __construct(
87
        string $api_site_key,
88
        string $api_secret_key,
89
        ?string $version = self::DEFAULT_API_VERSION
90
    ) {
91
92 43
        $this->setApiSiteKey($api_site_key);
93 43
        $this->setApiSecretKey($api_secret_key);
94 43
        $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

94
        $this->setVersion(/** @scrutinizer ignore-type */ $version);
Loading history...
95 43
        $this->setSkipByIp($this->skipByIp());
96 43
        $this->setAPIURL();
97 43
    }
98
99
    /**
100
     * @param string $api_site_key
101
     *
102
     * @return ReCaptchaBuilder
103
     */
104 43
    public function setApiSiteKey(string $api_site_key): ReCaptchaBuilder
105
    {
106
107 43
        $this->api_site_key = $api_site_key;
108
109 43
        return $this;
110
    }
111
112
    /**
113
     * @param string $api_secret_key
114
     *
115
     * @return ReCaptchaBuilder
116
     */
117 43
    public function setApiSecretKey(string $api_secret_key): ReCaptchaBuilder
118
    {
119
120 43
        $this->api_secret_key = $api_secret_key;
121
122 43
        return $this;
123
    }
124
125
    /**
126
     * @return int
127
     */
128 4
    public function getCurlTimeout(): int
129
    {
130
131 4
        return config('recaptcha.curl_timeout', self::DEFAULT_CURL_TIMEOUT);
132
    }
133
134
    /**
135
     * @param string $version
136
     *
137
     * @return ReCaptchaBuilder
138
     */
139 43
    public function setVersion(string $version): ReCaptchaBuilder
140
    {
141
142 43
        $this->version = $version;
143
144 43
        return $this;
145
    }
146
147
    /**
148
     * @return string
149
     */
150 1
    public function getVersion(): string
151
    {
152
153 1
        return $this->version;
154
    }
155
156
    /**
157
     * @param bool $skip_by_ip
158
     *
159
     * @return ReCaptchaBuilder
160
     */
161 43
    public function setSkipByIp(bool $skip_by_ip): ReCaptchaBuilder
162
    {
163
164 43
        $this->skip_by_ip = $skip_by_ip;
165
166 43
        return $this;
167
    }
168
169
    /**
170
     *
171
     * @return ReCaptchaBuilder
172
     */
173 43
    public function setAPIURL(): ReCaptchaBuilder
174
    {
175 43
        $this->api_url = 'https://'.config('recaptcha.baseurl','www.google.com').'/recaptcha/api/siteverify';
176
177 43
        return $this;
178
    }
179
180
    /**
181
     * @return array|mixed
182
     */
183 43
    public function getIpWhitelist()
184
    {
185
186 43
        $whitelist = config('recaptcha.skip_ip', []);
187
188 43
        if (!is_array($whitelist)) {
189 4
            $whitelist = explode(',', $whitelist);
190
        }
191
192
        $whitelist = array_map(function ($item) {
193
194 4
            return trim($item);
195 43
        }, $whitelist);
196
197 43
        return $whitelist;
198
    }
199
200
    /**
201
     * Checks whether the user IP address is among IPs "to be skipped"
202
     *
203
     * @return boolean
204
     */
205 43
    public function skipByIp(): bool
206
    {
207
208 43
        return (in_array(request()->ip(), $this->getIpWhitelist()));
209
    }
210
211
    /**
212
     * Write script HTML tag in you HTML code
213
     * Insert before </head> tag
214
     *
215
     * @param array|null $configuration
216
     *
217
     * @return string
218
     * @throws \Exception
219
     */
220 5
    public function htmlScriptTagJsApi(?array $configuration = []): string
221
    {
222
223 5
        $query = [];
224 5
        $html = '';
225
226
        // Language: "hl" parameter
227
        // resources $configuration parameter overrides default language
228 5
        $language = Arr::get($configuration, 'lang');
229 5
        if (!$language) {
230 4
            $language = config('recaptcha.default_language', null);
231
        }
232 5
        if ($language) {
233 2
            Arr::set($query, 'hl', $language);
234
        }
235
236
        // Onload JS callback function: "onload" parameter
237
        // "render" parameter set to "explicit"
238 5
        if (config('recaptcha.explicit', null) && $this->version === 'v2') {
239 2
            Arr::set($query, 'render', 'explicit');
240 2
            Arr::set($query, 'onload', self::DEFAULT_ONLOAD_JS_FUNCTION);
241
242
            /** @scrutinizer ignore-call */
243 2
            $html = $this->getOnLoadCallback();
244
        }
245
246 4
        $baseURL = config('recaptcha.baseurl','www.google.com');
247
248
        // Create query string
249 4
        $query = ($query) ? '?' . http_build_query($query) : "";
250 4
        $html .= "<script src=\"https://".$baseURL."/recaptcha/api.js" . $query . "\" async defer></script>";
251
252 4
        return $html;
253
    }
254
255
    /**
256
     * Call out to reCAPTCHA and process the response
257
     *
258
     * @param string $response
259
     *
260
     * @return boolean|array
261
     */
262 3
    public function validate($response)
263
    {
264
265 3
        if ($this->skip_by_ip) {
266 2
            if ($this->returnArray()) {
267
                // Add 'skip_by_ip' field to response
268
                return [
269 1
                    'skip_by_ip' => true,
270
                    'score'      => 0.9,
271
                    'success'    => true
272
                ];
273
            }
274
275 1
            return true;
276
        }
277
278 1
        $params = http_build_query([
279 1
            'secret'   => $this->api_secret_key,
280 1
            'remoteip' => request()->getClientIp(),
281 1
            'response' => $response,
282
        ]);
283
284 1
        $url = $this->api_url . '?' . $params;
285
286 1
        if (function_exists('curl_version')) {
287
288 1
            $curl = curl_init($url);
289 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

289
            curl_setopt(/** @scrutinizer ignore-type */ $curl, CURLOPT_HEADER, false);
Loading history...
290 1
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
291 1
            curl_setopt($curl, CURLOPT_TIMEOUT, $this->getCurlTimeout());
292 1
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
293 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

293
            $curl_response = curl_exec(/** @scrutinizer ignore-type */ $curl);
Loading history...
294
        } else {
295
            $curl_response = file_get_contents($url);
296
        }
297
298 1
        if (is_null($curl_response) || empty($curl_response)) {
299
            if ($this->returnArray()) {
300
                // Add 'error' field to response
301
                return [
302
                    'error'   => 'cURL response empty',
303
                    'score'   => 0.1,
304
                    'success' => false
305
                ];
306
            }
307
308
            return false;
309
        }
310 1
        $response = json_decode(trim($curl_response), true);
311
312 1
        if ($this->returnArray()) {
313 1
            return $response;
314
        }
315
316
        return $response['success'];
317
318
    }
319
320
    /**
321
     * @return string
322
     */
323 1
    public function getApiSiteKey(): string
324
    {
325
326 1
        return $this->api_site_key;
327
    }
328
329
    /**
330
     * @return string
331
     */
332 1
    public function getApiSecretKey(): string
333
    {
334
335 1
        return $this->api_secret_key;
336
    }
337
338
    /**
339
     * @return bool
340
     */
341 3
    protected function returnArray(): bool
342
    {
343
344 3
        return ($this->version == 'v3');
345
    }
346
}
347