SimpleDriver::canvas()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
namespace Shetabit\Captcha\Drivers\Simple;
4
5
use Illuminate\Support\Facades\View;
6
use Illuminate\Support\ServiceProvider;
7
use Shetabit\Captcha\Abstracts\Driver;
8
9
class SimpleDriver extends Driver
10
{
11
    protected $serviceProvider;
12
13
    /**
14
     * Driver settings.
15
     *
16
     * @var object
17
     */
18
    protected $settings;
19
20
    /**
21
     * SimpleDriver constructor.
22
     * Construct the class with the relevant settings.
23
     *
24
     * SimpleDriver constructor.
25
     * @param ServiceProvider $serviceProvider
26
     * @param $settings
27
     */
28
    public function __construct(ServiceProvider $serviceProvider, $settings)
29
    {
30
        $this->serviceProvider = $serviceProvider;
31
        $this->settings = (object) $settings;
32
33
        $this->bindViews()
34
             ->bindRoutes()
35
             ->publishResources();
36
    }
37
38
    /**
39
     * Bind driver views
40
     *
41
     * @return $this
42
     */
43
    protected function bindViews()
44
    {
45
        $this->serviceProvider->bindViewFile(__DIR__ . '/resources/views', 'captchaSimpleDriver');
0 ignored issues
show
Bug introduced by
The method bindViewFile() does not exist on Illuminate\Support\ServiceProvider. It seems like you code against a sub-type of Illuminate\Support\ServiceProvider such as Illuminate\Foundation\Su...rs\RouteServiceProvider or Shetabit\Captcha\Provider\CaptchaServiceProvider. ( Ignorable by Annotation )

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

45
        $this->serviceProvider->/** @scrutinizer ignore-call */ 
46
                                bindViewFile(__DIR__ . '/resources/views', 'captchaSimpleDriver');
Loading history...
46
47
        return $this;
48
    }
49
50
    /**
51
     * Bind driver routes
52
     *
53
     * @return $this
54
     */
55
    protected function bindRoutes()
56
    {
57
        $this->serviceProvider->bindRouteFile(__DIR__ . '/routes.php');
0 ignored issues
show
Bug introduced by
The method bindRouteFile() does not exist on Illuminate\Support\ServiceProvider. It seems like you code against a sub-type of Illuminate\Support\ServiceProvider such as Illuminate\Foundation\Su...rs\RouteServiceProvider or Shetabit\Captcha\Provider\CaptchaServiceProvider. ( Ignorable by Annotation )

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

57
        $this->serviceProvider->/** @scrutinizer ignore-call */ 
58
                                bindRouteFile(__DIR__ . '/routes.php');
Loading history...
58
59
        return $this;
60
    }
61
62
    /**
63
     * Publish driver assets
64
     *
65
     * @return $this
66
     */
67
    protected function publishResources()
68
    {
69
        $destinationPath = resource_path('views/vendor/captchaSimpleDriver');
70
71
        $this->serviceProvider
72
             ->publish(__DIR__ . '/resources/views', $destinationPath, 'views')
0 ignored issues
show
Bug introduced by
The method publish() does not exist on Illuminate\Support\ServiceProvider. Did you maybe mean publishes()? ( Ignorable by Annotation )

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

72
             ->/** @scrutinizer ignore-call */ 
73
               publish(__DIR__ . '/resources/views', $destinationPath, 'views')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
73
             ->publish(__DIR__ . '/resources/assets', $destinationPath.'/assets', 'assets');
74
75
        return $this;
76
    }
77
78
    /**
79
     * Prepare CAPTCHA image and memorize its token.
80
     *
81
     * @return false|string
82
     */
83
    public function prepareCaptchaImage()
84
    {
85
        $settings = $this->settings;
86
87
        $token = $this->randomString(
88
            $settings->characters,
89
            $settings->length[0],
90
            $settings->length[1]
91
        );
92
93
        // save token in memory
94
        $this->pushInMemory($settings->sessionKey, $token);
95
96
        $image = $this->drawImage(
97
            $token,
98
            $settings->width,
99
            $settings->height,
100
            $settings->foregroundColors,
101
            $settings->backgroundColor,
102
            $settings->letterSpacing,
103
            $settings->fontSize,
104
            $settings->fontFamily
105
        );
106
107
        return $image;
108
    }
109
110
    /**
111
     * Generate captcha.
112
     *
113
     * @return mixed
114
     */
115
    public function generate()
116
    {
117
        return View::make(
0 ignored issues
show
Bug Best Practice introduced by
The expression return Illuminate\Suppor...('captcha.validator'))) returns the type Illuminate\Contracts\View\View which is incompatible with the return type mandated by Shetabit\Captcha\Contrac...erInterface::generate() of string.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
118
            'captchaSimpleDriver::captcha',
119
            [
120
                'routeName' => $this->settings->route,
121
                'errorsName' => config('captcha.validator'),
122
            ]
123
        );
