RWCaptcha.php
[code=php]<?php
/**
 * Genereert een CAPTCHA Code die gekke botjesspam moet voorkomen in
 * bijvoorbeeld gastenboeken.
 *
 * @author René Willemsen (rene@renewillemsen.nl)
 * @version 1.0 (2010-11-05)
 */


class RWCaptcha {

    /* Algemene instellingen */
    private $iHeight = 51; // Hoogte van het plaatje
    private $iWidth = 201; // Breedte van het plaatje
    private $aBackground = array('red'=>255,'green'=>255,'blue'=>255); // Kleur van de achtergrond in te stellen van 0 tot 255
    private $sTokens = 'abcdefghjikmnlopqrstuvwxyz'; // Reeks tekens die kunnen voorkomen. Wees verstandig en doe geen gekke dingen zoals ö of een 0 (nul), dit verwart.
    private $iLength = 6; // Lengte van de te genereren string

    /* Afblijven */
    private $rImage;
    private $sCode;

    /* Text instellingen */
    private $aFont = array('arial.ttf','georgia.ttf'); //,'calibri.ttf','georgia.ttf'); // Locatie van de font
    private $aTextColorRange = array('min'=>0,'max'=>150); // Mogelijke kleuren van de letters (verschilt per letter) Zorg voor voldoende contrast.
    private $iFontSize = 24; // Grootte van de fontsize. Er zit volgensmij een beperking op tot ongeveer 18pt
    private $iAngleMin = -30; // Minimale hoek (mag negatief zijn) waarop een letter staat
    private $iAngleMax = 30; // Maximale hoek

    /* Achtergrondraster */
    private $bRaster = true; // False voor als er geen raster weergegeven moet worden
    private $aRasterColor = array('red'=>200,'green'=>200,'blue'=>200); // Kleur van dit raster.
    private $iSpacingRow = 10; // Ruimte per rij (over de Y-as)
    private $iSpacingCol = 10; // Ruimte per kolom (over de X-as)

    /* Achtergrondcirkels */
    private $bCirkel = true;
    private $aCirkelColor = array('min'=>150,'max'=>255);
    private $iCirkelAantal = 30;


    /* Hier beneden hoef je in principe niets meer aan te passen */

    public function __construct() {
        if(extension_loaded('gd') && function_exists('gd_info')) {
            // GD Library aanwezig als het goed is...
            
            
            if($this->iFontSize >= $this->iHeight|| ($this->iLength*$this->iFontSize*0.5) > $this->iWidth) {
                // De fontsize is te groot ten opzichte van de ruimte die ie nodigt heeft. Dus stoppen
                // Resulteert in een kruisje ;-)
                exit;
            }
            else {
                putenv('GDFONTPATH=' . realpath('.'));
                // putenv is nodig voor lastige servers... ;-)
                $this->generateCode();
                $this->startImage();
                $this->generateBackground();
                $this->writeText();
                $this->finalizeImage();
            }
        }
        else {
            exit;
        }
    }

    /* Aanmaken van de afbeelding, de .php wijsmaken dat ie 'n PNG is */
    private function startImage() {
        header("Content-Type: image/png");
        $this->rImage = ImageCreate($this->iWidth,$this->iHeight);
    }

    /* Afsluiten van het GD-proces */
    private function finalizeImage() {
        ImagePng($this->rImage);
        imagedestroy($this->rImage);
    }

    /* Achtergrond een kleur geven en evt. invulling */
    private function generateBackground() {
        //blaat
        $rBackground = imagecolorallocate($this->rImage, $this->aBackground['red'],$this->aBackground['green'],$this->aBackground['blue']);
        ImageFill($this->rImage,0,0,$rBackground);

        if($this->bRaster) {
            $this->generateRaster();
        }

        if($this->bCirkel) {
            $this->generateCirkel();
        }
    }

    /* Voor het maken van een raster */
    private function generateRaster() {
        $rColor = imagecolorallocate($this->rImage, $this->aRasterColor['red'],$this->aRasterColor['green'],$this->aRasterColor['blue']);

        // horizontal
        for($iHor = 0;$iHor<$this->iHeight;$iHor+=$this->iSpacingCol) {
            imageline($this->rImage, 0,$iHor, $this->iWidth, $iHor, $rColor);
        }

        for($iVer = 0;$iVer<$this->iWidth;$iVer+=$this->iSpacingRow) {
            imageline($this->rImage, $iVer, 0, $iVer, $this->iHeight, $rColor);
        }
        // vertical
    }

