Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like hn_captcha 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 hn_captcha, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 90 | class hn_captcha |
||
| 91 | { |
||
| 92 | |||
| 93 | //////////////////////////////// |
||
| 94 | // |
||
| 95 | // PUBLIC PARAMS |
||
| 96 | // |
||
| 97 | |||
| 98 | /** |
||
| 99 | * @shortdesc Absolute path to a Tempfolder (with trailing slash!). This must be writeable for PHP and also accessible via HTTP, because the image will be stored there. |
||
| 100 | * @type string |
||
| 101 | * @public |
||
| 102 | * |
||
| 103 | **/ |
||
| 104 | var $tempfolder; |
||
|
|
|||
| 105 | |||
| 106 | /** |
||
| 107 | * @shortdesc Absolute path to folder with TrueTypeFonts (with trailing slash!). This must be readable by PHP. |
||
| 108 | * @type string |
||
| 109 | * @public |
||
| 110 | * |
||
| 111 | **/ |
||
| 112 | var $TTF_folder; |
||
| 113 | |||
| 114 | /** |
||
| 115 | * @shortdesc A List with available TrueTypeFonts for random char-creation. |
||
| 116 | * @type mixed[array|string] |
||
| 117 | * @public |
||
| 118 | * |
||
| 119 | **/ |
||
| 120 | var $TTF_RANGE = array('COMIC.TTF','JACOBITE.TTF','LYDIAN.TTF','MREARL.TTF','RUBBERSTAMP.TTF','ZINJARON.TTF'); |
||
| 121 | |||
| 122 | /** |
||
| 123 | * @shortdesc How many chars the generated text should have |
||
| 124 | * @type integer |
||
| 125 | * @public |
||
| 126 | * |
||
| 127 | **/ |
||
| 128 | var $chars = 6; |
||
| 129 | |||
| 130 | /** |
||
| 131 | * @shortdesc The minimum size a Char should have |
||
| 132 | * @type integer |
||
| 133 | * @public |
||
| 134 | * |
||
| 135 | **/ |
||
| 136 | var $minsize = 20; |
||
| 137 | |||
| 138 | /** |
||
| 139 | * @shortdesc The maximum size a Char can have |
||
| 140 | * @type integer |
||
| 141 | * @public |
||
| 142 | * |
||
| 143 | **/ |
||
| 144 | var $maxsize = 40; |
||
| 145 | |||
| 146 | /** |
||
| 147 | * @shortdesc The maximum degrees a Char should be rotated. Set it to 30 means a random rotation between -30 and 30. |
||
| 148 | * @type integer |
||
| 149 | * @public |
||
| 150 | * |
||
| 151 | **/ |
||
| 152 | var $maxrotation = 30; |
||
| 153 | |||
| 154 | /** |
||
| 155 | * @shortdesc Background noise On/Off (if is Off, a grid will be created) |
||
| 156 | * @type boolean |
||
| 157 | * @public |
||
| 158 | * |
||
| 159 | **/ |
||
| 160 | var $noise = TRUE; |
||
| 161 | |||
| 162 | /** |
||
| 163 | * @shortdesc This will only use the 216 websafe color pallette for the image. |
||
| 164 | * @type boolean |
||
| 165 | * @public |
||
| 166 | * |
||
| 167 | **/ |
||
| 168 | var $websafecolors = FALSE; |
||
| 169 | |||
| 170 | /** |
||
| 171 | * @shortdesc Switches language, available are 'en' and 'de'. You can easily add more. Look in CONSTRUCTOR. |
||
| 172 | * @type string |
||
| 173 | * @public |
||
| 174 | * |
||
| 175 | **/ |
||
| 176 | var $lang = "de"; |
||
| 177 | |||
| 178 | /** |
||
| 179 | * @shortdesc If a user has reached this number of try's without success, he will moved to the $badguys_url |
||
| 180 | * @type integer |
||
| 181 | * @public |
||
| 182 | * |
||
| 183 | **/ |
||
| 184 | var $maxtry = 3; |
||
| 185 | |||
| 186 | /** |
||
| 187 | * @shortdesc Gives the user the possibility to generate a new captcha-image. |
||
| 188 | * @type boolean |
||
| 189 | * @public |
||
| 190 | * |
||
| 191 | **/ |
||
| 192 | var $refreshlink = TRUE; |
||
| 193 | |||
| 194 | /** |
||
| 195 | * @shortdesc If a user has reached his maximum try's, he will located to this url. |
||
| 196 | * @type boolean |
||
| 197 | * @public |
||
| 198 | * |
||
| 199 | **/ |
||
| 200 | var $badguys_url = "/"; |
||
| 201 | |||
| 202 | /** |
||
| 203 | * Number between 1 and 32 |
||
| 204 | * |
||
| 205 | * @shortdesc Defines the position of 'current try number' in (32-char-length)-string generated by function get_try() |
||
| 206 | * @type integer |
||
| 207 | * @public |
||
| 208 | * |
||
| 209 | **/ |
||
| 210 | var $secretposition = 21; |
||
| 211 | |||
| 212 | /** |
||
| 213 | * @shortdesc The string is used to generate the md5-key. |
||
| 214 | * @type string |
||
| 215 | * @public |
||
| 216 | * |
||
| 217 | **/ |
||
| 218 | var $secretstring = "This is a very secret string. Nobody should know it, =:)"; |
||
| 219 | |||
| 220 | /** |
||
| 221 | * @shortdesc Outputs configuration values for testing |
||
| 222 | * @type boolean |
||
| 223 | * @public |
||
| 224 | * |
||
| 225 | **/ |
||
| 226 | var $debug = FALSE; |
||
| 227 | |||
| 228 | |||
| 229 | |||
| 230 | //////////////////////////////// |
||
| 231 | // |
||
| 232 | // PRIVATE PARAMS |
||
| 233 | // |
||
| 234 | |||
| 235 | /** @private **/ |
||
| 236 | var $lx; // width of picture |
||
| 237 | /** @private **/ |
||
| 238 | var $ly; // height of picture |
||
| 239 | /** @private **/ |
||
| 240 | var $jpegquality = 80; // image quality |
||
| 241 | /** @private **/ |
||
| 242 | var $noisefactor = 9; // this will multiplyed with number of chars |
||
| 243 | /** @private **/ |
||
| 244 | var $nb_noise; // number of background-noise-characters |
||
| 245 | /** @private **/ |
||
| 246 | var $TTF_file; // holds the current selected TrueTypeFont |
||
| 247 | /** @private **/ |
||
| 248 | var $msg1; |
||
| 249 | /** @private **/ |
||
| 250 | var $msg2; |
||
| 251 | /** @private **/ |
||
| 252 | var $buttontext; |
||
| 253 | /** @private **/ |
||
| 254 | var $refreshbuttontext; |
||
| 255 | /** @private **/ |
||
| 256 | var $public_K; |
||
| 257 | /** @private **/ |
||
| 258 | var $private_K; |
||
| 259 | /** @private **/ |
||
| 260 | var $key; // md5-key |
||
| 261 | /** @private **/ |
||
| 262 | var $public_key; // public key |
||
| 263 | /** @private **/ |
||
| 264 | var $filename; // filename of captcha picture |
||
| 265 | /** @private **/ |
||
| 266 | var $gd_version; // holds the Version Number of GD-Library |
||
| 267 | /** @private **/ |
||
| 268 | var $QUERY_STRING; // keeps the ($_GET) Querystring of the original Request |
||
| 269 | /** @private **/ |
||
| 270 | var $current_try = 0; |
||
| 271 | /** @private **/ |
||
| 272 | var $r; |
||
| 273 | /** @private **/ |
||
| 274 | var $g; |
||
| 275 | /** @private **/ |
||
| 276 | var $b; |
||
| 277 | |||
| 278 | private static $validated = array(); |
||
| 279 | |||
| 280 | |||
| 281 | //////////////////////////////// |
||
| 282 | // |
||
| 283 | // CONSTRUCTOR |
||
| 284 | // |
||
| 285 | |||
| 286 | /** |
||
| 287 | * @shortdesc Extracts the config array and generate needed params. |
||
| 288 | * @private |
||
| 289 | * @type void |
||
| 290 | * @return nothing |
||
| 291 | * |
||
| 292 | **/ |
||
| 293 | function __construct($config,$secure=TRUE) |
||
| 294 | { |
||
| 295 | |||
| 296 | // Test for GD-Library(-Version) |
||
| 297 | $this->gd_version = $this->get_gd_version(); |
||
| 298 | if($this->gd_version == 0) die("There is no GD-Library-Support enabled. The Captcha-Class cannot be used!"); |
||
| 299 | if($this->debug) echo "\n<br>-Captcha-Debug: The available GD-Library has major version ".$this->gd_version; |
||
| 300 | |||
| 301 | |||
| 302 | // Hackprevention |
||
| 303 | if( |
||
| 304 | (isset($_GET['maxtry']) || isset($_POST['maxtry']) || isset($_COOKIE['maxtry'])) |
||
| 305 | || |
||
| 306 | (isset($_GET['debug']) || isset($_POST['debug']) || isset($_COOKIE['debug'])) |
||
| 307 | || |
||
| 308 | (isset($_GET['captcharefresh']) || isset($_COOKIE['captcharefresh'])) |
||
| 309 | || |
||
| 310 | (isset($_POST['captcharefresh']) && isset($_POST['private_key'])) |
||
| 311 | ) |
||
| 312 | { |
||
| 313 | if($this->debug) echo "\n<br>-Captcha-Debug: Buuh. You are a bad guy!"; |
||
| 314 | if(isset($this->badguys_url) && !headers_sent()) header('location: '.$this->badguys_url); |
||
| 315 | else die('Sorry.'); |
||
| 316 | } |
||
| 317 | |||
| 318 | |||
| 319 | // extracts config array |
||
| 320 | if(is_array($config)) |
||
| 321 | { |
||
| 322 | if($secure && strcmp('4.2.0', phpversion()) < 0) |
||
| 323 | { |
||
| 324 | if($this->debug) echo "\n<br>-Captcha-Debug: Extracts Config-Array in secure-mode!"; |
||
| 325 | $valid = get_class_vars(get_class($this)); |
||
| 326 | foreach($config as $k=>$v) |
||
| 327 | { |
||
| 328 | if(array_key_exists($k,$valid)) $this->$k = $v; |
||
| 329 | } |
||
| 330 | } |
||
| 331 | else |
||
| 332 | { |
||
| 333 | if($this->debug) echo "\n<br>-Captcha-Debug: Extracts Config-Array in unsecure-mode!"; |
||
| 334 | foreach($config as $k=>$v) $this->$k = $v; |
||
| 335 | } |
||
| 336 | } |
||
| 337 | |||
| 338 | |||
| 339 | // check vars for maxtry, secretposition and min-max-size |
||
| 340 | $this->maxtry = ($this->maxtry > 9 || $this->maxtry < 1) ? 3 : $this->maxtry; |
||
| 341 | $this->secretposition = ($this->secretposition > 32 || $this->secretposition < 1) ? $this->maxtry : $this->secretposition; |
||
| 342 | if($this->minsize > $this->maxsize) |
||
| 343 | { |
||
| 344 | $temp = $this->minsize; |
||
| 345 | $this->minsize = $this->maxsize; |
||
| 346 | $this->maxsize = $temp; |
||
| 347 | if($this->debug) echo "<br>-Captcha-Debug: Arrghh! What do you think I mean with min and max? Switch minsize with maxsize."; |
||
| 348 | } |
||
| 349 | |||
| 350 | |||
| 351 | // check TrueTypeFonts |
||
| 352 | if(is_array($this->TTF_RANGE)) |
||
| 353 | { |
||
| 354 | if($this->debug) echo "\n<br>-Captcha-Debug: Check given TrueType-Array! (".count($this->TTF_RANGE).")"; |
||
| 355 | $temp = array(); |
||
| 356 | foreach($this->TTF_RANGE as $k=>$v) |
||
| 357 | { |
||
| 358 | if(is_readable($this->TTF_folder.$v)) $temp[] = $v; |
||
| 359 | } |
||
| 360 | $this->TTF_RANGE = $temp; |
||
| 361 | if($this->debug) echo "\n<br>-Captcha-Debug: Valid TrueType-files: (".count($this->TTF_RANGE).")"; |
||
| 362 | if(count($this->TTF_RANGE) < 1) die('No Truetypefont available for the CaptchaClass.'); |
||
| 363 | } |
||
| 364 | else |
||
| 365 | { |
||
| 366 | if($this->debug) echo "\n<br>-Captcha-Debug: Check given TrueType-File! (".$this->TTF_RANGE.")"; |
||
| 367 | if(!is_readable($this->TTF_folder.$this->TTF_RANGE)) die('No Truetypefont available for the CaptchaClass.'); |
||
| 368 | } |
||
| 369 | |||
| 370 | // select first TrueTypeFont |
||
| 371 | $this->change_TTF(); |
||
| 372 | if($this->debug) echo "\n<br>-Captcha-Debug: Set current TrueType-File: (".$this->TTF_file.")"; |
||
| 373 | |||
| 374 | |||
| 375 | // get number of noise-chars for background if is enabled |
||
| 376 | $this->nb_noise = $this->noise ? ($this->chars * $this->noisefactor) : 0; |
||
| 377 | if($this->debug) echo "\n<br>-Captcha-Debug: Set number of noise characters to: (".$this->nb_noise.")"; |
||
| 378 | |||
| 379 | |||
| 380 | // set dimension of image |
||
| 381 | $this->lx = ($this->chars + 1) * (int)(($this->maxsize + $this->minsize) / 1.5); |
||
| 382 | $this->ly = (int)(2.4 * $this->maxsize); |
||
| 383 | if($this->debug) echo "\n<br>-Captcha-Debug: Set image dimension to: (".$this->lx." x ".$this->ly.")"; |
||
| 384 | |||
| 385 | |||
| 386 | // set all messages |
||
| 387 | // (if you add a new language, you also want to add a line to the function "notvalid_msg()" at the end of the class!) |
||
| 388 | $this->messages = array( |
||
| 389 | 'de'=>array( |
||
| 390 | 'msg1'=>'Du mu�t die <b>'.$this->chars.' Zeichen</b> im Bild, (Zahlen von <b>0 - 9</b> und Buchstaben von <b>A - F</b>),<br>in das Feld eintragen und das Formular abschicken um den Download zu starten.', |
||
| 391 | 'msg2'=>'Ohje, das kann ich nicht lesen. Bitte, generiere mir eine ', |
||
| 392 | 'buttontext'=>'abschicken', |
||
| 393 | 'refreshbuttontext'=>'neue ID' |
||
| 394 | ), |
||
| 395 | 'en'=>array( |
||
| 396 | 'msg1'=>'You must read and type the <b>'.$this->chars.' chars</b> within <b>0..9</b> and <b>A..F</b>, and submit the form.', |
||
| 397 | 'msg2'=>'Oh no, I cannot read this. Please, generate a ', |
||
| 398 | 'buttontext'=>'submit', |
||
| 399 | 'refreshbuttontext'=>'new ID' |
||
| 400 | ) |
||
| 401 | ); |
||
| 402 | $this->msg1 = $this->messages[$this->lang]['msg1']; |
||
| 403 | $this->msg2 = $this->messages[$this->lang]['msg2']; |
||
| 404 | $this->buttontext = $this->messages[$this->lang]['buttontext']; |
||
| 405 | $this->refreshbuttontext = $this->messages[$this->lang]['refreshbuttontext']; |
||
| 406 | if($this->debug) echo "\n<br>-Captcha-Debug: Set messages to language: (".$this->lang.")"; |
||
| 407 | |||
| 408 | |||
| 409 | // keep params from original GET-request |
||
| 410 | // (if you use POST or COOKIES, you have to implement it yourself, sorry.) |
||
| 411 | $this->QUERY_STRING = strlen(trim($_SERVER['QUERY_STRING'])) > 0 ? '?'.strip_tags($_SERVER['QUERY_STRING']) : ''; |
||
| 412 | $refresh = $_SERVER['PHP_SELF'].$this->QUERY_STRING; |
||
| 413 | if($this->debug) echo "\n<br>-Captcha-Debug: Keep this params from original GET-request: (".$this->QUERY_STRING.")"; |
||
| 414 | |||
| 415 | |||
| 416 | // check Postvars |
||
| 417 | View Code Duplication | if(isset($_POST['public_key'])) $this->public_K = substr(strip_tags($_POST['public_key']),0,$this->chars); |
|
| 418 | View Code Duplication | if(isset($_POST['private_key'])) $this->private_K = substr(strip_tags($_POST['private_key']),0,$this->chars); |
|
| 419 | $this->current_try = isset($_POST['hncaptcha']) ? $this->get_try() : 0; |
||
| 420 | if(!isset($_POST['captcharefresh'])) $this->current_try++; |
||
| 421 | if($this->debug) echo "\n<br>-Captcha-Debug: Check POST-vars, current try is: (".$this->current_try.")"; |
||
| 422 | |||
| 423 | |||
| 424 | // generate Keys |
||
| 425 | $this->key = md5($this->secretstring); |
||
| 426 | do { |
||
| 427 | $this->public_key = substr(md5(uniqid(rand(),true)), 0, $this->chars); |
||
| 428 | } while (preg_match('/[1o0l]/i', $this->generate_private())); |
||
| 429 | if($this->debug) echo "\n<br>-Captcha-Debug: Generate Keys, public key is: (".$this->public_key.")"; |
||
| 430 | |||
| 431 | } |
||
| 432 | |||
| 433 | |||
| 434 | |||
| 435 | //////////////////////////////// |
||
| 436 | // |
||
| 437 | // PUBLIC METHODS |
||
| 438 | // |
||
| 439 | |||
| 440 | /** |
||
| 441 | * |
||
| 442 | * @shortdesc displays a complete form with captcha-picture |
||
| 443 | * @public |
||
| 444 | * @type void |
||
| 445 | * @return HTML-Output |
||
| 446 | * |
||
| 447 | **/ |
||
| 448 | function display_form() |
||
| 473 | |||
| 474 | |||
| 475 | /** |
||
| 476 | * |
||
| 477 | * @shortdesc validates POST-vars and return result |
||
| 478 | * @public |
||
| 479 | * @type integer |
||
| 480 | * @return 0 = first call | 1 = valid submit | 2 = not valid | 3 = not valid and has reached maximum try's |
||
| 481 | * |
||
| 482 | **/ |
||
| 483 | function validate_submit() |
||
| 509 | |||
| 510 | |||
| 511 | |||
| 512 | //////////////////////////////// |
||
| 513 | // |
||
| 514 | // PRIVATE METHODS |
||
| 515 | // |
||
| 516 | |||
| 517 | /** @private **/ |
||
| 518 | function display_captcha($onlyTheImage=FALSE) |
||
| 525 | |||
| 526 | /** @private **/ |
||
| 527 | function public_key_input() |
||
| 531 | |||
| 532 | /** @private **/ |
||
| 533 | function make_captcha() |
||
| 631 | |||
| 632 | /** @private **/ |
||
| 633 | function makeWebsafeColors(&$image) |
||
| 650 | |||
| 651 | /** @private **/ |
||
| 652 | function random_color($min,$max) |
||
| 662 | |||
| 663 | /** @private **/ |
||
| 664 | function change_TTF() |
||
| 678 | |||
| 679 | /** @private **/ |
||
| 680 | function check_captcha($public,$private) |
||
| 702 | |||
| 703 | /** @private **/ |
||
| 704 | function get_filename($public="") |
||
| 709 | |||
| 710 | /** @private **/ |
||
| 711 | function get_filename_url($public="") |
||
| 716 | |||
| 717 | /** @private **/ |
||
| 718 | function get_try($in=TRUE) |
||
| 744 | |||
| 745 | /** @private **/ |
||
| 746 | function get_gd_version() |
||
| 760 | |||
| 761 | /** @private **/ |
||
| 762 | function generate_private($public="") |
||
| 768 | |||
| 769 | /** |
||
| 770 | * |
||
| 771 | * @shortdesc returns a message if the form validation has failed |
||
| 772 | * @private |
||
| 773 | * @type string |
||
| 774 | * @return string message or blankline as placeholder |
||
| 775 | * |
||
| 776 | **/ |
||
| 777 | function notvalid_msg() |
||
| 791 | |||
| 792 | |||
| 793 | } // END CLASS hn_CAPTCHA |
||
| 794 |
The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using
the property is implicitly global.
To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.