124
    }
125
126
    /**
127
     * Verify token.
128
     *
129
     * @param null|$token
130
     * @return bool
131
     */
132
    public function verify($token = null)
133
    {
134
        $storedToken = $this->pullFromMemory($this->settings->sessionKey);
135
136
        if (empty($this->settings->sensitive)) {
137
            $storedToken = mb_strtolower($storedToken);
138
            $token = mb_strtolower($token);
139
        }
140
141
        return $token == $storedToken;
142
    }
143
144
    /**
145
     * Save token in memory.
146
     *
147
     * @param $key
148
     * @param $value
149
     * @return $this
150
     */
151
    protected function pushInMemory($key, $value)
152
    {
153
        session()->put($key, $value);
154
155
        return $this;
156
    }
157
158
    /**
159
     * Retrieve token from memory.
160
     *
161
     * @return mixed
162
     */
163
    protected function pullFromMemory($key)
164
    {
165
        $value = session()->pull($key);
166
167
        if (! empty($value)) {
168
            session()->forget($key);
169
        }
170
171
        return $value;
172
    }
173
174
    /**
175
     * Create new canvas.
176
     *
177
     * @param $width
178
     * @param $height
179
     * @return resource
180
     */
181
    protected function canvas($width, $height)
182
    {
183
        $canvas = imagecreatetruecolor($width, $height);
184
185
        return $canvas;
186
    }
187
188
    /**
189
     * Generate image
190
     *
191
     * @param $token
192
     * @param $width
193
     * @param $height
194
     * @param $foregroundColors
195
     * @param $backgroundColor
196
     * @param $letterSpacing
197
     * @param $fontSize
198
     * @param $fontFamily
199
     * @return false|string
200
     */
201
    protected function drawImage(
202
        $token,
203
        $width,
204
        $height,
205
        $foregroundColors,
206
        $backgroundColor,
207
        $letterSpacing,
208
        $fontSize,
209
        $fontFamily
210
    ) {
211
        $canvas = $this->canvas($width, $height);
212
213
        $this->fillWithColor($canvas, $backgroundColor);
214
215
        $offsetX = ($width - strlen($token) * ($letterSpacing + $fontSize * 0.66)) / 2;
216
        $offsetY = ceil(($height) / 1.5);
217
218
        // write token
219
        for ($i = 0; $i < strlen($token); $i++) {
220
            $randomForegroundColor = $foregroundColors[mt_rand(0, count($foregroundColors) - 1)];
221
            imagettftext(
222
                $canvas,
223
                $this->settings->fontSize,
224
                ceil(mt_rand(0,10)),
225
                $offsetX,
0 ignored issues
show
Bug introduced by
$offsetX of type double is incompatible with the type integer expected by parameter $x of imagettftext(). ( Ignorable by Annotation )

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

225
                /** @scrutinizer ignore-type */ $offsetX,
Loading history...
226
                $offsetY,
0 ignored issues
show
Bug introduced by
$offsetY of type double is incompatible with the type integer expected by parameter $y of imagettftext(). ( Ignorable by Annotation )

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

226
                /** @scrutinizer ignore-type */ $offsetY,
Loading history...
227
                $this->prepareColor($canvas, $randomForegroundColor),
228
                $fontFamily,
229
                $token[$i]
230
            );
231
            $offsetX += ceil($fontSize * 0.66) + $letterSpacing;
232
        }
233
234
        //Scratches foreground
235
        for ($i = 0; $i < $this->settings->scratches[0]; $i++) {
236
            $randomForegroundColor = $foregroundColors[mt_rand(0, count($foregroundColors) - 1)];
237
238
            $this->drawScratch($canvas, $width, $height, $randomForegroundColor);
239
        }
240
241
        //Scratches background
242
        for ($i = 0; $i < $this->settings->scratches[1]; $i++) {
243
            $this->drawScratch($canvas, $width, $height, $backgroundColor);
244
        }
245
246
        ob_start();
247
        imagepng($canvas);
248
        $content = ob_get_contents();
249
        ob_end_clean();
250
251
        imagedestroy($canvas);
252
253
        return $content;
254
    }
255
256
    /**
257
     * Fill canvas with the given color
258
     *
259
     * @param $canvas
260
     * @param $color
261
     * @return $this
262
     */
263
    protected function fillWithColor($canvas, $color)
264
    {
265
        $fillColor = $this->prepareColor($canvas, $color);
266
267
        imagefill($canvas, 0, 0, $fillColor);
268
269
        return $this;
270
    }
