PhpCaptcha   F
last analyzed

Complexity

Total Complexity 60

Size/Duplication

Total Lines 426
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 157
dl 0
loc 426
rs 3.6
c 1
b 0
f 0
wmc 60

22 Methods

Rating   Name   Duplication   Size   Complexity  
A CaseInsensitive() 0 3 1
A UseColour() 0 3 1
A SetMaxFontSize() 0 3 1
A SetMinFontSize() 0 3 1
A SetBackgroundImages() 0 3 1
A CalculateSpacing() 0 3 1
A SetHeight() 0 5 2
A Validate() 0 14 4
A SetFileType() 0 7 2
B Create() 0 54 11
A SetWidth() 0 7 2
A DisplayShadow() 0 3 1
A SetNumChars() 0 4 1
A SetNumLines() 0 3 1
A DrawLines() 0 13 3
B WriteFile() 0 21 7
B DrawCharacters() 0 51 6
B SetCharSet() 0 30 7
A DrawOwnerText() 0 17 1
A __construct() 0 21 1
A SetOwnerText() 0 3 1
A GenerateCode() 0 21 4

How to fix   Complexity   

Complex Class

Complex classes like PhpCaptcha often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PhpCaptcha, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace XoopsModules\Extgallery;
4
5
/***************************************************************/
6
/* PhpCaptcha - A visual and audio CAPTCHA generation library
7
8
   Software License Agreement (BSD License)
9
10
   Copyright (C) 2005-2006, Edward Eliot.
11
   All rights reserved.
12
13
   Redistribution and use in source and binary forms, with or without
14
   modification, are permitted provided that the following conditions are met:
15
16
      * Redistributions of source code must retain the above copyright
17
        notice, this list of conditions and the following disclaimer.
18
      * Redistributions in binary form must reproduce the above copyright
19
        notice, this list of conditions and the following disclaimer in the
20
        documentation and/or other materials provided with the distribution.
21
      * Neither the name of Edward Eliot nor the names of its contributors
22
        may be used to endorse or promote products derived from this software
23
        without specific prior written permission of Edward Eliot.
24
25
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND ANY
26
   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
29
   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30
   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31
   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
32
   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33
   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
36
   Last Updated:  18th April 2006                               */
