Making a Unit Converter

A unit converter is a program that will convert to and from different units, such as inches and miles. You can input "1 mile" and it will output "63360 inches". This is a tutorial for making a program like this in a couple different programming languages. I'm starting with just Java and PHP, but I may add more later.

The easiest way to do this is in 2 steps. First, convert the entered number into a base unit. Second, convert the base unit into the desired unit. So, to convert inches to miles, you would convert inches into feet and then feet into miles. This might sound more complicated than converting directly from inches to miles, but it is actually much easier to program.

To make this work, we first need to define a bunch of constants for the first step. Here are a few we might need for this example:

  • INCHES_IN_FOOT = 12
  • CENTIMETERS_IN_FOOT = 30.48
  • MILES_IN_FOOT = 0.000189

We could also define constants for the second step, but it is not really needed.

  • FEET_IN_INCH = (INCHES_IN_FOOT)-1 = 1/12
  • FEET_IN_CENTIMETER = (CENTIMETERS_IN_FOOT)-1 = 1/30.48
  • FEET_IN_MILE = (MILES_IN_FOOT)-1 = 1/0.000189

Now that we have constants, the first step is fairly simple. I'll use a generic switch case statement to demonstrate.

switch (starting_unit)

  case "inch":
    return (starting_value / INCHES_IN_FOOT);

  case "centimeter":
    return (starting_value / CENTIMETERS_IN_FOOT);

  case "mile":
    return (starting_value / MILES_IN_FOOT);

  case "foot":
    return (starting_value);

The second step is almost identical to the first. We just multiply by the constant instead of divide.

switch (ending_unit)

  case "inch":
    return (base_value * INCHES_IN_FOOT);

  case "centimeter":
    return (base_value * CENTIMETERS_IN_FOOT);

  case "mile":
    return (base_value * MILES_IN_FOOT);

  case "foot":
    return (base_value);

Now for the complete program in the different languages. I assumed the variables "starting_value", "starting_unit", and "ending_unit" are already filled with data. I'll leave that part up to you.

Java

There is no way to switch on a String in Java, so I'm using if/else statements instead. I embedded these statements in a helper method to make the code more readable.

class UnitConverter
{  
  //Declare constants
  public static final double INCHES_IN_FOOT = 12;
  public static final double CENTIMETERS_IN_FOOT = 30.48;
  public static final double MILES_IN_FOOT = 0.000189;

  public static void main(String args[])
  {
    //Initialize variables
    double starting_value, base_value, end_value;
    String starting_unit, ending_unit;

    //get value converted to base unit
    base_value = starting_value / getConstant(starting_unit);

    //get value converted to ending unit
    end_value = base_value * getConstant(ending_unit);

    System.out.println(end_value);

  }
  private double getConstant(String unit)
  {
    if(unit=="inch")
      return INCHES_IN_FOOT;
    else if(unit=="centimeter")
      return CENTIMETERS_IN_FOOT;
    else if(unit=="mile")
      return MILES_IN_FOOT;
    else
      return 1;
  }
}

PHP

PHP does allow Stings in a switch statement, but I still embedded it in a helper function for readability.

//define constants
define("INCHES_IN_FOOT",12);
define("CENTIMETERS_IN_FOOT",30.48);
define("MILES_IN_FOOT",0.000189);

//convert to base unit
$base_value = $start_value / getConstant($starting_unit);

//convert to end unit
$end_value = $base_value * getConstant($ending_unit);

//print end value
echo $end_value;

function getConstant($unit) {
  switch($unit) {
    case "inch":
      return INCHES_IN_FOOT;
    case "centimeter":
      return CENTIMETERS_IN_FOOT;
    case "mile":
      return MILES_IN_FOOT;
    default:
      return 1;
  }
}

The Easiest Way to Code CSS

One of the biggest advantages of programs like Dreamweaver is the ability to see changes to CSS in real time. If you change a color in CSS, the live preview instantly reflects it. The problem is the "browser" that Dreamweaver uses only displays correctly for very simple websites.

Ideally, you could make a change to CSS and have a real browser update on the fly. Luckily there is a Firefox extension called Firebug that does just that.

Firebug lets you modify, add, and delete CSS properties from inside the browser and instantly update the page without reloading.

