SELECT INTO OUTFILE

December 31, 2007

There are times when, for example I am about to run a large database update or compare two tables, I need to output a simple copy of a SELECT statement to a file for reference at a later date.

After creating a script to handle the task, I realized that MySQL will do all this work for me. All I need to do is create the SELECT query, and append the INTO OUTFILE syntax to generate my file.

Below is a simple example of SELECT … INTO OUTFILE:

SELECT   *
FROM     Customers
INTO OUTFILE 'C:/MySQL_Output/Customers_Output.txt'
FIELDS  TERMINATED BY '|'
        ENCLOSED BY '"'
LINES TERMINATED BY '\n';

We are asking MySQL to SELECT all records from the Customers table and output the query results to the MySQL_Output folder in a file called Customers_Output.txt with some specific formatting. In the example above I am asking MySQL to terminate all fields from the result set with a | and enclose each field with a ” and at the end of a result set, output a new line.

The output from such a request would look like the following:

"ABC Widgets"|"12345 Simple Street"|"Richmond"|"British Columbia"
"XYZ Fasteners"|"78910 Enterprise Avenue"|"Vancouver"|"British Columbia"
Advertisements

Quick Answer: Count the Number of Child Elements in DOM

December 30, 2007

Sometimes I come across the need to count the number of child elements in any given area of the DOM. Below is an example of how to count the number of elements by using the getElementsByTagName() function.

<html>

<head>
<script language = "Javascript">
function countChildElements(parent, child)
     {
          var parent = document.getElementById(parent);
          var childCount = parent.getElementsByTagName(child).length;
          alert(childCount);
     }
</script>
</head>
<body>
<ul id = "listing">
 <li>List Item 1</li>
 <li>List Item 2</li>
 <li>List Item 3</li>
</ul>
<input type = "button" value = "Count <li>" onClick="countChildElements('listing','li');" />
</body>
</html>

When you click the button, the browser will alert you to the number of child <li> elements to the “listing” <ul>.


Quick Answer: Dynamically Adding a Hyperlink to the DOM

December 29, 2007

Javascript is capable of appending child elements to the DOM of a web page dynamically. This quick answer demonstrates one way to append a <a> element to a <div>.

How to append a hyperlink to the DOM is relatively straight forward.

  1. First reference the parent node to add the new child element to
  2. Create a new <a> element using document.createElement(‘a’);
  3. Set the “href” attribute value for the <a>
  4. Create a new text node using document.createTextNode();
  5. Append the text node to the <a> element
  6. Finally, append the new <a> element to the parent node

<html>
  <head>
    <script language="Javascript">
      function appendLink()
      {
        //Get the element that we want to append to
         var divEl = document.getElementById('link_div');
        //Create the new <a>
        var aElem = document.createElement('a');
        aElem.href="http://www.google.com";
        //Create a text node to hold the text of the <a>
        var aElemTN = document.createTextNode('link to Google.com');
        //Append the <a> text node to the <a> element
        aElem.appendChild(aElemTN);
        //Append the new link to the existing <div>
        divEl.appendChild(aElem);
      }
    </script>
  </head>
  <body>
    <input type="button" value="Add Link" onClick="appendLink();">
    <div id="link_div"></div>
  </body>
</html>

Web Browser Output of code before button is clicked:

Dynamically Adding a Hyperlink 001

Web Browser Output after button is clicked:
Dynamically Adding a Hyperlink 002


Dynamically Modifying the DOM with Javascript: CD Collection App Post 01

December 23, 2007

In this series of posts, I will be attempting to build a simple application that will allow me to catalog my CD collection. Whenever possible I will be using Javascript to modify the DOM of the application to respond as close to a desktop application as possible.

Let’s create a simple HTML list to display the tracks of a CD.
index.html …

