Object-oriented or procedural?


As I mentioned in the previous post, I needed a CAPTCHA algorithm. As I couldn’t find anything simple and usable that would do what I want (ie follow the basic principles of captcha creation (readibility for humans, and hopefully unreadibility for machines)), I decided to write my own. The purpose of this was just to generate the image, I left generating the text, passing it in a secure way and all the other boring stuff for others. And as an exercise, I’ve done both procedural and object-oriented versions.

I’m not an expert on object oriented programming, so my little class could have probably been written in a better (read: longer and even more confusing) way. Anyway, here it is:

/**
 * @name    TasuCaptcha
 * @author  Vit ‘tasuki’ Brunner
 * @license GNU GPL
 * @version 0.001
 *
 * remarks:
 * you need php (duh)
 * you need gd
 * you need the font file, in the same directory as this
 * (get it at http://www.sil.org/~gaultney/gentium/ )
 *
 * if you want to use antialiased line, you need function drawQSLine()
 * get it from http://php.net/manual/function.imageline.php
 * code_couturier at graffiti dot net
 * # antialiased draw_line function 1.1 (faster)
 */

class TasuCaptcha {
  private $bg;
  private $fg;
 
  // picture
  public $width; // width of the picture
  public $height; // height of the picture

  // letters
  public $font; // font for the letters
  public $fontHeight; // height of the letters
  public $maxAngle; // maximum angle for the letters
  public $top; // where the letters should start - y
  public $left; // where the letters should start - x
  public $letterWidth; // approximate width of each letter

  // line
  public $step; // the length of line between turns
  public $stepDiff; // the max height difference for step
  public $lineBorder; // borders for the middle line
  public $thickness; // thickness of the line
  public $antialiased; // if true, makes the line antialiased

  function __construct($text) {
    // picture
    $this->width = 170;
    $this->height = 50;
    $bgColor = ‘113355′;
    $fgColor = ‘CCCCCC’;

    // letters
    $this->text = $text;
    $this->font = ‘./gentium.ttf’;
    $this->fontHeight = 25;
    $this->maxAngle = 15;
    $this->top = 35;
    $this->left = 10;
    $this->letterWidth = 30;

    // center line
    $this->step = 5;
    $this->stepDiff = 5;
    $this->lineBorder = 15;
    $this->thickness = 2;
    $this->antialiased = false;

    $this->setBgColor($bgColor);
    $this->setFgColor($fgColor);
  }

  function setBgColor($color) {
    if (strlen($color) != ‘6′) die(’setBgColor needs 6 letter hex color’);
    $this->bg[‘r’] = hexdec(substr($color, 0, 2));
    $this->bg[‘g’] = hexdec(substr($color, 2, 2));
    $this->bg[‘b’] = hexdec(substr($color, 4, 2));
  }
  function setFgColor($color) {
    if (strlen($color) != ‘6′) die(’setFgColor needs 6 letter hex color’);
    $this->fg[‘r’] = hexdec(substr($color, 0, 2));
    $this->fg[‘g’] = hexdec(substr($color, 2, 2));
    $this->fg[‘b’] = hexdec(substr($color, 4, 2));
  }

  function drawImage() {
    // create image and set colors
    $im = imagecreatetruecolor($this->width, $this->height);
    $background = imagecolorallocate($im,
      $this->bg[‘r’], $this->bg[‘g’], $this->bg[‘b’]);
    imagefill($im, 0, 0, $background);
    $textcolor = imagecolorallocate($im,
      $this->fg[‘r’], $this->fg[‘g’], $this->fg[‘b’]);

    // draw letters
    for ($i = 0; $i < strlen($this->text); $i++) {
      $this->angle = rand(- $this->maxAngle, $this->maxAngle);
      imagettftext($im, $this->fontHeight, $this->angle,
        $this->left + $this->letterWidth * $i, $this->top,
        $textcolor, $this->font, $this->text{$i});
    }

    // draw the line
    $curheight = $this->height / 2;
    for ($i = 0; $i < ($this->width / $this->step); $i++) {
      $lastheight = $curheight;
      $curheight = $curheight + rand(-$this->stepDiff, $this->stepDiff);
      if ($curheight > $this->height - $this->lineBorder)
        $curheight -= $this->stepDiff;
      if ($curheight < $this->lineBorder)
        $curheight += $this->stepDiff;

      // this could be done better…
      for ($j = 0; $j < $this->thickness; $j++) {
        if ($this->antialiased) // draw antialiased line
          drawQSLine($im, $this->step * $i, $lastheight - $j,
            $this->step * $i + $this->step, $curheight - $j,
            $this->fg[‘r’], $this->fg[‘g’], $this->fg[‘b’]);
        else // draw aliased line
          imageline($im, $this->step * $i, $lastheight - $j,
            $this->step * $i + $this->step, $curheight - $j,
            $textcolor);
      }
    }

    // output the image
    header(‘Content-type: image/png’);
    imagepng($im);
    imagedestroy($im);

    // maybe someone could find this useful
    return $this->text;
  }
}

