Completed
Pull Request — master (#79)
by ARCANEDEV
07:58
created

NoCaptcha::toHtmlString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
ccs 2
cts 2
cp 1
cc 1
nc 1
nop 1
crap 1
1
<?php namespace Arcanedev\NoCaptcha;
2
3
use Arcanedev\NoCaptcha\Utilities\Request;
4
use Illuminate\Support\Arr;
5
use Illuminate\Support\HtmlString;
6
use Psr\Http\Message\ServerRequestInterface;
7
8
/**
9
 * Class     NoCaptcha
10
 *
11
 * @package  Arcanedev\NoCaptcha
12
 * @author   ARCANEDEV <[email protected]>
13
 */
14
class NoCaptcha implements Contracts\NoCaptcha
15
{
16
    /* -----------------------------------------------------------------
17
     |  Constants
18
     | -----------------------------------------------------------------
19
     */
20
21
    const CLIENT_URL   = 'https://www.google.com/recaptcha/api.js';
22
    const VERIFY_URL   = 'https://www.google.com/recaptcha/api/siteverify';
23
    const CAPTCHA_NAME = 'g-recaptcha-response';
24
25
    /* -----------------------------------------------------------------
26
     |  Properties
27
     | -----------------------------------------------------------------
28
     */
29
30
    /**
31
     * The shared key between your site and ReCAPTCHA
32
     *
33
     * @var string
34
     */
35
    private $secret;
36
37
    /**
38
     * Your site key
39
     *
40
     * @var string
41
     */
42
    private $siteKey;
43
44
    /**
45
     * Forces the widget to render in a specific language.
46
     * Auto-detects the user's language if unspecified.
47
     *
48
     * @var string
49
     */
50
    protected $lang;
51
52
    /**
53
     * Decides if we've already loaded the script file or not.
54
     *
55
     * @param bool
56
     */
57
    protected $scriptLoaded = false;
58
59
    /**
60
     * HTTP Request Client
61
     *
62
     * @var \Arcanedev\NoCaptcha\Contracts\Utilities\Request
63
     */
64
    protected $request;
65
66
    /**
67
     * ReCaptcha's response.
68
     *
69
     * @var \Arcanedev\NoCaptcha\Utilities\Response|null
70
     */
71
    protected $response;
72
73
    /* -----------------------------------------------------------------
74
     |  Constructor
75
     | -----------------------------------------------------------------
76
     */
77
78
    /**
79
     * NoCaptcha constructor.
80
     *
81
     * @param  string       $secret
82
     * @param  string       $siteKey
83
     * @param  string|null  $lang
84
     */
85 51
    public function __construct($secret, $siteKey, $lang = null)
86
    {
87 51
        $this->setSecret($secret);
88 51
        $this->setSiteKey($siteKey);
89 51
        $this->setLang($lang);
90
91 51
        $this->setRequestClient(new Request);
92 51
    }
93
94
    /* -----------------------------------------------------------------
95
     |  Getters & Setters
96
     | -----------------------------------------------------------------
97
     */
98
99
    /**
100
     * Set the secret key.
101
     *
102
     * @param  string  $secret
103
     *
104
     * @return self
105
     */
106 51
    protected function setSecret($secret)
107
    {
108 51
        self::checkKey('secret key', $secret);
109
110 51
        $this->secret = $secret;
111
112 51
        return $this;
113
    }
114
115
    /**
116
     * Get the site key.
117
     *
118
     * @return string
119
     */
120 15
    public function getSiteKey()
121
    {
122 15
        return $this->siteKey;
123
    }
124
125
    /**
126
     * Set Site key.
127
     *
128
     * @param  string  $siteKey
129
     *
130
     * @return self
131
     */
132 51
    protected function setSiteKey($siteKey)
133
    {
134 51
        self::checkKey('site key', $siteKey);
135
136 51
        $this->siteKey = $siteKey;
137
138 51
        return $this;
139
    }
140
141
    /**
142
     * Set language code.
143
     *
144
     * @param  string  $lang
145
     *
146
     * @return self
147
     */
148 51
    public function setLang($lang)
149
    {
150 51
        $this->lang = $lang;
151
152 51
        return $this;
153
    }
154
155
    /**
156
     * Set HTTP Request Client.
157
     *
158
     * @param  \Arcanedev\NoCaptcha\Contracts\Utilities\Request  $request
159
     *
160
     * @return self
161
     */
162 51
    public function setRequestClient(Contracts\Utilities\Request $request)
163
    {
164 51
        $this->request = $request;
165
166 51
        return $this;
167
    }
168
169
    /**
170
     * Get the last response.
171
     *
172
     * @return \Arcanedev\NoCaptcha\Utilities\Response|null
173
     */
174 3
    public function getLastResponse()
175
    {
176 3
        return $this->response;
177
    }
178
179
    /* -----------------------------------------------------------------
180
     |  Main Methods
181
     | -----------------------------------------------------------------
182
     */
183
184
    /**
185
     * @param  string  $name
186
     *
187
     * @return \Illuminate\Support\HtmlString
188
     */
189 9
    public function input($name = 'g-recaptcha-response')
190
    {
191 9
        return $this->toHtmlString(
192 9
            '<input type="hidden" id="'.$name.'" name="'.$name.'">'
193
        );
194
    }
195
196
    /**
197
     * Verify Response.
198
     *
199
     * @param  string  $response
200
     * @param  string  $clientIp
201
     *
202
     * @return \Arcanedev\NoCaptcha\Utilities\Response
203
     */
204 15
    public function verify($response, $clientIp = null)
205
    {
206 15
        return $this->response = $this->sendVerifyRequest([
207 15
            'secret'   => $this->secret,
208 15
            'response' => $response,
209 15
            'remoteip' => $clientIp
210
        ]);
211
    }
212
213
    /**
214
     * Calls the reCAPTCHA siteverify API to verify whether the user passes CAPTCHA
215
     * test using a PSR-7 ServerRequest object.
216
     *
217
     * @param  \Psr\Http\Message\ServerRequestInterface  $request
218
     *
219
     * @return \Arcanedev\NoCaptcha\Utilities\Response
220
     */
221 3
    public function verifyRequest(ServerRequestInterface $request)
222
    {
223 3
        $body   = $request->getParsedBody();
224 3
        $server = $request->getServerParams();
225
226 3
        return $this->verify(
227 3
            $body[self::CAPTCHA_NAME] ?? '',
228 3
            $server['REMOTE_ADDR'] ?? null
229
        );
230
    }
231
232
    /**
233
     * Get script tag.
234
     *
235
     * @param  string|null  $callbackName
236
     *
237
     * @return \Illuminate\Support\HtmlString
238
     */
239 15
    public function script($callbackName = null)
240
    {
241 15
        $script = '';
242
243 15
        if ( ! $this->scriptLoaded) {
244 15
            $script = '<script src="'.$this->getScriptSrc($callbackName).'"></script>';
245 15
            $this->scriptLoaded = true;
246
        }
247
248 15
        return $this->toHtmlString($script);
249
    }
250
251
    /**
252
     * Get the NoCaptcha API Script.
253
     *
254
     * @return \Illuminate\Support\HtmlString
255
     */
256
    public function getApiScript()
257
    {
258
        return $this->toHtmlString(
259
            "<script>
260
                window.noCaptcha = {
261
                    render: function(action, callback) {
262
                        grecaptcha.execute('".$this->getSiteKey()."', {action})
263
                              .then(callback);
264
                    }
265
                }
266
            </script>"
267
        );
268
    }
269
270
    /* -----------------------------------------------------------------
271
     |  Check Methods
272
     | -----------------------------------------------------------------
273
     */
274
275
    /**
276
     * Check if has lang.
277
     *
278
     * @return bool
279
     */
280 15
    private function hasLang()
281
    {
282 15
        return ! empty($this->lang);
283
    }
284
285
    /**
286
     * Check if callback is not empty.
287
     *
288
     * @param  string|null  $callbackName
289
     *
290
     * @return bool
291
     */
292 15
    private function hasCallbackName($callbackName)
293
    {
294 15
        return ! (is_null($callbackName) || trim($callbackName) === '');
295
    }
296
297
    /**
298
     * Check key.
299
     *
300
     * @param  string  $name
301
     * @param  string  $value
302
     */
303 51
    private static function checkKey($name, &$value)
304
    {
305 51
        self::checkIsString($name, $value);
306
307 51
        $value = trim($value);
308
309 51
        self::checkIsNotEmpty($name, $value);
310 51
    }
311
312
    /**
313
     * Check if the value is a string value.
314
     *
315
     * @param  string  $name
316
     * @param  string  $value
317
     *
318
     * @throws \Arcanedev\NoCaptcha\Exceptions\ApiException
319
     */
320 51
    private static function checkIsString($name, $value)
321
    {
322 51
        if ( ! is_string($value)) {
323 6
            throw new Exceptions\ApiException(
324 6
                "The {$name} must be a string value, ".gettype($value).' given.'
325
            );
326
        }
327 51
    }
328
329
    /**
330
     * Check if the value is not empty.
331
     *
332
     * @param string  $name
333
     * @param string  $value
334
     *
335
     * @throws \Arcanedev\NoCaptcha\Exceptions\ApiException
336
     */
337 51
    private static function checkIsNotEmpty($name, $value)
338
    {
339 51
        if (empty($value)) {
340 6
            throw new Exceptions\ApiException("The {$name} must not be empty");
341
        }
342 51
    }
343
344
    /* -----------------------------------------------------------------
345
     |  Other Methods
346
     | -----------------------------------------------------------------
347
     */
348
349
    /**
350
     * Send verify request to API and get response.
351
     *
352
     * @param  array  $query
353
     *
354
     * @return \Arcanedev\NoCaptcha\Utilities\Response
355
     */
356 15
    private function sendVerifyRequest(array $query = [])
357
    {
358 15
        $query = array_filter($query);
359
360 15
        return $this->request->send(
361 15
            static::VERIFY_URL.'?'.http_build_query($query)
362
        );
363
    }
364
365
    /**
366
     * Get script source link.
367
     *
368
     * @param  string|null  $callbackName
369
     *
370
     * @return string
371
     */
372 15
    private function getScriptSrc($callbackName = null)
373
    {
374 15
        $queries = [];
375
376 15
        if ($this->hasLang())
377 9
            Arr::set($queries, 'hl', $this->lang);
378
379 15
        Arr::set($queries, 'render', $this->getSiteKey());
380
381 15
        if ($this->hasCallbackName($callbackName)) {
382
            Arr::set($queries, 'onload', $callbackName);
383
        }
384
385 15
        return static::CLIENT_URL . (count($queries) ? '?' . http_build_query($queries) : '');
386
    }
387
388
    /**
389
     * Transform the string to an Html serializable object
390
     *
391
     * @param  string  $html
392
     *
393
     * @return \Illuminate\Support\HtmlString
394
     */
395 24
    protected function toHtmlString($html)
396
    {
397 24
        return new HtmlString($html);
398
    }
399
}
400