Completed
Push — master ( da6bda...f012c7 )
by ARCANEDEV
16s
created

src/NoCaptchaV2.php (2 issues)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php namespace Arcanedev\NoCaptcha;
2
3
use Arcanedev\Html\Elements\Button;
4
use Arcanedev\Html\Elements\Div;
5
use Arcanedev\NoCaptcha\Exceptions\InvalidArgumentException;
6
use Arcanedev\NoCaptcha\Utilities\ResponseV2;
7
use Illuminate\Support\Arr;
8
9
/**
10
 * Class     NoCaptchaV2
11
 *
12
 * @package  Arcanedev\NoCaptcha
13
 * @author   ARCANEDEV <[email protected]>
14
 */
15
class NoCaptchaV2 extends AbstractNoCaptcha
16
{
17
    /* -----------------------------------------------------------------
18
     |  Properties
19
     | -----------------------------------------------------------------
20
     */
21
22
    /**
23
     * Decides if we've already loaded the script file or not.
24
     *
25
     * @param bool
26
     */
27
    protected $scriptLoaded = false;
28
29
    /* -----------------------------------------------------------------
30
     |  Getters & Setters
31
     | -----------------------------------------------------------------
32
     */
33
34
    /**
35
     * Get script source link.
36
     *
37
     * @param  string|null  $callbackName
38
     *
39
     * @return string
40
     */
41 20
    private function getScriptSrc($callbackName = null)
42
    {
43 20
        $queries = [];
44
45 20
        if ($this->hasLang())
46 8
            Arr::set($queries, 'hl', $this->lang);
47
48 20
        if ($this->hasCallbackName($callbackName)) {
49 4
            Arr::set($queries, 'onload', $callbackName);
50 4
            Arr::set($queries, 'render', 'explicit');
51
        }
52
53 20
        return $this->getClientUrl() . (count($queries) ? '?' . http_build_query($queries) : '');
54
    }
55
56
    /* -----------------------------------------------------------------
57
     |  Main Methods
58
     | -----------------------------------------------------------------
59
     */
60
61
    /**
62
     * Display Captcha.
63
     *
64
     * @param  string|null  $name
65
     * @param  array        $attributes
66
     *
67
     * @return \Arcanedev\Html\Elements\Div
68
     */
69 76
    public function display($name = null, array $attributes = [])
70
    {
71 76
        return Div::make()->attributes(array_merge(
0 ignored issues
show
array_merge(static::prep...ttributes($attributes)) is of type array, but the function expects a object<Arcanedev\Html\Elements\Concerns\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
72 76
            static::prepareNameAttribute($name),
73 64
            $this->prepareAttributes($attributes)
74
        ));
75
    }
76
77
    /**
78
     * Display image Captcha.
79
     *
80
     * @param  string|null  $name
81
     * @param  array        $attributes
82
     *
83
     * @return \Arcanedev\Html\Elements\Div
84
     */
85 12
    public function image($name = null, array $attributes = [])
86
    {
87 12
        return $this->display($name, array_merge(
88 9
            $attributes,
89 12
            ['data-type' => 'image']
90
        ));
91
    }
92
93
    /**
94
     * Display audio Captcha.
95
     *
96
     * @param  string|null  $name
97
     * @param  array        $attributes
98
     *
99
     * @return \Arcanedev\Html\Elements\Div
100
     */
101 12
    public function audio($name = null, array $attributes = [])
102
    {
103 12
        return $this->display($name, array_merge(
104 9
            $attributes,
105 12
            ['data-type' => 'audio']
106
        ));
107
    }
108
109
    /**
110
     * Display an invisible Captcha (bind the challenge to a button).
111
     *
112
     * @param  string  $value
113
     * @param  array   $attributes
114
     *
115
     * @return \Arcanedev\Html\Elements\Button
116
     */
117 4
    public function button($value, array $attributes = [])
118
    {
119 4
        return Button::make()->text($value)->attributes(array_merge(
0 ignored issues
show
array_merge(array('data-...ttributes($attributes)) is of type array, but the function expects a object<Arcanedev\Html\Elements\Concerns\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
120 4
            ['data-callback' => 'onSubmit'],
121 4
            $this->prepareAttributes($attributes)
122
        ));
123
    }
124
125
    /**
126
     * Get script tag.
127
     *
128
     * @param  string|null  $callbackName
129
     *
130
     * @return \Illuminate\Support\HtmlString
131
     */
132 20
    public function script($callbackName = null)
133
    {
134 20
        $script = '';
135
136 20
        if ( ! $this->scriptLoaded) {
137 20
            $script = '<script src="'.$this->getScriptSrc($callbackName).'" async defer></script>';
138 20
            $this->scriptLoaded = true;
139
        }
140
141 20
        return $this->toHtmlString($script);
142
    }
143
144
    /**
145
     * Get the NoCaptcha API Script.
146
     *
147
     * @return \Illuminate\Support\HtmlString
148
     */
149 4
    public function getApiScript()
150
    {
151 4
        return $this->toHtmlString(
152 3
            "<script>
153
                window.noCaptcha = {
154
                    captchas: [],
155
                    reset: function(name) {
156
                        var captcha = window.noCaptcha.get(name);
157
        
158
                        if (captcha)
159
                            window.noCaptcha.resetById(captcha.id);
160
                    },
161
                    resetById: function(id) {
162
                        grecaptcha.reset(id);
163
                    },
164
                    get: function(name) {
165
                        return window.noCaptcha.find(function (captcha) {
166
                            return captcha.name === name;
167
                        });
168
                    },
169
                    getById: function(id) {
170
                        return window.noCaptcha.find(function (captcha) {
171
                            return captcha.id === id;
172
                        });
173
                    },
174
                    find: function(callback) {
175
                        return window.noCaptcha.captchas.find(callback);
176
                    },
177
                    render: function(name, sitekey) {
178
                        var captcha = {
179
                            id: grecaptcha.render(name, {'sitekey' : sitekey}),
180
                            name: name
181
                        };
182
                        
183
                        window.noCaptcha.captchas.push(captcha);
184
                        
185
                        return captcha;
186
                    }
187
                }
188 1
            </script>"
189
        );
190
    }
191
192
    /**
193
     * Get script tag with a callback function.
194
     *
195
     * @param  array   $captchas
196
     * @param  string  $callbackName
197
     *
198
     * @return \Illuminate\Support\HtmlString
199
     */
200 4
    public function scriptWithCallback(array $captchas, $callbackName = 'captchaRenderCallback')
201
    {
202 4
        $script = $this->script($callbackName)->toHtml();
203
204 4
        if ( ! empty($script) && ! empty($captchas)) {
205 4
            $script = implode(PHP_EOL, [
206 4
                $this->getApiScript()->toHtml(),
207 4
                '<script>',
208 4
                    "var $callbackName = function() {",
209 4
                        $this->renderCaptchas($captchas),
210 4
                    '};',
211 4
                '</script>',
212 4
                $script,
213
            ]);
214
        }
215
216 4
        return $this->toHtmlString($script);
217
    }
218
219
    /**
220
     * Rendering captchas with callback function.
221
     *
222
     * @param  array  $captchas
223
     *
224
     * @return string
225
     */
226 4
    private function renderCaptchas(array $captchas)
227
    {
228
        return implode(PHP_EOL, array_map(function($captcha) {
229 4
            return "if (document.getElementById('{$captcha}')) { window.noCaptcha.render('{$captcha}', '{$this->siteKey}'); }";
230 4
        }, $captchas));
231
    }
232
233
    /* -----------------------------------------------------------------
234
     |  Check Methods
235
     | -----------------------------------------------------------------
236
     */
237
238
    /**
239
     * Check if callback is not empty.
240
     *
241
     * @param  string|null  $callbackName
242
     *
243
     * @return bool
244
     */
245 20
    private function hasCallbackName($callbackName)
246
    {
247 20
        return ! (is_null($callbackName) || trim($callbackName) === '');
248
    }
249
250
    /* -----------------------------------------------------------------
251
     |  Other Methods
252
     | -----------------------------------------------------------------
253
     */
254
255
    /**
256
     * Parse the response.
257
     *
258
     * @param  string  $json
259
     *
260
     * @return \Arcanedev\NoCaptcha\Utilities\AbstractResponse|mixed
261
     */
262 12
    protected function parseResponse($json)
263
    {
264 12
        return ResponseV2::fromJson($json);
265
    }
266
267
    /**
268
     * Prepare the attributes.
269
     *
270
     * @param  array  $attributes
271
     *
272
     * @return array
273
     */
274 68
    private function prepareAttributes(array $attributes)
275
    {
276 68
        $attributes = array_merge(
277 68
            ['class' => 'g-recaptcha', 'data-sitekey' => $this->siteKey],
278 51
            array_filter($attributes)
279
        );
280
281 68
        self::checkDataAttribute($attributes, 'data-type', ['image', 'audio'], 'image');
282 68
        self::checkDataAttribute($attributes, 'data-theme', ['light', 'dark'], 'light');
283 68
        self::checkDataAttribute($attributes, 'data-size', ['normal', 'compact', 'invisible'], 'normal');
284 68
        self::checkDataAttribute($attributes, 'data-badge', ['bottomright', 'bottomleft', 'inline'], 'bottomright');
285
286 68
        return $attributes;
287
    }
288
289
    /**
290
     * Check the `data-*` attribute.
291
     *
292
     * @param  array   $attributes
293
     * @param  string  $name
294
     * @param  array   $supported
295
     * @param  string  $default
296
     */
297 68
    private static function checkDataAttribute(array &$attributes, $name, array $supported, $default)
298
    {
299 68
        $attribute = $attributes[$name] ?? null;
300
301 68
        if ( ! is_null($attribute)) {
302 60
            $attribute = (is_string($attribute) && in_array($attribute, $supported))
303 44
                ? strtolower(trim($attribute))
304 60
                : $default;
305
306 60
            $attributes[$name] = $attribute;
307
        }
308 68
    }
309
310
    /**
311
     * Prepare the name and id attributes.
312
     *
313
     * @param  string|null  $name
314
     *
315
     * @return array
316
     *
317
     * @throws \Arcanedev\NoCaptcha\Exceptions\InvalidArgumentException
318
     */
319 76
    protected static function prepareNameAttribute($name)
320
    {
321 76
        if (is_null($name))
322 4
            return [];
323
324 72
        if ($name === AbstractNoCaptcha::CAPTCHA_NAME) {
325 12
            throw new InvalidArgumentException(
326 12
                'The captcha name must be different from "' . AbstractNoCaptcha::CAPTCHA_NAME . '".'
327
            );
328
        }
329
330 60
        return array_combine(['id', 'name'], [$name, $name]);
331
    }
332
}
333