    private function generateCirkel() {
        for($iCirk = 0;$iCirk<$this->iCirkelAantal;$iCirk++) {
            $iSize = rand(10,100);
            $rColor = imagecolorallocate($this->rImage, rand($this->aCirkelColor['min'],$this->aCirkelColor['max']), rand($this->aCirkelColor['min'],$this->aCirkelColor['max']), rand($this->aCirkelColor['min'],$this->aCirkelColor['max']));
            imagearc($this->rImage, rand(0,$this->iWidth), rand(0,$this->iHeight), $iSize, $iSize,0, 360, $rColor);
        }
    }

    /* Het daadwerkelijk printen van de text */
    private function writeText() {
        $iMaximumStartFromLeft = $this->iWidth*0.2;
            // Uitvogelen wat de maximale positie mag zijn ten opzichte van (0,Y)
        $iLocationFromLeft = rand(10,$iMaximumStartFromLeft);
            // X-positie eerste letter
        $iLocationFromTop = rand($this->iHeight-($this->iHeight*0.2),$this->iHeight-($this->iHeight*0.5));
            // Y-positie eerste letter
        $iAngle = rand($this->iAngleMin,$this->iAngleMax);
            // Hoek v/d 1e letter
        for($iLoop=0;$iLoop<strlen($this->sCode);$iLoop++) {
            $rColor = imagecolorallocate($this->rImage, rand($this->aTextColorRange['min'],$this->aTextColorRange['max']),rand($this->aTextColorRange['min'],$this->aTextColorRange['max']),rand($this->aTextColorRange['min'],$this->aTextColorRange['max']));
            imagefttext($this->rImage, 28, $iAngle, $iLocationFromLeft, $iLocationFromTop, $rColor, $this->aFont[rand(0,(count($this->aFont)-1))], substr($this->sCode,$iLoop,1));


            /* Letterinstellingen opnieuw instellen */
            $iLocationFromLeft = $iLocationFromLeft + $this->iFontSize;
            $iLocationFromTop = rand($this->iHeight-($this->iHeight*0.2),$this->iHeight-($this->iHeight*0.5));
            $iAngle = rand($this->iAngleMin,$this->iAngleMax);
        }
        
    }

    /* De code genereren */
    private function generateCode() {
        $sString = '';

        for($iLoop=0;$iLoop<$this->iLength;$iLoop++) {
            $iStart = rand(0,strlen($this->sTokens));
            $sString .= substr($this->sTokens,$iStart,1);
        }
        $this->sCode = $sString;
        $this->setCodeInSession();
    }

    /* Beetje overbodige functie eigenlijk */
    private function setCodeInSession() {
        $_SESSION['sCode'] = $this->sCode;
    }

}

/* Heel belangrijk:
 * -Nieuwe sessie starten (!!!)
 * -Nieuwe instantie van de Captcha aanmaken
 */
session_start();
$r = new RWCaptcha();
?>


Voorbeeld gebruik: [code=php]<?php
/**
 * Voorbeelddocument
 *
 * @author René Willemsen (rene@renewillemsen.nl)
 * @version 1.0 (2010-11-05)
 */
session_start();
?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Voorbeeld RWCaptcha</title>
    </head>
    <body>
    <?php
    if($_SERVER['REQUEST_METHOD'] == 'POST') {
        if(isset($_SESSION['sCode'])) {
            if($_POST['code']==$_SESSION['sCode']) {
                echo '<p>De code is juist!</p>';
            }
            else {
                echo '<p>Ben je blind ofzo?</p>';
            }
        }
        else {
            echo '<p>Sessie niet gezet. Check je script op session_start() aanwezigheid.</p>';
        }
    }
    ?>

        <form method="post" action="index.php">
            <p><img src="RWCaptcha.php" alt="" /></p>
            <p>Type de code over: <input type="text" name="code" /></p>
            <p><input type="submit" value="Verzenden" /></p>
        </form>


    </body>
</html>