Firebug is perfect for putting the finishing touches on a website, figuring out a good color to use, or testing different background images to name a few.

You can download Firebug at https://addons.mozilla.org/en-US/firefox/addon/1843. Click below for a screenshot of me changing CSS on the fly with Firebug.

Using Constants in PHP

Constants are an underused part of PHP. If you have a website with more than 5 pages, you should probably be using them.

I'm going to use an example to demonstrate the usefulness of constants. Let's say you have an image gallery and you let people upload images. Here's the function that adds an uploaded image to the gallery:

function addPicture($picture) {
  //Picture bigger than 100,000 bytes
  if($picture=>size > 100000)
    return false;
  //Picture's name is larger than 20 characters
  else if(strlen($picture=>name) > 20)
    return false;
  //Picture is not an accepted file type
  else if(strpos('jpg, gif, png',$picture=>type)===false)
    return false;
  //Passed all validation
  else
    return $picture=>add();
}

As you can see, we have certain requirements for a picture to be uploaded. We also have to tell people these requirements somewhere. Here are some instructions on our upload form:

<p>Your image must meet the following requirements:</p>
<ul>
  <li>It can't be larger than 100kb</li>
  <li>The file name can't be longer than 20 characters</li>
  <li>It must be in one of the following formats: jpg, gif, png</li>
</ul>

We also have similar instructions in our FAQ and other help pages. What happens if we want to increase the size to 200kb? We would have to replace the text everywhere it appears. This is time consuming and leaves a lot of room for human error.

Wouldn't it be nice if we only had to change the value once? Constants in PHP are a perfect solution to this problem. In PHP, constants are declared using the define() function. Here's the constants.php script that declares all our constants:

//maximum file size in bytes
define('MAX_FILE_SIZE',100000);
//maximum length of file name
define('MAX_FILENAME_LENGTH',20);
//accepted file types
define('FILE_TYPES','jpg, gif, png');

//maximum file size in kilobytes
define('MAX_FILE_SIZE_KB',intval(MAX_FILE_SIZE/1024));

Now we need to add these constants into our pages. Here's the revised addPicture() function:

//Load the constant declarations
include "constants.php";

function addPicture($picture) {
  //Picture bigger than MAX_FILE_SIZE
  if($picture=>size > MAX_FILE_SIZE)
    return false;
  //Picture's name is larger than MAX_FILENAME_LENGTH characters
  else if(strlen($picture=>name) > MAX_FILENAME_LENGTH)
    return false;
  //Picture is not an accepted file type
  else if(strpos(FILE_TYPES,$picture=>type)===false)
    return false;
  //Passed all validation
  else
    return $picture=>add();
}

Here's our revised instruction page:

<?php 
//Load the constant declarations
include "constants.php"; 
?>

<p>Your image must meet the following requirements:</p>
<ul>
  <li>It can't be larger than <?php 
      //display max file size in kilobytes
      echo MAX_FILE_SIZE_KB;
    ?>kb</li>
  <li>The file name can't be longer than <?php
      //Display max number of characters for file name
      echo MAX_FILENAME_LENGTH;
    ?> characters</li>
  <li>It must be in one of the following formats: <?php
      //Display list of allowed file types
      echo FILE_TYPES;
    ?></li>
</ul>

Hopefully this gives you an idea about how to use constants in your site. Here are some things to keep in mind about constants:

  • They are not variables and do not have a '$' in front.
  • They are generally named in all caps with underscores between words.
  • They can only hold scalar values (ie. no arrays or objects). If you want to store an array or object in a constant, use the serialize() function.

Real Time Stock Quotes with PHP

I wanted to create a custom widget to put in a website that pulls and formats stock data in real time. I came across Micro Stock, a free PHP script that does just that. This script was ok, but the code was written for PHP4 and not very flexible.

I decided to write my own script instead. I went with Google Finance, since it has the added bonus of correcting misspellings. Here is the function getStockInfo($url) that parses stock information from the passed url.