271
272
    /**
273
     * Draw scratches
274
     *
275
     * @param $img
276
     * @param $imageWidth
277
     * @param $imageHeight
278
     * @param $hex
279
     */
280
    private function drawScratch($img, $imageWidth, $imageHeight, $hex)
281
    {
282
        $rgb = $this->hexToRgb($hex);
283
284
        imageline(
285
            $img,
286
            mt_rand(0, floor($imageWidth / 2)),
287
            mt_rand(1, $imageHeight),
288
            mt_rand(floor($imageWidth / 2), $imageWidth),
289
            mt_rand(1, $imageHeight),
290
            imagecolorallocate($img, $rgb['red'], $rgb['green'], $rgb['blue'])
0 ignored issues
show
Bug introduced by
It seems like $rgb['blue'] can also be of type double; however, parameter $blue of imagecolorallocate() does only seem to accept integer, 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

290
            imagecolorallocate($img, $rgb['red'], $rgb['green'], /** @scrutinizer ignore-type */ $rgb['blue'])
Loading history...
Bug introduced by
It seems like $rgb['green'] can also be of type double; however, parameter $green of imagecolorallocate() does only seem to accept integer, 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

290
            imagecolorallocate($img, $rgb['red'], /** @scrutinizer ignore-type */ $rgb['green'], $rgb['blue'])
Loading history...
Bug introduced by
It seems like $rgb['red'] can also be of type double; however, parameter $red of imagecolorallocate() does only seem to accept integer, 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

290
            imagecolorallocate($img, /** @scrutinizer ignore-type */ $rgb['red'], $rgb['green'], $rgb['blue'])
Loading history...
291
        );
292
    }
293
294
    /**
295
     * prepare a color
296
     *
297
     * @param $canvas
298
     * @param $color
299
     * @return int
300
     */
301
    private function prepareColor($canvas, $hexColor)
302
    {
303
        $rgbColor = $this->hexToRGB($hexColor);
304
305
        return imagecolorallocate(
306
            $canvas,
307
            $rgbColor['red'],
0 ignored issues
show
Bug introduced by
It seems like $rgbColor['red'] can also be of type double; however, parameter $red of imagecolorallocate() does only seem to accept integer, 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

307
            /** @scrutinizer ignore-type */ $rgbColor['red'],
Loading history...
308
            $rgbColor['green'],
0 ignored issues
show
Bug introduced by
It seems like $rgbColor['green'] can also be of type double; however, parameter $green of imagecolorallocate() does only seem to accept integer, 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

308
            /** @scrutinizer ignore-type */ $rgbColor['green'],
Loading history...
309
            $rgbColor['blue']
0 ignored issues
show
Bug introduced by
It seems like $rgbColor['blue'] can also be of type double; however, parameter $blue of imagecolorallocate() does only seem to accept integer, 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

309
            /** @scrutinizer ignore-type */ $rgbColor['blue']
Loading history...
310
        );
311
    }
312
313
    /**
314
     * Create a random string
315
     *
316
     * @param string $characters
317
     * @param int $minLength
318
     * @param int $maxLength
319
     * @return string
320
     */
321
    private function randomString($characters = '123456789' , $minLength = 4, $maxLength = 6)
322
    {
323
        $randomLength = mt_rand($minLength, $maxLength);
324
        $string = [];
325
326
        for ($i = 0; $i < $randomLength; $i++) {
327
            $string[] = $characters[mt_rand(1, mb_strlen($characters) - 1)];
328
        }
329
330
        $string = implode($string);
331
332
        return $string;
333
    }
334
335
    /**
336
     * Convert hex color to rgb
337
     *
338
     * @param $hexColor
339
     * @return array
340
     */
341
    private function hexToRGB($hexColor)
342
    {
343
        $hexColor = ($hexColor[0] == '#') ? substr($hexColor, 1) : $hexColor;
344
345
        // Separate colors
346
        switch(strlen($hexColor)) {
347
            case 6:
348
                $red = $hexColor[0].$hexColor[1];
349
                $green = $hexColor[2].$hexColor[3];
350
                $blue = $hexColor[4].$hexColor[5];
351
                break;
352
            case 3:
353
                $red = str_repeat($hexColor[0],2);
354
                $green = str_repeat($hexColor[1],2);
355
                $blue = str_repeat($hexColor[2],2);
356
                break;
357
            default:
358
                $red = $green = $blue = 0;
359
                break;
360
        }
361
362
        // Convert hex to dec
363
        $red = hexdec($red);
364
        $green = hexdec($green);
365
        $blue = hexdec($blue);
366
367
        return [
368
            'red' => $red,
369
            'green' => $green,
370
            'blue' => $blue,
371
        ];
372
    }
373
}
374