SimpleDriver::drawImage()   A
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 53
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 27
dl 0
loc 53
rs 9.488
c 0
b 0
f 0
cc 4
nc 8
nop 8

How to fix   Long Method    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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