<ol id="disc_1_tracks">
  <li id="disc_1_track_1">Disc 1`Track 1</li>
  <li id="disc_1_track_2">Disc 1 Track 2</li>
</ol>

To accommodate the dynamic functionality of our new track list, we can add a temporary <a> to add additional tracks to the list.
index.html modified …

<ol id="disc_1_tracks">
  <a href="">Add Track to Disc 1</a>
  <li id="disc_1_track_1">Disc 1`Track 1</li>
  <li id="disc_1_track_2">Disc 1 Track 2</li>
</ol>

Now we are ready to build some Javascript functions that will add new tracks to the disc dynamically.
index.html modified …

<html>
  <head>
    <script language="Javascript">
      function addNewTrack(disc)
      {
        //Purpose: Adds a new track <li> to the current disc <ol>
        var currentDisc = document.getElementById('disc_' + disc + '_tracks');

        var trackLI = document.createElement('li');
        trackLI.id = 'disc_' + disc + '_track_' + (countTracks(disc) + 1);

        var trackLITN = document.createTextNode("Disc " + disc + " Track " + (countTracks(disc) + 1));

        trackLI.appendChild(trackLITN);
        currentDisc.appendChild(trackLI);
      }

      function countTracks(disc)
      {
        //Purpose: Counts the number of tracks for the current disc

        var discToCount = document.getElementById('disc_' + disc + '_tracks');
        //Count the child <li>s
        var trackCount = discToCount.getElementsByTagName('li').length;
        return trackCount;
      }
    </script>
  </head>
<body>