function getStockInfo($url){

  //Load content from passed url into $page variable
  $page = file_get_contents($url);
  
  // Get company name and stock symbol
    //Search and place matches in array
    preg_match("/<h1>([^<]*)<\/h1>[\s]* [\s]*\([a-zA-z]*,([^\)]*)\)/",$page,$temp);
    if(count($temp)>1) //If name found
      list(,$name,$symbol) = $temp;
    else //No name found
      list($name,$symbol) = array('Unknown','');

  // Get other info
    //Define reusable regular expressions
    $decimal = '[0-9]+[\.]?[0-9]*'; //Matches a decimal number
    $span_id = '[^"]*'; //Matches dynamic span ids used by google
    $span_class = 'ch[grb]'; //Matches classes for colored text
    
    //Long regular expression to match price and changes in price
    preg_match('/<span class="pr" id="'.$span_id.'">('.$decimal.')<\/span><br>\n<span class=bld><span class="'.$span_class.'" id="'.$span_id.'">([\+\-]?)('.$decimal.')<\/span><\/span>\n<span class="'.$span_class.'" id="'.$span_id.'">\([\+\-]?('.$decimal.')%\)<\/span>/',$page,$text);

/*  $text holds formatted info
    $price holds the price in dollars
    $dir holds the direction of change ("+" or "-")
    $diff holds the change amount in dollars
    $percent holds the change amount in percent */ 

    if(count($text)>1)// Info found
      list($text,$price,$dir,$diff,$percent) = $text;
    else// No info found, fill with dummy data
      list($text,$price,$dir,$diff,$percent) = array('-','-','','','-');
    
    //Store data in array and return
    $result['name']  = $name;
    $result['symbol'] = trim($symbol);
    $result['price'] = $price;
    $result['diff']  = $diff;
    $result['percent'] = $percent;
    $result['dir'] = $dir;
    $result['text']  = $text;
    
    //Return array of data
    return $result;
}

The process is fairly straight forward. First, load the page into a variable. Then, pick out key data by searching for unique patterns.

The second step was the hardest part to do. I first looked at the source of a few Google Finance pages and found recurring patterns. I then translated those patterns into regular expressions, capturing the data I wanted to keep.

My example function only works with Google Finance, but the regular expressions could be altered to work with other sites.

Below is the return value when called with the following url: http://finance.google.com/finance?q=goog

Array
(
    [name] => Google Inc.
    [symbol] => NASDAQ:GOOG
    [price] => 442.93
    [diff] => 9.07
    [percent] => 2.09
    [dir] => +
    [text] => <span class="pr" id="ref_694653_l">442.93</span><br>
<span class=bld><span class="chg" id="ref_694653_c">+9.07</span></span>
<span class="chg" id="ref_694653_cp">(2.09%)</span>
)

This gives you the option of using pre-formatted text, but also gives you all the information you need to format it yourself. To see an example of this script in action, go to http://jeremydorn.com/demos/stocks.php

Using Javascript with Textfields

I'm going to look at different ways to manipulate textboxes using javascript. All of these examples are things I've had to use for various projects.

Here's what I'll cover:

  • Selecting text on focus
  • Creating a live preview
  • Keyboard shortcuts (ie. ctrl+b to bold text)
  • Advanced macros

Selecting Text On Focus

Let's start with the easy one. Here's the scenario: you have HTML code in a textbox that you want people to copy and paste. When the user clicks the textbox, the text should be highlighted.

<textarea onFocus='this.select();'>Click to select</textarea>
Example:

Creating a Live Preview

Here's the scenario: You allow users to create their own html template. You want them to see a live preview of the rendered HTML code as they type.

First, the javascript.

//Puts html in the preview pane
function updatePreview(html) {
  //Get preview element
  target = document.getElementById('preview');
  //Replace element's contents with passed text
  target.innerHTML = html;
}

Now, the html.

<!--Text area that user types in-->
<textarea onKeyUp='updatePreview(this.value);'></textarea>

<!--Element where preview is displayed-->
<div id='preview'></div>
Example:
Preview
No Text
Type HTML code here:

Keyboard Shortcuts

Here's the scenario: When a user fills out a textbox, you want their progress to be saved when they hit ctrl+s (Control + S), the standard keyboard shortcut for "save".

To actually save the data to a database, Ajax is required. I may get to this in a later tutorial, but for now, I'll assume you have a javascript function "saveData()" that does this. Here's the javascript.