// now we can try to use it:
$captcha = new TasuCaptcha($cnum);
$captcha->antialiased = true;
$captcha->drawImage();

Well, that was long, wasn’t it? And most of the time we were just moving values around… $this->shit = $shit;

Now the procedural version:

/**
 * @name    tasucaptcha
 * @author  Vit ‘tasuki’ Brunner
 * @license GNU GPL
 * @version 0.001
 *
 * remarks:
 * you need php (duh)
 * you need gd
 * you need the font file, in the same directory as this
 * (get it at http://www.sil.org/~gaultney/gentium/ )
 *
 * if you want to use antialiased line, you need function drawQSLine()
 * get it from http://php.net/manual/function.imageline.php
 * code_couturier at graffiti dot net
 * # antialiased draw_line function 1.1 (faster)
 */

function tasucaptcha($text) {
  // picture
  $width = 170; // width of the picture
  $height = 50; // height of the picture
  $bgcolor = ‘113355′; // please keep this 6 letters long
  $fgcolor = ‘CCCCCC’; // please keep this 6 letters long

  // letters
  $font = ‘./gentium.ttf’; // font for the letters
  $fontHeight = 25; // height of the letters
  $maxAngle = 15; // maximum angle for the letters
  $top = 35; // where the letters should start - y
  $left = 10; // where the letters should start - x
  $letterWidth = 30; // approximate width of each letter

  // center line
  $step = 5; // the length of line between turns
  $stepdiff = 5; // the max height difference for step
  $lineBorder = 15; // borders for the middle line
  $thickness = 2; // thickness of the line
  $antialiased = false; // if true, makes the line antialiased

  // END OF SETTINGS

  // get colors
  $bg[‘r’] = hexdec(substr($bgcolor, 0, 2));
  $bg[‘g’] = hexdec(substr($bgcolor, 2, 2));
  $bg[‘b’] = hexdec(substr($bgcolor, 4, 2));
  $color[‘r’] = hexdec(substr($fgcolor, 0, 2));
  $color[‘g’] = hexdec(substr($fgcolor, 2, 2));
  $color[‘b’] = hexdec(substr($fgcolor, 4, 2));

  // set up the image
  $im = imagecreatetruecolor($width, $height);
  $background = imagecolorallocate($im, $bg[‘r’], $bg[‘g’], $bg[‘b’]);
  imagefill($im, 0, 0, $background);
  $textcolor = imagecolorallocate($im, $color[‘r’], $color[‘g’], $color[‘b’]);

  // draw letters
  for ($i = 0; $i < strlen($text); $i++) {
    $angle = rand(-$maxAngle, $maxAngle);
    imagettftext($im, $fontHeight, $angle, $left + $letterWidth*$i,
      $top, $textcolor, $font, $text{$i});
  }

  // draw the line
  $curheight = $height/2;
  for ($i = 0; $i < ($width / $step); $i++) {
    $lastheight = $curheight;
    $curheight = $curheight + rand(-$stepdiff, $stepdiff);
    if ($curheight > $height - $lineBorder) $curheight -= $stepdiff;
    if ($curheight < $lineBorder) $curheight += $stepdiff;

    //
    for ($j = 0; $j < $thickness; $j++) {
      if ($antialiased) // create antialiased line
        drawQSLine($im, $step * $i, $lastheight - $j, $step * $i + $step,
          $curheight - $j, $color[‘r’], $color[‘g’], $color[‘b’]);
      else // draw aliased line
        imageline($im, $step * $i, $lastheight - $j, $step * $i + $step,
          $curheight - $j, $textcolor);
    }
  }

  // draw the image
  header(‘Content-type: image/png’);
  imagepng($im);
  imagedestroy($im);

  return $text;
}
tasucaptcha($cnum);

Unless one is paid by lines of code, one must conclude that the second version is much better. It’s half as long and twice as readable.

As for the captcha itself — I can’t guarantee that the results are machine-unreadable, but at least they are human readable. :-)

Posted on 2008-01-20 at 4:58 pm, filed under programming. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

Leave a Reply

Because this post is too old and I'm getting a lot of spam on old posts, only registered users can post a comment here.