<ol id="disc_1_tracks">
  <a href="Javascript:addNewTrack(1);">Add Track to Disc 1</a>
  <li id="disc_1_track_1">Disc 1`Track 1</li>
  <li id="disc_1_track_2">Disc 1 Track 2</li>
</ol>

</body>
</html>

The two new Javascript functions are addNewTrack() and countTracks(). Our <a> tag in the <body> has been modified to “Javascript:addNewTrack(1);”. This simply allows us to use a hyperlink to call our Javascript function. Now let’s go through the two functions in greater detail.

Step by step tasks of addNewTrack():

  1. By using getElementById() we create the currentDisc element which contains all the references needed for the current disc.
  2. We then create a new <li> element and set its id attribute. You will notice that we call the countTracks() function to determine the number of the new track we are creating (I will explain this more in the step by step walkthrough of the countTracks() function.
  3. To add some text to the new<li> we create a new textnode (trackLTTN), and set the text we want to display.
  4. With the new <li> element we first append the textnode (trackLTT) as a child.
  5. Finally we add the <li> element to the currentDisc as a child.

Step by step tasks of countTracks():

  1. Again using the getElementById(), we create an object to reference the current disc.
  2. Then we create a variable that is the length (or count of elements) of all the <li> elements within our discToCount object.
  3. Finally we return the value of our trackCount variable.

Below is a screen shot of index.html in action:

DOM Example 001 Add Tracks to Disc

As you can see, each time the “Add Track to Disc 1” hyperlink is clicked, the javascript will modify the DOM and append a new <li> to the target <ol>.

Before we move on to extending the functionality, lets enhance the list by including the capability to define the name of the track. This can be accomplished by adding a text input field to the <li>.
index.html modified …

function addNewTrack(disc)
      {
        //Purpose: Adds a new track <li> to the current disc <ol>
        var currentDisc = document.getElementById('disc_' + disc + '_tracks');

        var trackLI = document.createElement('li');
        trackLI.id = 'disc_' + disc + '_track_' + (countTracks(disc) + 1);

        var trackLIInput = document.createElement('input');
        trackLIInput.type = "text";
        trackLIInput.value = "Disc " + disc + " Track " + (countTracks(disc) + 1);
        trackLIInput.name = "disc_" + disc + "_track_" + (countTracks(disc) + 1) + "_name";
        trackLIInput.id = "disc_" + disc + "_track_" + (countTracks(disc) + 1) + "_id";

        trackLI.appendChild(trackLIInput);
        currentDisc.appendChild(trackLI);
      }

By changing our addNewTrack() function we now have a new text input box for each new track that is added to the list.

Here is an updated screenshot:

DOM Example 002 Add Tracks to Disc with text input

Things are starting to function dynamically. Post 2 will further expand upon our Javascript and enhance our client side application.


Captcha (keeping those spammers out of your “business”) Part 03: Adding a user interface to the captcha

December 21, 2007

In Part 03 of this series of posts we are going to build a simple input form to hold the captcha image.

First, I will write out some HTML and in the <body> I will include a simple form.

<form method="post" action="" id="comment_form">
  <ul>
    <li>
      <label for="form_name">Name</label>
      <input type="text" name="form_name" id="form_name" />
    </li>
    <li>
      <label for="form_email">Email</label>
      <input type="text" name="form_email" id="form_email" />
    </li>
    <li>
      <label for="form_captcha"> </label>
      <img id="form_captcha" />
    </li>
    <li>
      <label for="form_captcha_instructions"> </label>
      <span id="form_captcha_instructions">enter the code exactly as shown above</span>
    </li>
    <li>
      <label for="form_captcha_value">Code<em>*</em></label>
      <input type="text" name="form_captcha_value" id="form_captcha_value" />
    </li>
    <li>
      <label for="form_comment">Comment<em>*</em></label>
      <textarea id="form_comment" name="form_comment" rows="10"></textarea>
    </li>
    <li>
      <label for="submit"> </label>
      <input type="button" name="submit" value="Add Comment" id="submit"/>
    </li>
  </ul>
</form>

Curious to see how horrible this looks? Below is a snapshot:

Captcha Form, No Style — Literally

Before we continue with the Javascript, I will add some CSS to cleanup the layout:

      *
      {
        font: 10pt Arial;
        margin: 0;
        padding: 0;
      }
      #comment_form
      {
        margin: 5px;
        padding: 5px;
        border: 1px Solid #e5e5e5;
        width: 400px;
        background: #f9f9f9;
      }
        #comment_form ul
        {
          list-style: none;
        }
        #comment_form ul li
        {
          margin-bottom: 5px;
        }
            #comment_form ul li label
            {
              width: 75px;
              float: left;
            }
            #comment_form ul li textarea
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
            #comment_form ul li input
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
              #comment_form ul li label em
              {
                color: red;
              }

There, some simple CSS and things are starting to look a lot better.

Captcha Form, with some style

To include the captcha image within the form I will use some javascript to call the captcha.php. This is easily accomplished by using some very simple Javascript to modify the DOM.

index.php cont…

      function getCaptchaImage(session_id)
      {
        document.getElementById("form_captcha").src="captcha.php?action=challenge&session_id=" + session_id;
      }

The getCaptchaImage() function adds the ‘src’ attribute to the <img> in our HTML. We can call this function in the <body> by using <body onLoad=”getCaptchaImage(session_id);”>. But the session_id value will need to be determined by some PHP.

Up to this point, here is the index.php:

index.php

<?php
session_start();
?>
<html>
  <head>
    <style type="text/css">
      *
      {
        font: 10pt Arial;
        margin: 0;
        padding: 0;
      }
      #comment_form
      {
        margin: 5px;
        padding: 5px;
        border: 1px Solid #e5e5e5;
        width: 400px;
        background: #f9f9f9;
      }
        #comment_form ul
        {
          list-style: none;
        }
        #comment_form ul li
        {
          margin-bottom: 5px;
        }
            #comment_form ul li label
            {
              width: 75px;
              float: left;
            }
            #comment_form ul li textarea
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
            #comment_form ul li input
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
              #comment_form ul li label em
              {
                color: red;
              }
    </style>
    <script language="Javascript">
      function getCaptchaImage(session_id)
      {
        document.getElementById("form_captcha").src="captcha.php?action=challenge&session_id=" + session_id;
      }
    </script>
  </head>
<body onLoad="getCaptchaImage('<?php echo session_id();?>');">
<form method="post" action="" id="comment_form">
  <ul>
    <li>
      <label for="form_name">Name</label>
      <input type="text" name="form_name" id="form_name" />
    </li>
    <li>
      <label for="form_email">Email</label>
      <input type="text" name="form_email" id="form_email" />
    </li>
    <li>
      <label for="form_captcha"> </label>
      <img id="form_captcha" />
    </li>
    <li>
      <label for="form_captcha_instructions"> </label>
      <span id="form_captcha_instructions">enter the code exactly as shown above</span>
    </li>
    <li>
      <label for="form_captcha_value">Code<em>*</em></label>
      <input type="text" name="form_captcha_value" id="form_captcha_value" />
    </li>
    <li>
      <label for="form_comment">Comment<em>*</em></label>
      <textarea id="form_comment" name="form_comment" rows="10"></textarea>
    </li>
    <li>
      <label for="submit"> </label>
      <input type="button" name="submit" value="Add Comment" id="submit" />
    </li>
  </ul>
</form>
</body>
</html>

And our result:

Captcha Form, dynamically loading the captcha

A quick check of our database verifies that the correct captcha values have been saved to the database (our PHP is working!) :

mysql> select * from captcha;
+----------------------------+-----------+
| sid                        | challenge |
+----------------------------+-----------+
| upgf36fgm8o9b1a381fmk90gf4 | 406fb     |
+----------------------------+-----------+
1 row in set (0.00 sec)

Now we need a way to determine if the user has entered the correct captcha code to the input box. The check will be completed when the user clicks the “Add Comment” button. By using some Ajax we can easily determine if the code is correct.

index.php cont…

      function createRequestObject()
      {
        var reqObj = null;
        //Attempt to create native XMLHttpRequest Object
        if(window.XMLHttpRequest)
        {
          try
          {
            reqObj = new XMLHttpRequest();
          }
          catch(error)
          {
            reqObj = null;
          }
        }
        //Attempt to create ActiveX version
        else if(window.ActiveXObject)
        {
          try
          {
            reqObj = new ActiveXObject("Msxml2.XMLHTTP");
          }
          catch(error)
          {
            try
            {
              reqObj = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch(error)
            {
              reqObj = null;
            }
          }
        }
        return reqObj;
      }

      var ro = createRequestObject();

      function testChallenge(sid)
      {
        var challenge = document.getElementById('form_captcha_value').value;
        ro.open('get', 'captcha.php?session_id=' + sid + '&action=verify&challenge=' + challenge, true);
        ro.onreadystatechange = getChallengeResponse;
        ro.send(null);
      }

      function getChallengeResponse()
      {
        if(ro.readyState == 4)
        {
          alert(ro.responseText);
        }
      }

In the <script> I have added 3 new functions and 1 variable. The createRequestObject() function is responsible for creating our repsonse object based on the user’s browser type (IE or a browser that natively support XMLHttpRequest). The testChallenge() function sends the request to our captcha.php and waits for a response. The getChallengeResponse() will display an alert with the response from our captcha.php when it has completed processing on the server.

Now to trigger the testChallenge() function we will modify the “Add Comment” button.

index.php complete

<?php
session_start();
?>
<html>
  <head>
    <style type="text/css">
      *
      {
        font: 10pt Arial;
        margin: 0;
        padding: 0;
      }
      #comment_form
      {
        margin: 5px;
        padding: 5px;
        border: 1px Solid #e5e5e5;
        width: 400px;
        background: #f9f9f9;
      }
        #comment_form ul
        {
          list-style: none;
        }
        #comment_form ul li
        {
          margin-bottom: 5px;
        }
            #comment_form ul li label
            {
              width: 75px;
              float: left;
            }
            #comment_form ul li textarea
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
            #comment_form ul li input
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
              #comment_form ul li label em
              {
                color: red;
              }
    </style>
    <script language="Javascript">
      function createRequestObject()
      {
        var reqObj = null;
        //Attempt to create native XMLHttpRequest Object
        if(window.XMLHttpRequest)
        {
          try
          {
            reqObj = new XMLHttpRequest();
          }
          catch(error)
          {
            reqObj = null;
          }
        }
        //Attempt to create ActiveX version
        else if(window.ActiveXObject)
        {
          try
          {
            reqObj = new ActiveXObject("Msxml2.XMLHTTP");
          }
          catch(error)
          {
            try
            {
              reqObj = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch(error)
            {
              reqObj = null;
            }
          }
        }
        return reqObj;
      }

      var ro = createRequestObject();

      function testChallenge(sid)
      {
        var challenge = document.getElementById('form_captcha_value').value;
        ro.open('get', 'captcha.php?session_id=' + sid + '&action=verify&challenge=' + challenge, true);
        ro.onreadystatechange = getChallengeResponse;
        ro.send(null);
      }

      function getChallengeResponse()
      {
        if(ro.readyState == 4)
        {
          alert(ro.responseText);
        }
      }  

      function getCaptchaImage(session_id)
      {
        document.getElementById("form_captcha").src="captcha.php?action=challenge&session_id=" + session_id;
      }
    </script>
  </head>
  <body onLoad="getCaptchaImage('<?php echo session_id();?>');">
  <form method="post" action="" id="comment_form">
    <ul>
      <li>
        <label for="form_name">Name</label>
        <input type="text" name="form_name" id="form_name" />
      </li>
      <li>
        <label for="form_email">Email</label>
        <input type="text" name="form_email" id="form_email" />
      </li>
      <li>
        <label for="form_captcha"> </label>
        <img id="form_captcha" />
      </li>
      <li>
        <label for="form_captcha_instructions"> </label>
        <span id="form_captcha_instructions">enter the code exactly as shown above</span>
      </li>
      <li>
        <label for="form_captcha_value">Code<em>*</em></label>
        <input type="text" name="form_captcha_value" id="form_captcha_value" />
      </li>
      <li>
        <label for="form_comment">Comment<em>*</em></label>
        <textarea id="form_comment" name="form_comment" rows="10"></textarea>
      </li>
      <li>
        <label for="submit"> </label>
        <input type="button" name="submit" value="Add Comment" id="submit" onClick="testChallenge('<?php echo session_id(); ?>');"/>
      </li>
    </ul>
  </form>
  </body>
</html>

Now lets refresh our index.php and run it from the top.

Step 1: Viewing the form

Captcha Form, Running the Scripts from the top

Step 2: Verify the database has the correct challenge code

mysql> select * from captcha;
+----------------------------+-----------+
| sid                        | challenge |
+----------------------------+-----------+
| upgf36fgm8o9b1a381fmk90gf4 | 8d2ac     |
+----------------------------+-----------+
1 row in set (0.00 sec)

Step 3: Adding the correct captcha challenge code into the input box and the result after clicking “Add Comment”

Captcha Form, Running the Scripts from the top 2

Step 4: Adding the incorrect captcha challenge code into the input box and the result after clicking “Add Comment”

Captcha Form, Running the Scripts from the top 3

Everything is working just the way it was expected too. Now that we have the fundamentals of building a simple captcha method there are some additional issues that require future planning:

  1. Accessibility is a large concern with this captcha as it currently sits. Users with visual impairments may not be able to use this method to access your site. How can we add to our script to ensure all potential users are able to utilize our sites?
  2. I would not consider this captcha to be all that strong. More advanced captcha methods would utilize more sophisticated methods to prevent OCR bots from breaking our spam prevention.

This series of posts was not intended to be an absolute guide to developing captcha. The purpose of this post was to demonstrate one possible method to build a captcha from the ground up in 1 hour or less.

Please feel free to provide feedback or request clarification, but remember “I am NOT a web designer!”


Captcha (keeping those spammers out of your “business”) Part 02: Extending the PHP the add the challenge code to a database

December 21, 2007

In Part 02 of this series of posts I am going to extend the existing PHP script that generates the captcha image to include functions that add the challenge code to a database, a function to verify a user’s challenge response and some main logic to determine which action to take.

To store the captcha challenge code and the current user’s session id, I have created a simple database “portfolio” and in that database is the “captcha” table (as seen below):

mysql> show columns from captcha;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| sid       | varchar(45) | NO   | PRI |         |       |
| challenge | varchar(45) | NO   |     |         |       |
+-----------+-------------+------+-----+---------+-------+
2 rows in set (0.05 sec)

Now let us add a function to add the challenge code to the database:

captcha.php cont…

function update_database($challenge, $sid)
{
  $connection = mysql_connect("localhost", "root", "root");
  $db = mysql_select_db("portfolio");
  $deletesql = "  DELETE
                  FROM    captcha
                  WHERE   sid = '". $sid ."';
                ";
  $updatesql = "  INSERT INTO captcha
                  (sid, challenge)
                  VALUES('". $sid. "','". $challenge ."');
               ";
  mysql_query($deletesql);
  mysql_query($updatesql);
  mysql_close($connection);
}

The new function update_database() opens a connection to a MySQL database, accesses the desired database and runs two queries. The first query deletes all records in the captcha table where the session id is equal to the $sid variable. The second query adds the current $sid of the user and $challenge code from our generateMD5String() function.

Now we need a simple function to test if a user has entered the correct captcha code. This is accomplished by querying the captcha table for a record that contains their sid, and challenge.

captcha.php cont…

function verify_code()
{
  $connection = mysql_connect("localhost", "root", "root");
  $db = mysql_select_db("portfolio");
  $sql = "SELECT * FROM captcha where sid='". $_GET['session_id'] ."' and challenge='". $_GET['challenge'] ."';";
  $result = mysql_query($sql);
  if(mysql_num_rows($result) == 1)
  {
    echo "Authenticated";
  }
  mysql_close($connection);
}

verify_code() uses $_GET values (values from the querystring) of ‘session_id’ and ‘challenge’ to run the query. If the database has a record with the matching session id and challenge, it is safe to assume that the user has passed our automated test.

Before we move to the Javascript and HTML to present a form and handle all the fun ajax, we need some additional logic to drive our captcha.php flow.

captcha.php cont…

switch($_GET['action'])
{
  case "challenge":
    generateCaptcha();
    break;
  case "verify":
    verify_code();
    break;
  default:
    generateCaptcha();
}

With the use of a swtich, we can direct the program flow quite easily. The switch is watching for the ‘action=’ portion of the querystring to determine which function to call.

Putting it all together, we now are able to move to the client side development.

captcha.php complete

<?php

function generateMD5String($chars = 5)
{
  //Generate a random md5 code $chars long
  $md5 = md5(microtime() * mktime());
  $string = substr($md5,0,$chars);
  return $string;
}

function drawRandomLines($im, $recHeight, $recWidth, $lines = 50)
{
  //Add $lines lines to the image with random x, y coordinates
  //within the $recHeight, $recWidth values
  for($i = 1; $i <= $lines; $i++)
  {
    $line = imagecolorallocate($im, 207, 207, 207);
    imageline($im, rand(0, $recHeight), rand(0, $recWidth), rand(0, $recHeight), rand(0, $recWidth), $line);
  }
}

function generateCaptcha($recHeight = 200, $recWidth = 75)
{
  $challenge_string = generateMD5String();    update_database($challenge_string, $_GET['session_id']);

  //Set the header for the PNG
  header("Content-type: image/png");
  $im = imagecreatetruecolor($recHeight, $recWidth);
  //Let's make the font black (0,0,0)
  $font_color = imagecolorallocate($im, 0, 0, 0);
  //For fun I have set the background to a greyish color
  $im_background = imagecolorallocate($im, 229, 229, 229);
  //Fill in the rectangle with color
  imagefilledrectangle($im, 0, 0, $recHeight, $recWidth, $im_background);  

  //Load the gdf to display the challenge word
  $font = imageloadfont("./fonts/alienation48.gdf");

  imagestring($im, $font, rand(0, $recHeight/3), rand(0, $recWidth/3), $challenge_string, $font_color);

  //To add some further complexity, I want to draw lines across the random text
  drawRandomLines($im, $recHeight, $recWidth);

  //Display the image
  imagepng($im);
}   

function update_database($challenge, $sid)
{
  $connection = mysql_connect("localhost", "root", "root");
  $db = mysql_select_db("portfolio");
  $deletesql = "  DELETE
                  FROM    captcha
                  WHERE   sid = '". $sid ."';
                ";
  $updatesql = "  INSERT INTO captcha
                  (sid, challenge)
                  VALUES('". $sid. "','". $challenge ."');
               ";
  mysql_query($deletesql);
  mysql_query($updatesql);
  mysql_close($connection);
}     

function verify_code()
{
  $connection = mysql_connect("localhost", "root", "root");
  $db = mysql_select_db("portfolio");
  $sql = "SELECT * FROM captcha where sid='". $_GET['session_id'] ."' and challenge='". $_GET['challenge'] ."';";
  $result = mysql_query($sql);
  if(mysql_num_rows($result) == 1)
  {
    echo "Authenticated";
  }
  mysql_close($connection);
}      

switch($_GET['action'])
{
  case "challenge":
    generateCaptcha();
    break;
  case "verify":
    verify_code();
    break;
  default:
    generateCaptcha();
}  ?>

Part 03 in this series of posts will focus on using Javascript, Ajax, CSS and PHP to build a form to utilize our captcha.

captcha_003.png


Captcha (keeping those spammers out of your “business”) Part 01: Using PHP to dynamically generate a simple captcha image

December 20, 2007

Yesterday I sat down and remonissed about a simple web site that I put together using Coldfusion. Not too long after the site was published on the internet, the guest book that I had built was beginning to become filled with spam.

Long story short, I realized that I should have built in a captcha (Completely Automated Public Turing Test to tell Computers and Humans Apart) method to make the spammer’s jobs a little more difficult.

You can learn a little more about captcha here.

In this series of posts, I will attempt to develop a simple captcha method that might be used in a guest book, blog or whatever else we can imagine we would want to keep spammers out.

First things first, I want to layout some guidelines for the simple project.

  1. PHP will be used for the server side language (for the sake of my sanity and to cut down on discussions regarding OOP and procedural programming, I will use the latter in this example and append the former in a later follow-up post)
    1. PHP will dynamically generate the captcha image
    2. The captcha challenge code will be saved to something like a session, cookie or database
  2. Javascript will be used as the client side language
    1. Using Ajax, we can request the captcha image and test the user’s response by enhancing the original PHP script
  3. All screen shots will be from Firefox
    1. Don’t get me started on IE
  4. This little project will not attempt to solve every possible spam attack, just make their jobs harder
    1. Read, I am attempting to demonstrate how easy it is to start on the journey of freeing yourself from spam by building a captcha system from scratch

Ok, now that we have the programming languages chosen, it is time to decide how we will code the functions. Since I prefer to keep everything neat, I will break each task into its own function.

Open a text editor, and follow along as I build the server side functions.

captcha.php

function generateMD5String($chars = 5)
{
  //Generate a random md5 code $chars long
  $md5 = md5(microtime() * mktime());
  $string = substr($md5,0,$chars);
  return $string;
}

Simple, we have a nice little function that will generate a random string of letters and or numbers X characters long. Now we can move on to the fun stuff. Next I am going to build a quick function that will use the libgd functions of PHP to build a simple rectangle shaped image, and write the challenge string from our generateMD5String() function overtop the image.

captcha.php cont …

function generateCaptcha($recHeight = 200, $recWidth = 75)
{
  $challenge_string = generateMD5String();  

  //Set the header for the PNG
  header("Content-type: image/png");
  $im = imagecreatetruecolor($recHeight, $recWidth);
  //Let's make the font black (0,0,0)
  $font_color = imagecolorallocate($im, 0, 0, 0);
  //For fun I have set the background to a greyish color
  $im_background = imagecolorallocate($im, 229, 229, 229);
  //Fill in the rectangle with color
  imagefilledrectangle($im, 0, 0, $recHeight, $recWidth, $im_background);   

  //Load the gdf to display the challenge word
  $font = imageloadfont("./fonts/alienation48.gdf"); 

  imagestring($im, $font, rand(0, $recHeight/3), rand(0, $recWidth/3), $challenge_string, $font_color); 

  //Display the image
  imagepng($im);
}

Captcha - Random Positioned Text

The generateCaptcha() function will draw a png with our challenge string from the generateMD5String() and position the text randomly.

You may have noticed that I used the imageloadfont() function to use a font other than the default. A quick search on the internet can provide you with a listing of gdfs that you can use in this script.

So our captcha is looking pretty good so far. To enhance our method, I thought it would be a good idea to draw several lines throughout the png that we are dynamically generating.

captcha.php cont…

function drawRandomLines($im, $recHeight, $recWidth, $lines = 50)
{
  //Add $lines lines to the image with random x, y coordinates
  //within the $recHeight, $recWidth values
  for($i = 1; $i <= $lines; $i++)
  {
    $line = imagecolorallocate($im, 207, 207, 207);
    imageline($im, rand(0, $recHeight), rand(0, $recWidth), rand(0, $recHeight), rand(0, $recWidth), $line);
  }
}

The drawRandomLines() function will draw (by default) 50 lines with random start and end positions on our image. To ensure that the lines overlap the font, call the drawRandomLines() function after you have used the imagestring() function.

Below is the complete PHP code that generates our new captcha image:

captcha.php complete

<?php

function generateMD5String($chars = 5)
{
  //Generate a random md5 code $chars long
  $md5 = md5(microtime() * mktime());
  $string = substr($md5,0,$chars);
  return $string;
}

function drawRandomLines($im, $recHeight, $recWidth, $lines = 50)
{
  //Add $lines lines to the image with random x, y coordinates
  //within the $recHeight, $recWidth values
  for($i = 1; $i <= $lines; $i++)
  {
    $line = imagecolorallocate($im, 207, 207, 207);
    imageline($im, rand(0, $recHeight), rand(0, $recWidth), rand(0, $recHeight), rand(0, $recWidth), $line);
  }
}

function generateCaptcha($recHeight = 200, $recWidth = 75)
{
  $challenge_string = generateMD5String(); 

  //Set the header for the PNG
  header("Content-type: image/png");
  $im = imagecreatetruecolor($recHeight, $recWidth);
  //Let's make the font black (0,0,0)
  $font_color = imagecolorallocate($im, 0, 0, 0);
  //For fun I have set the background to a greyish color
  $im_background = imagecolorallocate($im, 229, 229, 229);
  //Fill in the rectangle with color
  imagefilledrectangle($im, 0, 0, $recHeight, $recWidth, $im_background);  

  //Load the gdf to display the challenge word
  $font = imageloadfont("./fonts/alienation48.gdf");

  imagestring($im, $font, rand(0, $recHeight/3), rand(0, $recWidth/3), $challenge_string, $font_color);

  //To add some further complexity, I want to draw lines across the random text
  drawRandomLines($im, $recHeight, $recWidth);

  //Display the image
  imagepng($im);
}

generateCaptcha();
?>

The resulting image from our new captcha script:

Captcha - Randomly Positioned Lines throughout captcha

In Part 02 of this series of posts, I will enhance the PHP script to support saving the captcha challenge code to the database.