//Find out which key is pressed
function interpretKey(e) {
  //Get window event (handle different browsers)
  e = (e) ? e : window.event;

  //Get key code (handle different browsers)
  key = (e.keyCode) ? e.keyCode : event.which;
  
  if(e.ctrlKey) //Control Key is pressed
    if(key==83) //S key is pressed
      saveData(); //Save the data
}

And here's the HTML.

<textarea onKeyDown='interpretKey(event);'></textarea>
Example:

Advanced Macros

A macro is a function that performs several tasks. They are often used to automate actions that humans repeatedly perform. I'm going to use the keyboard shortcuts we just made to preform macros.

Let's say you often type the following skeleton for an HTML file in a textfield:

<html>
<head>
  <title>title</title>
</head>

<body>

</body>
</html>

You want to automate this so you don't have to type it every time. Let's make the keyboard shortcut "Alt+1" type this for you.

First, the javascript.

//Define array of macros
var macros = new Array;

//Define first macro
macros[0] = {
  //Name of macro
  "name":
    "HTML Skeleton",
  //Key that triggers Macro
  "key":
    49, // "1" key
  //Text to insert into textbox
  "text": 
    "<html>\n"+
    "<head>\n"+
    "  <title>title</title>\n"+
    "</head>\n"+
    "<body>\n\n</body>\n"+
    "</html>",
  //Where to place cursor after inserting (from start of insert)
  "cursor_offset":
    52 //Places cursor 52 characters from start (Between the <body> tags)
};
function handleMacro(e) {
  //Get window event (handle different browsers)
  e = (e) ? e : window.event;

  //Get key code (handle different browsers)
  key = (e.keyCode) ? e.keyCode : event.which;
  
  if(e.altKey) { //Alt Key is pressed, check for macros
    for(i=0;i<macros.length;i++) {
      if(macros[i].key == key) { //One of the macros matches the key pressed
        //Do the macro
        addText01(macros[i].text,macros[i].cursor_offset);
    }
  }
  }
}
function addText01(text,offset) {
  //Get textbox element
  textbox = document.getElementById("textbox");
  
  //Get current cursor position.  This is where the text will be inserted.
  cursorPosition = getCursorPosition(textbox);
  if(cursorPosition==-1)
    return false;
  
  //Get the current text in the textbox
  currentText = textbox.value;
  
  //Generate new value with new text inserted
  newText = currentText.substr(0,cursorPosition) + text + currentText.substr(cursorPosition);
  textbox.value = newText;
  
  //Get the new cursor position
  if(offset || offset=='0') //Offset defined
    newCursorPosition = offset;
  else
    newCursorPosition = text.length;
    
  //Set the new cursor position
  setCursorPosition(textbox,cursorPosition+newCursorPosition);
  
  return true;
}
function getCursorPosition(node) {
//Firefox support
  if(node.selectionStart) return node.selectionStart;
//Catch Exception (unsupported browser)
  else if(!document.selection) return 0;
//IE support
    //Define character to search for
  var c = "\001";
  //Create empty range
  var sel  = document.selection.createRange();
  //Duplicate range
  var dul  = sel.duplicate();
  var len  = 0;
  //Move duplicate range to node
  dul.moveToElementText(node);
  //Set selected value to character
  sel.text = c;
  //Search for character
  len  = (dul.text.indexOf(c));
  //Delete character
  sel.moveStart('character',-1);
  sel.text = "";
  //Return character position
  return len;
}
function setCursorPosition (node, pos) {
// Firefox support
  if (node.selectionStart || node.selectionStart == '0') {
    node.selectionStart = pos;
    node.selectionEnd = pos;
    return;
  }
  
// IE Support
    // Create empty selection range
  var sel = document.selection.createRange ();
  // Move selection start and end to 0 position
  sel.moveStart ('character', -node.value.length);
  // Move selection start and end to desired position
  sel.moveStart ('character', pos);
  sel.moveEnd ('character', 0);
  sel.select ();
}

Now for the HTML.

<textarea id='textbox' onKeyDown='handleMacro(event);'></textarea>

Using this technique, you can easily define multiple macros. For a list of javascript key codes, check out http://www.cambiaresearch.com/c4/702b8cd1-e5b0-42e6-83ac-25f0306e3e25/Javascript-Char-Codes-Key-Codes.aspx

Example:

MyISAM vs InnoDB

I'm going to pit two of MySQL's most popular table engines against each other using the four S's as a guide: Speed, Storage, Stability, and Special features.

