Passed
Push — master ( b01395...c75806 )
by Roberto
05:23 queued 13s
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 42
    public function __construct(
87
        string $api_site_key,
88
        string $api_secret_key,
89
        ?string $version = self::DEFAULT_API_VERSION
90
    ) {
91
92 42
        $this->setApiSiteKey($api_site_key);
93 42
        $this->setApiSecretKey($api_secret_key);
94 42
        $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 42
        $this->setSkipByIp($this->skipByIp());
96 42
    }
97
98
    /**
99
     * @param string $api_site_key
100
     *
101
     * @return ReCaptchaBuilder
102
     */
103 42
    public function setApiSiteKey(string $api_site_key): ReCaptchaBuilder
104
    {
105
106 42
        $this->api_site_key = $api_site_key;
107
108 42
        return $this;
109
    }
110
111
    /**
112
     * @param string $api_secret_key
113
     *
114
     * @return ReCaptchaBuilder
115
     */
116 42
    public function setApiSecretKey(string $api_secret_key): ReCaptchaBuilder
117
    {
118
119 42
        $this->api_secret_key = $api_secret_key;
120
121 42
        return $this;
122
    }
123
124
    /**
125
     * @return int
126
     */
127 4
    public function getCurlTimeout(): int
128
    {
129
130 4
        return config('recaptcha.curl_timeout', self::DEFAULT_CURL_TIMEOUT);
131
    }
132
133
    /**
134
     * @param string $version
135
     *
136
     * @return ReCaptchaBuilder
137
     */
138 42
    public function setVersion(string $version): ReCaptchaBuilder
139
    {
140
141 42
        $this->version = $version;
142
143 42
        return $this;
144
    }
145
146
    /**
147
     * @return string
148
     */
149 1
    public function getVersion(): string
150
    {
151
152 1
        return $this->version;
153
    }
154
155
    /**
156
     * @param bool $skip_by_ip
157
     *
158
     * @return ReCaptchaBuilder
159
     */
160 42
    public function setSkipByIp(bool $skip_by_ip): ReCaptchaBuilder
161
    {
162
163 42
        $this->skip_by_ip = $skip_by_ip;
164
165 42
        return $this;
166
    }
167
168
    /**
169
     * @return array|mixed
170
     */
171 42
    public function getIpWhitelist()
172
    {
173
174 42
        $whitelist = config('recaptcha.skip_ip', []);
175
176 42
        if (!is_array($whitelist)) {
177 4
            $whitelist = explode(',', $whitelist);
178
        }
179
180
        $whitelist = array_map(function ($item) {
181
182 4
            return trim($item);
183 42
        }, $whitelist);
184
185 42
        return $whitelist;
186
    }
187
188
    /**
189
     * Checks whether the user IP address is among IPs "to be skipped"
190
     *
191
     * @return boolean
192
     */
193 42
    public function skipByIp(): bool
194
    {
195
196 42
        return (in_array(request()->ip(), $this->getIpWhitelist()));
197
    }
198
199
    /**
200
     * Write script HTML tag in you HTML code
201
     * Insert before </head> tag
202
     *
203
     * @param array|null $configuration
204
     *
205
     * @return string
206
     * @throws \Exception
207
     */
208 4
    public function htmlScriptTagJsApi(?array $configuration = []): string
209
    {
210
211 4
        $query = [];
212 4
        $html = '';
213
214
        // Language: "hl" parameter
215
        // resources $configuration parameter overrides default language
216 4
        $language = Arr::get($configuration, 'resources');
217 4
        if (!$language) {
218 4
            $language = config('recaptcha.default_language', null);
219
        }
220 4
        if ($language) {
221 1
            Arr::set($query, 'hl', $language);
222
        }
223
224
        // Onload JS callback function: "onload" parameter
225
        // "render" parameter set to "explicit"
226 4
        if (config('recaptcha.explicit', null) && $this->version === 'v2') {
227 2
            Arr::set($query, 'render', 'explicit');
228 2
            Arr::set($query, 'onload', self::DEFAULT_ONLOAD_JS_FUNCTION);
229
230 2
            $html = $this->getOnLoadCallback();
0 ignored issues
show
Bug introduced by
The method getOnLoadCallback() does not exist on Biscolab\ReCaptcha\ReCaptchaBuilder. It seems like you code against a sub-type of Biscolab\ReCaptcha\ReCaptchaBuilder such as Biscolab\ReCaptcha\ReCaptchaBuilderV2. ( Ignorable by Annotation )

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

230
            /** @scrutinizer ignore-call */ 
231
            $html = $this->getOnLoadCallback();
Loading history...
231
        }
232
233
        // Create query string
234 3
        $query = ($query) ? '?' . http_build_query($query) : "";
235 3
        $html .= "<script src=\"https://www.google.com/recaptcha/api.js" . $query . "\" async defer></script>";
236
237 3
        return $html;
238
    }
239
240
    /**
241
     * Call out to reCAPTCHA and process the response
242
     *
243
     * @param string $response
244
     *
245
     * @return boolean|array
246
     */
247 3
    public function validate($response)
248
    {
249
250 3
        if ($this->skip_by_ip) {
251 2
            if ($this->returnArray()) {
252
                // Add 'skip_by_ip' field to response
253
                return [
254 1
                    'skip_by_ip' => true,
255
                    'score'      => 0.9,
256
                    'success'    => true
257
                ];
258
            }
259
260 1
            return true;
261
        }
262
263 1
        $params = http_build_query([
264 1
            'secret'   => $this->api_secret_key,
265 1
            'remoteip' => request()->getClientIp(),
266 1
            'response' => $response,
267
        ]);
268
269 1
        $url = $this->api_url . '?' . $params;
270
271 1
        if (function_exists('curl_version')) {
272
273 1
            $curl = curl_init($url);
274 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

274
            curl_setopt(/** @scrutinizer ignore-type */ $curl, CURLOPT_HEADER, false);
Loading history...
275 1
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
276 1
            curl_setopt($curl, CURLOPT_TIMEOUT, $this->getCurlTimeout());
277 1
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
278 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

278
            $curl_response = curl_exec(/** @scrutinizer ignore-type */ $curl);
Loading history...
279
        } else {
280
            $curl_response = file_get_contents($url);
281
        }
282
283 1
        if (is_null($curl_response) || empty($curl_response)) {
284
            if ($this->returnArray()) {
285
                // Add 'error' field to response
286
                return [
287
                    'error'   => 'cURL response empty',
288
                    'score'   => 0.1,
289
                    'success' => false
290
                ];
291
            }
292
293
            return false;
294
        }
295 1
        $response = json_decode(trim($curl_response), true);
296
297 1
        if ($this->returnArray()) {
298 1
            return $response;
299
        }
300
301
        return $response['success'];
302
303
    }
304
305
    /**
306
     * @return string
307
     */
308 1
    public function getApiSiteKey(): string
309
    {
310
311 1
        return $this->api_site_key;
312
    }
313
314
    /**
315
     * @return string
316
     */
317 1
    public function getApiSecretKey(): string
318
    {
319
320 1
        return $this->api_secret_key;
321
    }
322
323
    /**
324
     * @return bool
325
     */
326 3
    protected function returnArray(): bool
327
    {
328
329 3
        return ($this->version == 'v3');
330
    }
331
}