37
/***************************************************************/
38
39
40
41
/************************ Documentation ************************/
42
43
/*
44
45
Documentation is available at http://www.ejeliot.com/pages/2
46
47
*/
48
49
/************************ Default Options **********************/
50
51
// start a PHP session - this class uses sessions to store the generated
52
// code. Comment out if you are calling already from your application
53
//session_start();
54
55
// class defaults - change to effect globally
56
57
\define('CAPTCHA_SESSION_ID', 'php_captcha');
58
\define('CAPTCHA_WIDTH', 200); // max 500
59
\define('CAPTCHA_HEIGHT', 50); // max 200
60
\define('CAPTCHA_NUM_CHARS', 5);
61
\define('CAPTCHA_NUM_LINES', 70);
62
\define('CAPTCHA_CHAR_SHADOW', false);
63
\define('CAPTCHA_OWNER_TEXT', '');
64
\define('CAPTCHA_CHAR_SET', ''); // defaults to A-Z
65
\define('CAPTCHA_CASE_INSENSITIVE', true);
66
\define('CAPTCHA_BACKGROUND_IMAGES', '');
67
\define('CAPTCHA_MIN_FONT_SIZE', 18);
68
\define('CAPTCHA_MAX_FONT_SIZE', 26);
69
\define('CAPTCHA_USE_COLOUR', false);
70
\define('CAPTCHA_FILE_TYPE', 'jpeg');
71
\define('CAPTCHA_FLITE_PATH', '/usr/bin/flite');
72
\define('CAPTCHA_AUDIO_PATH', '/tmp/'); // must be writeable by PHP process
73
74
/************************ End Default Options **********************/
75
// don't edit below this line (unless you want to change the class!)
76
77
class PhpCaptcha
78
{
79
    public $oImage;
80
    public $aFonts;
81
    public $iWidth;
82
    public $iHeight;
83
    public $iNumChars;
84
    public $iNumLines;
85
    public $iSpacing;
86
    public $bCharShadow;
87
    public $sOwnerText;
88
    public $aCharSet;
89
    public $bCaseInsensitive;
90
    public $vBackgroundImages;
91
    public $iMinFontSize;
92
    public $iMaxFontSize;
93
    public $bUseColour;
94
    public $sFileType;
95
    public $sCode = '';
96
97
    /**
98
     * PhpCaptcha constructor.
99
     * @param     $aFonts
100
     * @param int $iWidth
101
     * @param int $iHeight
102
     */
103
    public function __construct(
104
        $aFonts, // array of TrueType fonts to use - specify full path
105
        $iWidth = CAPTCHA_WIDTH, // width of image
106
        $iHeight = CAPTCHA_HEIGHT // height of image
107
    )
108
    {
109
        // get parameters
110
        $this->aFonts = $aFonts;
111
        $this->SetNumChars(CAPTCHA_NUM_CHARS);
112
        $this->SetNumLines(CAPTCHA_NUM_LINES);
113
        $this->DisplayShadow(CAPTCHA_CHAR_SHADOW);
114
        $this->SetOwnerText(CAPTCHA_OWNER_TEXT);
115
        $this->SetCharSet(CAPTCHA_CHAR_SET);
116
        $this->CaseInsensitive(CAPTCHA_CASE_INSENSITIVE);
117
        $this->SetBackgroundImages(CAPTCHA_BACKGROUND_IMAGES);
118
        $this->SetMinFontSize(CAPTCHA_MIN_FONT_SIZE);
119
        $this->SetMaxFontSize(CAPTCHA_MAX_FONT_SIZE);
120
        $this->UseColour(CAPTCHA_USE_COLOUR);
121
        $this->SetFileType(CAPTCHA_FILE_TYPE);
122
        $this->SetWidth($iWidth);
123
        $this->SetHeight($iHeight);
124
    }
125
126
    public function CalculateSpacing()
127
    {
128
        $this->iSpacing = (int)($this->iWidth / $this->iNumChars);
129
    }
130
131
    /**
132
     * @param $iWidth
133
     */
134
    public function SetWidth($iWidth)
135
    {
136
        $this->iWidth = $iWidth;
137
        if ($this->iWidth > 500) {
138
            $this->iWidth = 500;
139
        } // to prevent perfomance impact
140
        $this->CalculateSpacing();
141
    }
142
143
    /**
144
     * @param $iHeight
145
     */
146
    public function SetHeight($iHeight)
147
    {
148
        $this->iHeight = $iHeight;
149
        if ($this->iHeight > 200) {
150
            $this->iHeight = 200;
151
        } // to prevent performance impact
152
    }
153
154
    /**
155
     * @param $iNumChars
156
     */
157
    public function SetNumChars($iNumChars)
158
    {
159
        $this->iNumChars = $iNumChars;
160
        $this->CalculateSpacing();
161
    }
162
163
    /**
164
     * @param $iNumLines
165
     */
166
    public function SetNumLines($iNumLines)
167
    {
168
        $this->iNumLines = $iNumLines;
169
    }
170
171
    /**
172
     * @param $bCharShadow
173
     */
174
    public function DisplayShadow($bCharShadow)
175
    {
176
        $this->bCharShadow = $bCharShadow;
177
    }
178
179
    /**
180
     * @param $sOwnerText
181
     */
182
    public function SetOwnerText($sOwnerText)
183
    {
184
        $this->sOwnerText = $sOwnerText;
185
    }
186
187
    /**
188
     * @param $vCharSet
189
     */
190
    public function SetCharSet($vCharSet)
191
    {
192
        // check for input type
193
        if (\is_array($vCharSet)) {
194
            $this->aCharSet = $vCharSet;
195
        } else {
196
            if ('' != $vCharSet) {
197
                // split items on commas
198
                $aCharSet = \explode(',', $vCharSet);
199
200
                // initialise array
201
                $this->aCharSet = [];
202
203
                // loop through items
204
                foreach ($aCharSet as $sCurrentItem) {
205
                    // a range should have 3 characters, otherwise is normal character
206
                    if (3 == mb_strlen($sCurrentItem)) {
207
                        // split on range character
208
                        $aRange = \explode('-', $sCurrentItem);
209
210
                        // check for valid range
211
                        if (2 == \count($aRange) && $aRange[0] < $aRange[1]) {
212
                            // create array of characters from range
213
                            $aRange = \range($aRange[0], $aRange[1]);
214
215
                            // add to charset array
216
                            $this->aCharSet = \array_merge($this->aCharSet, $aRange);
217
                        }
218
                    } else {
219
                        $this->aCharSet[] = $sCurrentItem;
220
                    }
221
                }
222
            }
223
        }
224
    }
225
226
    /**
227
     * @param $bCaseInsensitive
228
     */
229
    public function CaseInsensitive($bCaseInsensitive)
230
    {
231
        $this->bCaseInsensitive = $bCaseInsensitive;
232
    }
233
234
    /**
235
     * @param $vBackgroundImages
236
     */
237
    public function SetBackgroundImages($vBackgroundImages)
238
    {
239
        $this->vBackgroundImages = $vBackgroundImages;
240
    }
241
242
    /**
243
     * @param $iMinFontSize
244
     */
245
    public function SetMinFontSize($iMinFontSize)
246
    {
247
        $this->iMinFontSize = $iMinFontSize;
248
    }
249
250
    /**
251
     * @param $iMaxFontSize
252
     */
253
    public function SetMaxFontSize($iMaxFontSize)
254
    {
255
        $this->iMaxFontSize = $iMaxFontSize;
256
    }
257
258
    /**
259
     * @param $bUseColour
260
     */
261
    public function UseColour($bUseColour)
262
    {
263
        $this->bUseColour = $bUseColour;
264
    }
265
266
    /**
267
     * @param $sFileType
268
     */
269
    public function SetFileType($sFileType)
270
    {
271
        // check for valid file type
272
        if (\in_array($sFileType, ['gif', 'png', 'jpeg'])) {
273
            $this->sFileType = $sFileType;
274
        } else {
275
            $this->sFileType = 'jpeg';
276
        }
277
    }
278
279
    public function DrawLines()
280
    {
281
        for ($i = 0; $i < $this->iNumLines; ++$i) {
282
            // allocate colour
283
            if ($this->bUseColour) {
284
                $iLineColour = \imagecolorallocate($this->oImage, \mt_rand(100, 250), \mt_rand(100, 250), \mt_rand(100, 250));
285
            } else {
286
                $iRandColour = \mt_rand(100, 250);
287
                $iLineColour = \imagecolorallocate($this->oImage, $iRandColour, $iRandColour, $iRandColour);
288
            }
289
290
            // draw line
291
            \imageline($this->oImage, \mt_rand(0, $this->iWidth), \mt_rand(0, $this->iHeight), \mt_rand(0, $this->iWidth), \mt_rand(0, $this->iHeight), $iLineColour);
292
        }
293
    }
294
295
    public function DrawOwnerText()
296
    {
297
        // allocate owner text colour
298
        $iBlack = \imagecolorallocate($this->oImage, 0, 0, 0);
299
        // get height of selected font
300
        $iOwnerTextHeight = \imagefontheight(2);
301
        // calculate overall height
302
        $iLineHeight = $this->iHeight - $iOwnerTextHeight - 4;
303
304
        // draw line above text to separate from CAPTCHA
305
        \imageline($this->oImage, 0, $iLineHeight, $this->iWidth, $iLineHeight, $iBlack);
306
307
        // write owner text
308
        \imagestring($this->oImage, 2, 3, $this->iHeight - $iOwnerTextHeight - 3, $this->sOwnerText, $iBlack);
309
310
        // reduce available height for drawing CAPTCHA
311
        $this->iHeight = $this->iHeight - $iOwnerTextHeight - 5;
312
    }
313
314
    public function GenerateCode()
315
    {
316
        // reset code
317
        $this->sCode = '';
318
319
        // loop through and generate the code letter by letter
320
        for ($i = 0; $i < $this->iNumChars; ++$i) {
321
            if (\count($this->aCharSet) > 0) {
322
                // select random character and add to code string
323
                $this->sCode .= $this->aCharSet[\array_rand($this->aCharSet)];
324
            } else {
325
                // select random character and add to code string
326
                $this->sCode .= \chr(\mt_rand(65, 90));
327
            }
328
        }
329
330
        // save code in session variable
331
        if ($this->bCaseInsensitive) {
332
            $_SESSION[CAPTCHA_SESSION_ID] = mb_strtoupper($this->sCode);
333
        } else {
334
            $_SESSION[CAPTCHA_SESSION_ID] = $this->sCode;
335
        }
336
    }
337
338
    public function DrawCharacters()
339
    {
340
        $iShadowColour = '';
341
        // loop through and write out selected number of characters
342
        for ($i = 0, $iMax = mb_strlen($this->sCode); $i < $iMax; ++$i) {
343
            // select random font
344
            $sCurrentFont = $this->aFonts[\array_rand($this->aFonts)];
345
346
            // select random colour
347
            if ($this->bUseColour) {
348
                $iTextColour = \imagecolorallocate($this->oImage, \mt_rand(0, 100), \mt_rand(0, 100), \mt_rand(0, 100));
349
350
                if ($this->bCharShadow) {
351
                    // shadow colour
352
                    $iShadowColour = \imagecolorallocate($this->oImage, \mt_rand(0, 100), \mt_rand(0, 100), \mt_rand(0, 100));
353
                }
354
            } else {
355
                $iRandColour = \mt_rand(0, 100);
356
                $iTextColour = \imagecolorallocate($this->oImage, $iRandColour, $iRandColour, $iRandColour);
357
358
                if ($this->bCharShadow) {
359
                    // shadow colour
360
                    $iRandColour   = \mt_rand(0, 100);
361
                    $iShadowColour = \imagecolorallocate($this->oImage, $iRandColour, $iRandColour, $iRandColour);
362
                }
363
            }
364
365
            // select random font size
366
            $iFontSize = \mt_rand($this->iMinFontSize, $this->iMaxFontSize);
367
368
            // select random angle
369
            $iAngle = \mt_rand(-30, 30);
370
371
            // get dimensions of character in selected font and text size
372
            $aCharDetails = \imageftbbox($iFontSize, $iAngle, $sCurrentFont, $this->sCode[$i], []);
373
374
            // calculate character starting coordinates
375
            $iX          = $this->iSpacing / 4 + $i * $this->iSpacing;
376
            $iCharHeight = $aCharDetails[2] - $aCharDetails[5];
377
            $iY          = $this->iHeight / 2 + $iCharHeight / 4;
378
379
            // write text to image
380
            \imagefttext($this->oImage, $iFontSize, $iAngle, $iX, $iY, $iTextColour, $sCurrentFont, $this->sCode[$i], []);
381
382
            if ($this->bCharShadow) {
383
                $iOffsetAngle = \mt_rand(-30, 30);
384
385
                $iRandOffsetX = \mt_rand(-5, 5);
386
                $iRandOffsetY = \mt_rand(-5, 5);
387
388
                \imagefttext($this->oImage, $iFontSize, $iOffsetAngle, $iX + $iRandOffsetX, $iY + $iRandOffsetY, $iShadowColour, $sCurrentFont, $this->sCode[$i], []);
389
            }
390
        }
391
    }
392
393
    /**
394
     * @param $sFilename
395
     */
396
    public function WriteFile($sFilename)
397
    {
398
        if ('' == $sFilename) {
399
            // tell browser that data is jpeg
400
            \header("Content-type: image/$this->sFileType");
401
        }
402
403
        switch ($this->sFileType) {
404
            case 'gif':
405
406
                '' != $sFilename ? \imagegif($this->oImage, $sFilename) : \imagegif($this->oImage);
407
408
                break;
409
            case 'png':
410
411
                '' != $sFilename ? \imagepng($this->oImage, $sFilename) : \imagepng($this->oImage);
412
413
                break;
414
            default:
415
416
                '' != $sFilename ? \imagejpeg($this->oImage, $sFilename) : \imagejpeg($this->oImage);
417
        }
418
    }
419
420
    /**
421
     * @param string $sFilename
422
     *
423
     * @return bool
424
     */
425
    public function Create($sFilename = '')
426
    {
427
        // check for required gd functions
428
        if (!\function_exists('imagecreate') || !\function_exists("image$this->sFileType")
429
            || ('' != $this->vBackgroundImages && !\function_exists('imagecreatetruecolor'))) {
430
            return false;
431
        }
432
433
        // get background image if specified and copy to CAPTCHA
434
        if (\is_array($this->vBackgroundImages) || '' != $this->vBackgroundImages) {
435
            // create new image
436
            $this->oImage = \imagecreatetruecolor($this->iWidth, $this->iHeight);
437
438
            // create background image
439
            if (\is_array($this->vBackgroundImages)) {
0 ignored issues
show
introduced by
The condition is_array($this->vBackgroundImages) is always false.
Loading history...
440
                $iRandImage       = \array_rand($this->vBackgroundImages);
441
                $oBackgroundImage = \imagecreatefromjpeg($this->vBackgroundImages[$iRandImage]);
442
            } else {
443
                $oBackgroundImage = \imagecreatefromjpeg($this->vBackgroundImages);
444
            }
445
446
            // copy background image
447
            \imagecopy($this->oImage, $oBackgroundImage, 0, 0, 0, 0, $this->iWidth, $this->iHeight);
448
449
            // free memory used to create background image
450
            \imagedestroy($oBackgroundImage);
451
        } else {
452
            // create new image
453
            $this->oImage = \imagecreate($this->iWidth, $this->iHeight);
454
        }
455
456
        // allocate white background colour
457
        \imagecolorallocate($this->oImage, 255, 255, 255);
458
459
        // check for owner text
460
        if ('' != $this->sOwnerText) {
461
            $this->DrawOwnerText();
462
        }
463
464
        // check for background image before drawing lines
465
        if (!\is_array($this->vBackgroundImages) && '' == $this->vBackgroundImages) {
0 ignored issues
show
introduced by
The condition is_array($this->vBackgroundImages) is always false.
Loading history...
466
            $this->DrawLines();
467
        }
468
469
        $this->GenerateCode();
470
        $this->DrawCharacters();
471
472
        // write out image to file or browser
473
        $this->WriteFile($sFilename);
474
475
        // free memory used in creating image
476
        \imagedestroy($this->oImage);
477
478
        return true;
479
    }
480
481
    // call this method statically
482
483
    /**
484
     * @param      $sUserCode
485
     * @param bool $bCaseInsensitive
486
     *
487
     * @return bool
488
     */
489
    public static function Validate($sUserCode, $bCaseInsensitive = true)
490
    {
491
        if ($bCaseInsensitive) {
492
            $sUserCode = mb_strtoupper($sUserCode);
493
        }
494
495
        if (!empty($_SESSION[CAPTCHA_SESSION_ID]) && $sUserCode == $_SESSION[CAPTCHA_SESSION_ID]) {
496
            // clear to prevent re-use
497
            unset($_SESSION[CAPTCHA_SESSION_ID]);
498
499
            return true;
500
        }
501
502
        return false;
503
    }
504
}
505