To test these catagories, I created two identical tables in the same database. One using MyISAM, one using InnoDB.

 CREATE TABLE `test`.`myisam` (
`field1` INT( 11 ) NOT NULL AUTO_INCREMENT ,
`field2` VARCHAR( 32 ) NOT NULL ,
`field3` TEXT NOT NULL ,
PRIMARY KEY ( `field1` )
) ENGINE = MYISAM 

 CREATE TABLE `test`.`innodb` (
`field1` INT( 11 ) NOT NULL AUTO_INCREMENT ,
`field2` VARCHAR( 32 ) NOT NULL ,
`field3` TEXT NOT NULL ,
PRIMARY KEY ( `field1` )
) ENGINE = INNODB 
  1. Speed

    I devised a few tests to compare the relative speeds of the two tables. I repeated each query 3 times and took the average execution time for each table.

    Insert 1,000 rows in one query

    MyISAM: An incredible .0094 second average
    InnoDB: A respectable .0849 second average

    SELECT 10,000 Rows

    MyISAM: A fast .0121 second average
    InnoDB: A decent .0302 second average

    Advanced UPDATE Query

    /* 1,000 rows with md5 hashes (21 rows match) */
    UPDATE innodb SET field3 = 'changed'
      WHERE field3 LIKE '%a12%';
    UPDATE myisam SET field3 = 'changed'
      WHERE field3 LIKE '%a12%';
    

    MyISAM: A good .0112 second average
    InnoDB: A slightly worse .0282 second average

    Conclusion

    I left out some measures of speed, but these numbers are pretty clear. MyISAM was anywhere from 2 to 10 times faster than InnoDB when performing the same tasks. MyISAM wins this round.

  2. Storage

    Here I'm comparing the disk space the two tables use when storing data.

    Empty Table

    MyISAM: 1kb of disk space
    InnoDB: 32kb of disk space

    1,000 Rows

    MyISAM: 166kb of disk space
    InnoDB: 256kb of disk space

    10,000 Rows

    MyISAM: 1,341kb of disk space
    InnoDB: 2,080kb of disk space

    Conclusion

    MyISAM is again the clear winner. MyISAM appears to use about 60% of the disk space that InnoDB uses to store the same amount of data.

  3. Stability

    One measure of stability is the ability of the table to handle simultaneous connections to the database without failing. I haven't tested this myself, but the consensus seems to be that InnoDB is better at this.

    Conclusion

    I personally haven't had stability issues with either table engine, but then again, I haven't had to manage hundreds of simultaneous connections. I'll trust the people that have and declare InnoDB the winner for this round.

  4. Special Features

    This is the category that matters most when choosing an engine type. Both MyISAM and InnoDB have very useful features the other one doesn't. I'll take a look at the main unique feature from each table type.

    Transaction Support

    InnoDB is transaction safe. This means that you can roll back changes you make to a database. This is primarily used for rolling back changes if an error occurs.

    For example, let's say you are transferring money between accounts and taking out a fee. You have three queries: take money out, take fee out, and put money in. If the last query fails, you can roll back the first two. This is a must-have for many applications.

    Full Text Support

    MyISAM supports full text indices and full text searches. This lets you search through text fields extremely quickly. Instead of using LIKE on large tables, which is very ineficient, you can use the MATCH AGAINST syntax for a fraction of the time and processing power. This is a must-have if your tables have description fields that are searchable or you store entire articles in a database.

    Using "article_text LIKE '%mysql%'" on 100 5-page articles would take forever. However using "MATCH(article_text) AGAINST('mysql')" would take no time at all. Another advantage is advanced relevancy scores for searches. If an article mentions mysql 3 times, it will be ranked higher. If you search for "the dog and the cat," only the words "dog" and "cat" will be taken into account when calculating relevancy.

    Conclusion

    Which feature is better depends entirely on what your application is.

And the winner is...

InnoDB. Although InnoDB is often slower and uses more space than MyISAM, I consider transactions as too important to give up. If you look at the times, we're talking a difference of at most .05 seconds. Also, most hosting companies today offer at least 100mb of space per database. 10,000 rows with 100 characters each took up 600kb extra using InnoDB. If you store images and files in the database, space might be an issue, but for most applications, it isn't.

There are certain types of applications that should use MyISAM, most notably Search Engines, but for most applications, InnoDB is the right choice.

Making a Captcha Verification Image

This is a short tutorial on adding a captcha verification image to a web form. PHP 4 or higher and the GD Image Library (comes bundled with newer PHP versions) are required.

Making a Captcha image is a lot simpler than you might think. It involves two main steps: generating a random string and making an image of that string.

Let's start with generating the string. This is the first part of captcha.php

<?php
session_start();

//$string will hold our generated random string
$string = "";

//List of characters that are unique (ie. no "1" and "I")
//This changes based on the font you use
$chars = "2345789ABcdEfGhJkmNpQrstVwxy";

//Choose 5 random characters from the list and 
//add to our string.
for($i=0;$i<5;$i++)
{
  $string .= $chars{rand(0,strlen($chars)-1)};
}

//Store our string in a session variable
$_SESSION['captcha'] = $string;

Now let's generate the image of this string, which is a little more complicated. This is part 2 of the captcha.php file.

//Set the output as a PNG image
header("Content-type: image/png");

//Tell browser to not store captcha in cache
header ("Cache-Control: no-cache, must-revalidate");
header ("Pragma: no-cache");

// create a new image canvas 160x60 px
$iwidth = 160;
$iheight = 60;
$image = imagecreate($iwidth, $iheight);

//Create colors (RGB values)
//Background color - light gray
$bg_color = imagecolorallocate($image, 230, 230, 230);
//Noise color for dots - green
$noise_color1 = imagecolorallocate($image, 40, 120, 80);
//Noise color for lines - light green
$noise_color2 = imagecolorallocate($image, 50, 180, 120);
//Text color - dark green
$text_color = imagecolorallocate($image, 20, 100, 40);

//Add 500 random dots to the image
//Lower this number if it's too hard to read
for( $i=0; $i<500; $i++ ) {
   //Create ellipses with 1px width and height
   imagefilledellipse($image, mt_rand(0,$iwidth), 
     mt_rand(0,$iheight), 1, 1, $noise_color1);
}

//Add 30 random lines to the image
//Lower this number if it's too hard to read
for( $i=0; $i<30; $i++ ) {
   //Make line with two random end points
   imageline($image, mt_rand(0,$iwidth), 
     mt_rand(0,$iheight), mt_rand(0,$iwidth), 
     mt_rand(0,$iheight), $noise_color2);
}

//Choose font file (download link after code block)
//I picked this font because it's easy to read
$font = "annifont.ttf";

//Choose font size
$fsize = 26;

//Set angle of font (just dealing with horizontal text for now)
$fangle = 0;

/*Useful function for getting dimensions of text
Returns:
array(
  0=>bottom left x position,
  1=>bottom left y position,
  2=>bottom right x position,
  3=>bottom right y position,
  4=>top right x position,
  5=>top right y position,
  6=>top left x position,
  7=>top left y position
)
*/
$dims = imagettfbbox ( $fsize, $fangle, $font, $string );

//Height is same as -1 times top_right_y 
$fheight = -1*$dims[5];
//Width is same as bottom_right_x
$fwidth = $dims[2];

//Get starting x,y position so text is centered
$fy = ($iheight-$fheight)/2+$fheight;
$fx = ($iwidth - $fwidth)/2;

//Now the magic function.  Adds the text to our image
//Using all the variables we created
imagettftext($image, $fsize, $fangle, $fx, $fy, 
  $text_color, $font , $string);

//generate a png image and output to screen
imagepng($image);

// destroy image resources
imagedestroy($image);
?>

Here a link to the font file: http://www.urbanfonts.com/fonts/Annifont.htm. Now you should be able to test your captcha image. Here's a demo of what it should look like: http://jeremydorn.com/demos/captcha.php

Now we add it to our form. This part is easy.

<img src='captcha.php' /><br />
Enter the text above: 
<input type='text' name='captcha' />

Finally, we check to see if they enter the right code. This next part goes in your validation script.

if(isset($_SESSION['captcha']) &&
$_REQUEST['captcha']==$_SESSION['captcha'])  {
  //correct code
}
else {
  //incorrect code
}

To see a sample form, go to http://jeremydorn.com/demos/captcha_form.php.