<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1752795022597091910</id><updated>2012-01-30T15:49:46.193-08:00</updated><category term='mootools'/><category term='cakephp'/><category term='jquery'/><category term='xhtml'/><category term='css'/><category term='javascript'/><category term='java'/><category term='mysql'/><category term='php'/><category term='ajax'/><category term='html'/><title type='text'>Jeremy Dorn Web Design</title><subtitle type='html'>A web design blog focusing on php, mysql, ajax, and whatever else I want to write about.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>20</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-4610337217906581277</id><published>2011-07-09T23:50:00.000-07:00</published><updated>2011-07-09T23:50:30.724-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='mootools'/><title type='text'>Slick vs. Sizzle: The CSS Selector Engine Battle</title><content type='html'>&lt;p&gt;Javascript frameworks like MooTools and jQuery are used on most modern websites.  CSS selector engines are one of the most useful and widely used features in these frameworks.  Because of this, selector performance is extremely important for modern web applications.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Both MooTools and jQuery have built their own stand alone selector engines for use in their respective projects.  MooTools uses the Slick selector engine and jQuery uses the Sizzle selector engine.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Surprisingly, there are very few performance comparisons out there for these two engines.  Below are the results of a selector engine performance test between MooTools 1.3.2 and jQuery 1.6.2.  The original code for this test came from &lt;a href="http://blog.frontendforce.com/2010/05/jquery-vs-mootools-nightly-benchmark/"&gt;this post&lt;/a&gt; and you can check out the source code on &lt;a href="http://jsfiddle.net/3yEeL/49/"&gt;jsFiddle&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-jZxEBjQe3XE/ThlLG_7SrhI/AAAAAAAABdE/D-46-yE99Ys/s1600/slick_vs_sizzle.png" /&gt;&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;jQuery seems faster than MooTools for almost any type of selector.  The one big exception is the ".note" selector, which also happens to be one of the most commonly used. Given that, the overall performance of a webapp between these two frameworks is somewhat up in the air.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-4610337217906581277?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/4610337217906581277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/4610337217906581277'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2011/07/slick-vs-sizzle-css-selector-engine.html' title='Slick vs. Sizzle: The CSS Selector Engine Battle'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-jZxEBjQe3XE/ThlLG_7SrhI/AAAAAAAABdE/D-46-yE99Ys/s72-c/slick_vs_sizzle.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-8929446642113964788</id><published>2011-02-14T14:52:00.000-08:00</published><updated>2011-02-14T14:52:00.050-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Simple AJAX with jQueryUI and PHP</title><content type='html'>&lt;p&gt;This will be a short tutorial on how to incorporate AJAX interaction into a PHP site using jQuery and the jQueryUI framework.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Imagine we have a page that lets users manage a list of books that is stored in a database.&amp;nbsp; We want them to be able to easily edit the books via ajax without having to go to another page.  Below is an example of what we want.&lt;/p&gt;&lt;br /&gt;
&lt;div style="clear: both; text-align: center;"&gt;&lt;img border="0" style='width:100%' src="http://3.bp.blogspot.com/-VluiiURFL2M/TVmw22w4JpI/AAAAAAAABaA/elG_NgP5UjU/s1600/example.png" /&gt;&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;Here's the database table we'll use plus a few sample books.&lt;/p&gt;&lt;br /&gt;
&lt;pre name='code' class='sql'&gt;CREATE TABLE `books` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(30) NOT NULL,
  `genre` enum('fantasy','mystery','nonfiction') NOT NULL,
  `description` text NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE = InnoDB;

INSERT INTO `book` (`id`, `title`, `genre`, `description`) VALUES
(1, 'The Lord of the Rings', 'fantasy', 'The Lord of the Rings is an epic fantasy novel written by philologist and University of Oxford professor J. R. R. Tolkien (from Wikipedia).'),
(2, 'The Maltese Falcon', 'mystery', 'The Maltese Falcon is a 1930 detective novel by Dashiell Hammett, originally serialized in the magazine Black Mask (from Wikipedia).'),
(3, 'Economics in One Lesson', 'nonfiction', 'Economics in One Lesson is an introduction to free market economics written by Henry Hazlitt and published in 1946, based on Frédéric Bastiat''s essay Ce qu''on voit et ce qu''on ne voit pas (English: "What is Seen and What is Not Seen") (from Wikipedia).');

&lt;/pre&gt;&lt;br /&gt;
First, let's create the PHP page that pulls these books from the database and displays them to the user.  Later, we'll add javascript code to this page to make the edit link use AJAX to interact with the database.&lt;br /&gt;
&lt;br /&gt;
&lt;pre name='code' class='php'&gt;//display.php
&amp;lt;div class='books'&amp;gt;
 &amp;lt;?php
 //connect to database
 mysql_connect('localhost', 'mysql_user', 'mysql_password');
 mysql_select_db('dbname');

 //get all books
 $query = "SELECT * FROM books";
 $result = mysql_query($query) or die("Error selecting books");

 //display books
 while($row = mysql_fetch_assoc($result)) {
 ?&amp;gt;
  &amp;lt;div class='book' id='book_&amp;lt;?php echo $row['id']; ?&amp;gt;'&amp;gt;
   &amp;lt;a class='edit' href='#'&amp;gt;edit&amp;lt;/a&amp;gt;
   &amp;lt;h3 class='title'&amp;gt;&amp;lt;?php echo $row['title']; ?&amp;gt;&amp;lt;/h3&amp;gt;
   &amp;lt;p&amp;gt;&amp;lt;em class='genre'&amp;gt;&amp;lt;?php echo $row['genre']; ?&amp;gt;&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
   &amp;lt;p class='description'&amp;gt;&amp;lt;?php echo $row['description']; ?&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
 &amp;lt;?php
 }
 ?&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;The next page to create is the edit.php page that the ajax link will call.&lt;/p&gt;&lt;br /&gt;
&lt;pre name='code' class='php'&gt;//edit.php
&amp;lt;?php
//connect to database
mysql_connect('localhost', 'mysql_user', 'mysql_password');
mysql_select_db('dbname');

//pull info from $_POST and sanitize it
$id = mysql_real_escape_string($_POST['id']);
$title = mysql_real_escape_string($_POST['title']);
$genre = mysql_real_escape_string($_POST['genre']);
$description = mysql_real_escape_string($_POST['description']);

//update in database
$query = 'Update books SET title="'.$title.'", genre="'.$genre.'", description="'.$description.'" WHERE id="'.$id.'"';
mysql_query($query);

//generate json code
echo json_encode(array(
 'id'=&gt;$id,
 'title'=&gt;$title,
 'genre'=&gt;$genre,
 'description'=&gt;$description
));
?&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;When the user clicks the edit link for a book, we want a dialog box to pop up with a form that lets the user edit the data.  We want the form to submit to the edit.php page via AJAX.  Finally, we want the page to update to reflect the changes.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;To do this, we first create the html for the dialog box.  The submit button will be handled by jQuery, so we don't need to add it here.  This goes at the bottom of the display.php page.&lt;/p&gt;&lt;br /&gt;
&lt;pre name='code' class='html'&gt;&amp;lt;div id='edit_dialog'&amp;gt;
 &amp;lt;form action='edit.php' method='post'&amp;gt;
  &amp;lt;input type='hidden' name='id' /&amp;gt;

  Title: 
  &amp;lt;input type='text' name='title' /&amp;gt;&amp;lt;br /&amp;gt;
  
  Genre:
  &amp;lt;select name='genre'&amp;gt;
   &amp;lt;option value='fantasy'&amp;gt;Fantasy&amp;lt;/option&amp;gt;
   &amp;lt;option value='mystery'&amp;gt;Mystery&amp;lt;/option&amp;gt;
   &amp;lt;option value='nonfiction'&amp;gt;Nonfiction&amp;lt;/option&amp;gt;
  &amp;lt;/select&amp;gt;&amp;lt;br /&amp;gt;
  
  Description:
  &amp;lt;textarea name='description' cols='30' rows='3'&amp;gt;&amp;lt;/textarea&amp;gt;  
 &amp;lt;/form&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;Now we add the jQuery code to the display.php page to tie everything together.  This requires jQuery, jQueryUI with the dialog widget, and a jQueryUI theme to be loaded.  This goes somewhere on the display.php page.&lt;/p&gt;&lt;br /&gt;
&lt;pre name='code' class='javascript'&gt;$(document).ready(function() {
 //Create dialog
 $edit_dialog = $("#edit_dialog").dialog({
  autoOpen:false, 
  title:"Edit Book", 
  modal:true, 
  buttons:[
   {text: "Submit", click: function() { $('form',$(this)).submit(); }},
   {text: "Cancel", click: function() { $(this).dialog("close"); }},
  ]
 });
 
 //Submit action for dialog form
 $("#edit_dialog form").submit(function() {
  var form = $(this);
  //post form data to form's action attribute
  $.post($(this).attr('action'), $(this).serialize(),function(data) {   
   //get DOM element of updated book
   var book = $('#book_'+data.id);
  
   //update title
   $('.title',book).html(data.title);
   
   //update genre
   $('.genre',book).html(data.genre);
   
   //update description
   $('.description',book).html(data.description);
  
   //close the dialog
   $("#edit_dialog").dialog('close');
  },'json');
  
  //stop default form submit action
  return false;
 });

 //when the edit link is clicked
 function edit_link_action() {
  //get closest book div
  var book = $(this).closest('.book');
  
  //get id from div
  var id = book.attr('id').split('_');
  id = id[id.length-1];
  
  //set id in form
  $('#edit_dialog input[name="id"]').val(id);
  
  //set current title in form
  $('#edit_dialog input[name="title"]').val($('.title',book).html());
  
  //set current genre in form
  $('#edit_dialog select[name="genre"]').val($('.genre',book).html());
  
  //set current description in form
  $('#edit_dialog textarea[name="description"]').val($('.description',book).html());
  
  //open dialog
  $edit_dialog.dialog('open');
  
  //stop default link action
  return false;
 }
 
 //attach action to edit links
 $(".edit").click(edit_link_action);
});
&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;Once all the parts are put together, you should have a fully functioning AJAX site.  Below is the complete code for the display.php page.&lt;/p&gt;&lt;br /&gt;
&lt;pre name='code' class='html'&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
 &amp;lt;script type='text/javascript' src='jquery-1.4.4.min.js'&amp;gt;&amp;lt;/script&amp;gt;
 &amp;lt;script type='text/javascript' src='jquery-ui-1.8.9.custom.min.js'&amp;gt;&amp;lt;/script&amp;gt;
 &amp;lt;link rel='stylesheet' href='jquery-ui-1.8.9.custom.css' /&amp;gt;
 &amp;lt;script type='text/javascript'&amp;gt;
  $(document).ready(function() {
   //Create dialog
   $edit_dialog = $("#edit_dialog").dialog({
    autoOpen:false, 
    title:"Edit Book", 
    modal:true, 
    buttons:[
     {text: "Submit", click: function() { $('form',$(this)).submit(); }},
     {text: "Cancel", click: function() { $(this).dialog("close"); }},
    ]
   });
   
   //Submit action for dialog form
   $("#edit_dialog form").submit(function() {
    var form = $(this);
    //post form data to form's action attribute
    $.post($(this).attr('action'), $(this).serialize(),function(data) {   
     //get DOM element of updated book
     var book = $('#book_'+data.id);
    
     //update title
     $('.title',book).html(data.title);
     
     //update genre
     $('.genre',book).html(data.genre);
     
     //update description
     $('.description',book).html(data.description);
    
     //close the dialog
     $("#edit_dialog").dialog('close');
    },'json');
    
    //stop default form submit action
    return false;
   });

   //when the edit link is clicked
   function edit_link_action() {
    //get closest book div
    var book = $(this).closest('.book');
    
    //get id from div
    var id = book.attr('id').split('_');
    id = id[id.length-1];
    
    //set id in form
    $('#edit_dialog input[name="id"]').val(id);
    
    //set current title in form
    $('#edit_dialog input[name="title"]').val($('.title',book).html());
    
    //set current genre in form
    $('#edit_dialog select[name="genre"]').val($('.genre',book).html());
    
    //set current description in form
    $('#edit_dialog textarea[name="description"]').val($('.description',book).html());
    
    //open dialog
    $edit_dialog.dialog('open');
    
    //stop default link action
    return false;
   }
   
   //attach action to edit links
   $(".edit").click(edit_link_action);
  });
 &amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
 &amp;lt;div class='books'&amp;gt;
  &amp;lt;?php
  //connect to database
  mysql_connect('localhost', 'mysql_user', 'mysql_password');
  mysql_select_db('dbname');

  //get all books
  $query = "SELECT * FROM books";
  $result = mysql_query($query) or die("Error selecting books");

  //display books
  while($row = mysql_fetch_assoc($result)) {
  ?&amp;gt;
   &amp;lt;div class='book' id='book_&amp;lt;?php echo $row['id']; ?&amp;gt;'&amp;gt;
    &amp;lt;a class='edit' href='#'&amp;gt;edit&amp;lt;/a&amp;gt;
    &amp;lt;h3 class='title'&amp;gt;&amp;lt;?php echo $row['title']; ?&amp;gt;&amp;lt;/h3&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;em class='genre'&amp;gt;&amp;lt;?php echo $row['genre']; ?&amp;gt;&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p class='description'&amp;gt;&amp;lt;?php echo $row['description']; ?&amp;gt;&amp;lt;/p&amp;gt;
   &amp;lt;/div&amp;gt;
  &amp;lt;?php
  }
  ?&amp;gt;
 &amp;lt;/div&amp;gt;
 
 &amp;lt;div id='edit_dialog'&amp;gt;
  &amp;lt;form action='edit.php' method='post'&amp;gt;
   &amp;lt;input type='hidden' name='id' /&amp;gt;

   Title: 
   &amp;lt;input type='text' name='title' /&amp;gt;&amp;lt;br /&amp;gt;
   
   Genre:
   &amp;lt;select name='genre'&amp;gt;
    &amp;lt;option value='fantasy'&amp;gt;Fantasy&amp;lt;/option&amp;gt;
    &amp;lt;option value='mystery'&amp;gt;Mystery&amp;lt;/option&amp;gt;
    &amp;lt;option value='nonfiction'&amp;gt;Nonfiction&amp;lt;/option&amp;gt;
   &amp;lt;/select&amp;gt;&amp;lt;br /&amp;gt;
   
   Description:
   &amp;lt;textarea name='description' cols='30' rows='3'&amp;gt;&amp;lt;/textarea&amp;gt;  
  &amp;lt;/form&amp;gt;
 &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-8929446642113964788?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/8929446642113964788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/8929446642113964788'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2011/02/simple-ajax-with-jqueryui-and-php.html' title='Simple AJAX with jQueryUI and PHP'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-VluiiURFL2M/TVmw22w4JpI/AAAAAAAABaA/elG_NgP5UjU/s72-c/example.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-229962862330570415</id><published>2010-09-30T11:22:00.000-07:00</published><updated>2010-09-30T12:37:27.127-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Storing Passwords in a Database</title><content type='html'>&lt;p&gt;In this post, I'll go through three common ways to store and retrieve passwords in a database.  I'll assume PHP and MySQL, but the techniques should be very similar for other setups.&lt;/p&gt;

&lt;p&gt;Only the last method should ever be used for security reasons, but unfortunately, a large number of sites use one of the less secure methods and put their users in danger.&lt;/p&gt;

&lt;h3&gt;Storing a Password as Plain Text&lt;/h3&gt;
&lt;p&gt;This is the most basic and definitely least secure method for handling passwords. &lt;strong&gt;Never Use This Method!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The basic strategy is to store the password directly in the database.  You would then authenticate a user by running a query like this:&lt;/p&gt;
&lt;pre name="code" class="sql"&gt;
SELECT id FROM users WHERE username='johnsmith' AND password='123456'
&lt;/pre&gt;
&lt;p&gt;If the query returns a row, the username and password are correct.  As you can see, if anyone intercepts this query along the way, they automatically have the user's username and password.  Also if your database gets stolen, the thief has all of your users' usernames and passwords.  This is an even bigger problem because most people use the same username and password for everything.&lt;/p&gt;

&lt;p&gt;If you ever click a Forgot Your Password link and the site gives you your current password, they are using this method and I would highly suggest not using the site or at least using a unique password just for the site.&lt;/p&gt;

&lt;h3&gt;Using a Password Hash&lt;/h3&gt;
&lt;p&gt;This method is better than plain text, but still has some major, relatively little known, security holes in it.  A lot of sites use this method thinking it is secure.  &lt;strong&gt; Again, do not use this method.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This method takes a user's password and converts it to an md5 or similar hash before storing in the database.  For example, "123456" becomes "e10adc3949ba59abbe56e057f20f883e".  This seems like it solves the problem with plain text since a person cannot look at the hashed string and know the user's password.  But, if you type this hash into Google, the second result is titled "Google Hash: md5(123456) = e10adc3949ba59abbe56e057f20f883e".  Not as secure as it first looked, is it?&lt;/p&gt;

&lt;p&gt;There are md5 hash tables you can download that contain every word in the dictionary and every common password that make this method very susceptible to attacks.  Many sites require passwords with numbers, symbols, capital letters, etc., which helps fix the security hole, but why make things harder on your users when you can just use a password salt?&lt;/p&gt;

&lt;h3&gt;Using a Password Salt&lt;/h3&gt;
&lt;p&gt;There is no reason not to use this method.  It provides an extra layer of security on top of a password hash with very little extra work.&lt;/p&gt;
&lt;p&gt;This method generates a random string (salt) and appends it to the user's password before generating an md5 or similar hash.  Then, both the password hash and the password salt are stored in the database and used to authenticate the user.  For example, "123456" becomes "123456ghjfdweurt" becomes "8e1a92e8f87a5bbf36f26e330cf7f0b5".  Try typing that hash into Google and the most you may find is this article.&lt;/p&gt;

&lt;p&gt;Here's the PHP code for initially inserting a user into a database.  The getRandomString() function is from &lt;a href='http://www.lost-in-code.com/programming/php-code/php-random-string-with-numbers-and-letters/' target='_blank'&gt;http://www.lost-in-code.com/programming/php-code/php-random-string-with-numbers-and-letters/&lt;/a&gt;.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
//get username and password
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];

//generate password salt
$password_salt = genRandomString();

//generate password hash
$password_hash = $password . $password_salt;

//insert into database
$query = "INSERT INTO users (`username`,`password_hash`,`password_salt`) VALUES ('$username', '$password_hash', '$password_salt')";
mysql_query($query);



function  genRandomString() {
    $length = 10;
    $characters = ’0123456789abcdefghijklmnopqrstuvwxyz’;
    $string = ”;    

    for ($p = 0; $p &lt; $length; $p++) {
        $string .= $characters[mt_rand(0, strlen($characters))];
    }

    return $string;
}
&lt;/pre&gt;
&lt;p&gt;Here's the code for authenticating a user once they are already in the database:&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
//get username and password
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];

//query database
$query = "SELECT * FROM users WHERE username='$username'";
$result = mysql_query($query);

//if no result, username is incorrect
if(!$result) {
    //authentication failed
}

//get database row
$row = mysql_fetch_assoc($result);

//generate password hash from entered password
$password_hash = md5($password . $row['password_salt']);

//check if the generated hash is equal to the hash in the database
if($password_hash === $row['password_hash']) {
     //authentication passed
}
else {
     //authentication failed
}
&lt;/pre&gt;

&lt;h3&gt;Important Safety Tip&lt;/h3&gt;
&lt;p&gt;No matter what method you use, SSL encryption is essential during authentication to protect against man-in-the-middle attacks.  This is where an attacker intercepts data between the user and the server.  If the user submits a login form and an attacker intercepts it, the password will be compromised no matter which method you use.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-229962862330570415?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/229962862330570415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/229962862330570415'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2010/09/storing-passwords-in-database.html' title='Storing Passwords in a Database'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-4270757203180521622</id><published>2010-08-14T13:51:00.001-07:00</published><updated>2010-08-14T15:34:57.114-07:00</updated><title type='text'>Installing and Configuring Dornbase</title><content type='html'>&lt;p&gt;Dornbase is a PHP contact management system I started a while ago and recently released via Sourceforge (&lt;a href='http://sourceforge.net/projects/dornbase/'&gt;http://sourceforge.net/projects/dornbase/&lt;/a&gt;).  You can see a demo of it running at &lt;a href='http://jeremydorn.com/dornbase.php'&gt;http://jeremydorn.com/dornbase.php&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The project is still in its early stages, so there is not much documentation available.  I plan to eventually implement an automated installer, like Joomla and most other major PHP applications have.  If you have any suggestions or want to help, email me at &lt;a href='mailto:jeremy@jeremydorn.com'&gt;jeremy@jeremydorn.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post is meant to serve as a guide for installing and configuring Dornbase on either your local machine or a remote host.&lt;/p&gt;

&lt;h3&gt;Minimum Requirements&lt;/h3&gt;
&lt;p&gt;Most paid web hosts meet these requirements.  All of the required software is included in xampp (&lt;a href='http://sourceforge.net/projects/xampp/'&gt;http://sourceforge.net/projects/xampp/&lt;/a&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apache 2.2 with mod_rewrite enabled&lt;/li&gt;
&lt;li&gt;PHP 5+ (make sure ERROR_REPORTING is set to not show E_NOTICE)&lt;/li&gt;
&lt;li&gt;MySQL 5+ (I haven't tested older versions, but they probably work too)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Download Files&lt;/h3&gt;
&lt;p&gt;Download Dornbase 1.7 from &lt;a href='http://sourceforge.net/projects/dornbase/'&gt;http://sourceforge.net/projects/dornbase/&lt;/a&gt; and extract the files to your document root (C:/xampp/htdocs/ if using xampp).&lt;/p&gt;

&lt;h3&gt;Set up Database&lt;/h3&gt;
&lt;p&gt;Create a new database and run all of the queries in the db_setup.sql file included in the download to set up the database tables.  If you are using xampp, follow the phpmyadmin instructions below to do this.&lt;/p&gt;
&lt;div style='margin-left:20px;'&gt;
&lt;strong&gt;PhpMyAdmin Instructions&lt;/strong&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;a href='http://localhost/phpmyadmin/'&gt;http://localhost/phpmyadmin&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In the "Create new database" form, enter a name for the database (e.g. "dornbase") and click the "Create" button.&lt;/li&gt;
&lt;li&gt;Click the "import" tab.&lt;/li&gt;
&lt;li&gt;Select the db_setup.sql file and click "Go".&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

&lt;p&gt;The only table you may want to change is the "fields" table, which contains all the fields a record will have.  By default, it is set up with all of the fields in the demo, but this can be easily changed.  Below is a brief description of what each column in the fields table means.&lt;/p&gt;
&lt;div style='margin:10px 0 10px 20px;'&gt;
&lt;strong&gt;name&lt;/strong&gt;-
The id of the field (no spaces, all lowercase) (e.g. fname, address_line_1)

&lt;strong&gt;display&lt;/strong&gt;-
&lt;em&gt;The display name of the field (all lowercase) (e.g. "first name", "address line 1")&lt;/em&gt;&lt;br/&gt;

&lt;strong&gt;datatype&lt;/strong&gt;-
&lt;em&gt;The datatype of the field.  Either 'text', 'number', 'date', or 'boolean'.&lt;/em&gt;&lt;br/&gt;

&lt;strong&gt;type&lt;/strong&gt;-
&lt;em&gt;A more specific type differentiator (e.g. 'phone', 'website', 'money', 'ssn')&lt;/em&gt;&lt;br/&gt;

&lt;strong&gt;multiple&lt;/strong&gt;-
&lt;em&gt;Can the field have multiple values?  '1' for yes, '0' for no&lt;/em&gt;&lt;br/&gt;

&lt;strong&gt;discrete&lt;/strong&gt;-
&lt;em&gt;Are the field values limited and discrete (i.e. will it use a drop down menu)?  '1' for yes, '0' for no&lt;/em&gt;&lt;br/&gt;

&lt;strong&gt;search&lt;/strong&gt;-
&lt;em&gt;Should this field be searchable?  '1' for yes, '0' for no&lt;/em&gt;&lt;br/&gt;

&lt;strong&gt;default&lt;/strong&gt;-
&lt;em&gt;The default value this field will have for a new record&lt;/em&gt;&lt;br/&gt;

&lt;strong&gt;summary&lt;/strong&gt;-
&lt;em&gt;Is this field vital for uniquely identifying a record?&lt;/em&gt;&lt;br/&gt;

&lt;strong&gt;padto&lt;/strong&gt;-
&lt;em&gt;For numeric fields, the max number of digits (e.g. if '12', all values are left padded with 0s until there are 12 characters left of the decimal point).  This enables storing numbers as text and still being able to properly sort them.&lt;/em&gt;

&lt;/div&gt;

&lt;h3&gt;Configure Server&lt;/h3&gt;
&lt;p&gt;Rename the "server_default" folder to "server".  This folder contains 2 files which you need to edit.  There are comments and instructions in each of the files.&lt;/p&gt;

&lt;p&gt;Rename "htaccess-default" to ".htaccess" (You may need to open it in notepad and use Save As on some Windows machines).  Edit the line "RewriteBase /dornbase" and change "/dornbase" to whatever directory you installed the application in.&lt;/p&gt;

&lt;p&gt;Rename the "config_default" folder to "config".  This folder contains several files that help you customize Dornbase to fit your needs.  I'll explain each of these files later.&lt;/p&gt;

&lt;h3&gt;Test it out&lt;/h3&gt;
&lt;p&gt;Open up Dornbase in a browser (if you installed it in /htdocs/dornbase/, go to http://localhost/dornbase/).  You should see a sign in page.  Sign in with the default admin account (username: "admin", password: "password").  If you see an empty record page, everything is installed correctly.&lt;/p&gt;

&lt;p&gt;You may see errors if you modified the record fields in the database. After the next step, this should be fixed.&lt;/p&gt;

&lt;h3&gt;Customize&lt;/h3&gt;
&lt;p&gt;The important files to edit in the config folder are template.php, record.php, and functions.php.&lt;/p&gt;

&lt;p&gt;template.php is really short and defines a few variables such as business name and return address (for printing envelopes).&lt;/p&gt;

&lt;p&gt;record.php only needs to be changed if you modified the record fields in the database at all.&lt;/p&gt;

&lt;p&gt;functions.php is a large file and contains a bunch of callback functions that provide hooks into the application.  I'll go through a few of the important functions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;get_record_header(&amp;$data)&lt;/strong&gt; - Should return the header for a record.  By default, it displays the full name (or company if name is empty) followed by a description and the policy type.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;get_field_order(&amp;$record, $mobile=false)&lt;/strong&gt; - This determines the layout of the record.  It allows you to layout the record differently depending on the record data.  For example, you can have a "spouse name" field only show up if their status is set to "married".  You can also display different fields on a mobile device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can play around with the other functions and the other files in the config folder if you want.&lt;/p&gt;

&lt;p&gt;After you make a change to a file in the config folder, most of the time refreshing the page will work.  In a few cases, you may have to sign out and sign back in for the changes to take effect.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-4270757203180521622?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/4270757203180521622'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/4270757203180521622'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2010/08/installing-and-configuring-dornbase.html' title='Installing and Configuring Dornbase'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-3700347704597853658</id><published>2010-07-02T09:33:00.000-07:00</published><updated>2010-07-02T11:45:12.391-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>Reducing Javascript and CSS Load Times</title><content type='html'>&lt;p&gt;I was making a complex ajax web application and ran into problems with load times.  In some cases, it was taking up to 7 or 8 seconds to load a page.  I found out the culprit was multiple large javascript and css files.  Here's how I was able to get my load times under a second with a few changes.  All of the examples below are for javascript files, but they apply equally to css files.&lt;/p&gt;

&lt;h3&gt;Combine javascript and CSS files&lt;/h3&gt;
&lt;p&gt;There is overhead each time the browser has to load an external file, so reducing the number of server calls can speed up page loads.  Originally, I put all of my javascript scripts in one large file.  This helped page load times, but made it difficult to edit individual scripts.  I then switched to a PHP solution.&lt;/p&gt;
&lt;p&gt;I created the following PHP page, which combines multiple javascript files on the fly.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
&amp;lt;?php
//set content type
header('Content-type: text/javascript');

//pull list of comma-separated files from $_GET
$js_files = explode(',',urldecode($_REQUEST['files']));

//read each file
foreach($js_files as $js_file) {
 echo "\n\n\n/*******************$js_file********************/\n";
 $file = '/path/to/js/directory/'.$js_file.".js";
 if(file_exists($file)) readfile($file);
}
?&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Here is how you would load javascript files in the head section of page.&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;!-- Using PHP's urlencode function --&gt;
&amp;lt;?php
$files = array("jsfile1","jsfile2","jsfile3");
?&gt;
&amp;lt;script type='text/javascript' src='/path/to/js/directory/javascript.php?files=&amp;lt;?php
 echo urlencode(implode(',',$files));
?&gt;'&gt;&amp;lt;/script&gt;

&amp;lt;!-- No PHP, hard code commas as "%2C" --&gt;
&amp;lt;script type='text/javascript' src='/path/to/js/directory/javascript.php?files=jsfile1%2Cjsfile2%2Cjsfile3'&gt;&amp;lt;/script&gt;
&lt;/pre&gt;
&lt;p&gt;This reduced my load times to about 5 or 6 seconds.&lt;/p&gt;

&lt;h3&gt;Take Advantage of Parallel Downloading&lt;/h3&gt;
&lt;p&gt;This partially contradicts the previous tip when I said less files were always better.  Most browsers are capable of downloading a limited number of javascript and css files at the same time.  For example, Firefox will usually download up to 3 files simultaneously from a single host.&lt;/p&gt;
&lt;p&gt;If you have 1 or 2 large javascript files, you should put them in their own script tags and combine the rest of the scripts as before.  This way, the browser doesn't have to wait for the large script to download before fetching the other ones.  Here's an example.&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;!-- Large js file --&gt;
&amp;lt;script type='text/javascript' src='/path/to/js/directory/javascript.php?files=largefile'&gt;&amp;lt;/script&gt;

&amp;lt;!-- Multiple smaller files --&gt;
&amp;lt;script type='text/javascript' src='/path/to/js/directory/javascript.php?files=jsfile1%2Cjsfile2%2Cjsfile3'&gt;&amp;lt;/script&gt;
&lt;/pre&gt;
&lt;p&gt;Even though you are only loading one file in the first case, you should still use the php script to take advantage of the later tips.&lt;/p&gt;
&lt;p&gt;This reduced my load times further to about 2 or 3 seconds.&lt;/p&gt;

&lt;h3&gt;Compress Javascript and CSS files&lt;/h3&gt;
&lt;p&gt;The basic idea behind this is to reduce the size of javascript and css files by removing comments and extra white spaces among other things.  There are two ways to do this, each with their advantages and disadvantages.&lt;/p&gt;
&lt;p&gt;The first is to compress the files directly.  The advantages here are speed and reduced server load.  The server doesn't have to waste time compressing each file at runtime.  The big disadvantage is that compressed files are almost impossible to edit.  This method works best for 3rd party scripts, such as javascript frameworks, that you aren't going to be editing yourself.  Most frameworks offer compressed versions directly, but you can also compress any file yourself with &lt;a href='http://jscompress.com/'&gt;http://jscompress.com/&lt;/a&gt; for javascript files and &lt;a href='http://www.cssdrive.com/index.php/main/csscompressor/'&gt;http://www.cssdrive.com/index.php/main/csscompressor/&lt;/a&gt; for css files.&lt;p&gt;
&lt;p&gt;The other method is realtime compression.  The advantage is files remain editable and the development process is much easier.  The disadvantage is that the server must re-compress each script at runtime, which takes time and processing power.  This method is best for scripts which you edit often.  There is a great php script for doing this called &lt;a href='http://code.google.com/p/minify/'&gt;minify&lt;/a&gt; that works for js and css files.  They have plenty of documentation on the site and it's pretty easy to implement into the php script we created above.&lt;/p&gt;
&lt;p&gt;Compressing files can reduce the size by anywhere from 10% to 50% or more.  As an example, the &lt;a href='http://code.jquery.com/jquery-1.4.2.js' target='_blank'&gt;uncompressed jquery framework&lt;/a&gt; is 160kb and the &lt;a href='http://code.jquery.com/jquery-1.4.2.min.js' target='_blank'&gt;compressed version&lt;/a&gt; is 70kb.  This can potentially cut loading times in half, but the actual gain is probably less than that.&lt;/p&gt;

&lt;h3&gt;Take Advantage of Caching&lt;/h3&gt;
&lt;p&gt;A lot of times, javascript and css files don't change very often and it doesn't make sense to load them on each page view.  With a few simple changes to our php script, we can make the browser cache these pages.  This provides the biggest improvement in load times.&lt;/p&gt;

&lt;pre name="code" class="php"&gt;
&amp;lt;?php
//set content type
header('Content-type: text/javascript');

//if the nocache parameter is not explicitly set
if(!$_REQUEST['nocache']) {
 //store in cache for 1 day (86400 seconds)
 header('Expires: '.gmdate('D, d M Y H:i:s', time()+86400).'GMT');  
 header('Cache-Control: max-age=86400'); 
 header('Pragma: max-age=86400'); 
}

...
&lt;/pre&gt;

&lt;p&gt;If you have some file which changes frequently or is generated at the server side, you can pass "nocache=true" in the script tag source.&lt;/p&gt;
&lt;p&gt;One problem you may notice is that if you make a change to a javascript file, a user may not get the latest version for a full day, which could break the site in the meantime.  Luckily, there is an easy solution.&lt;/p&gt;
&lt;p&gt;If the script src changes, a browser will always reload the script, even if it was cached.  Since we're already passing parameters in the script url, we can add "version=#" and increment this number whenever we make a change.  That way, the script will be reloaded the next time the page is.&lt;/p&gt;
&lt;p&gt;The first time the webpage is loaded, there is no added benefit, but the load times of subsequent pages was reduced to less than a second.

&lt;p&gt;If you can think of any other tips to reduce page load times, let me know.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-3700347704597853658?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/3700347704597853658'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/3700347704597853658'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2010/07/reducing-javascript-and-css-load-times.html' title='Reducing Javascript and CSS Load Times'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-2553049513939281528</id><published>2010-04-11T23:46:00.000-07:00</published><updated>2010-04-12T01:08:44.441-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>Fixing jQuery UI Autocomplete</title><content type='html'>&lt;p&gt;jQuery UI 1.8 was recently released and it included a much needed autocomplete widget.  For the most part, this widget works fine, but there is an &lt;a href='http://dev.jqueryui.com/ticket/5405'&gt;annoying bug&lt;/a&gt; that makes it not quite ready for a production environment.  Basically, if a user clicks on an option and doesn't release the mouse button fast enough, the option is not selected.  You can  see the bug in action on the &lt;a href='http://jqueryui.com/demos/autocomplete/#remote'&gt;jQuery UI autocomplete demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thankfully, there is a workaround.&lt;/p&gt;

&lt;p&gt;Here is what we would like to be able to do:&lt;/p&gt;
&lt;pre name="code" class="js"&gt;
$("#autocomplete").autocomplete({
   source: 'search.php',
   select: function(event, ui) {
      alert(ui.item.id);
   }
});
&lt;/pre&gt;

&lt;p&gt;Here is the bug-fixed version I came up with:&lt;/p&gt;

&lt;pre name="code" class="js"&gt;
//global variable that stores the last focused option
var search_option = false;

$("#autocomplete").autocomplete({
   source: 'search.php',
 
   //when an item is focused, store item in the global variable
   focus: function(event, ui) {
      search_option = ui.item;
   },
 
   //when an item is selected with the keyboard, trigger
   //the mouse down event for consistency
   select: function(event, ui) {
      search_option = ui.item;
      $("#autocomplete").autocomplete('widget')
      .trigger('mousedown.choose_option');
   }
})

//bind the select event to mousedown
.autocomplete('widget').bind('mousedown.choose_option',function() {
   //immediately closes autocomplete when option is selected
   $("#autocomplete").autocomplete('close');
 
   //perform desired action
   alert(search_option.id);
});
&lt;/pre&gt;

&lt;p&gt;Obviously, this isn't a very elegant solution, but I didn't want to mess around with the actual autocomplete code.  Hopefully, this is fixed in the next release.  Until then, this will have to do.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-2553049513939281528?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/2553049513939281528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/2553049513939281528'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2010/04/fixing-jquery-ui-autocomplete.html' title='Fixing jQuery UI Autocomplete'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-822612298762229316</id><published>2008-10-23T18:41:00.000-07:00</published><updated>2008-10-23T18:42:11.832-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cakephp'/><title type='text'>Site Templates with CakePHP</title><content type='html'>&lt;p&gt;I started with cakePHP a few months ago and was impressed by their documentation.  I could find answers to most of my questions fairly quickly.  However, I couldn't find a good tutorial on adding a site-wide template.  Hopefully this will help other people with the same problem.&lt;/p&gt;
&lt;p&gt;First start with an HTML template.  If you don't feel like making one from scratch, any div layout will work fine.  Open Source Web Design (&lt;a href='http://oswd.org'&gt;http://oswd.org&lt;/a&gt;) has tons of free templates that work perfectly with CakePHP.  Here's a simplified version of one of their templates:&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&gt;
&amp;lt;html&gt;

&amp;lt;head&gt;
&amp;lt;link rel='stylesheet' href='style.css'/&gt;
&amp;lt;title&gt;CakePHP&amp;lt;/title&gt;
&amp;lt;/head&gt;

&amp;lt;body&gt;
&amp;lt;div class="site_container"&gt;
 &amp;lt;div class="main_content"&gt;
  &amp;lt;div class="top_header"&gt;
   &amp;lt;div class="site_title"&gt;
    &amp;lt;h1&gt;CakePHP&amp;lt;/h1&gt;
   &amp;lt;/div&gt;
  &amp;lt;/div&gt;

  &amp;lt;div class="content"&gt;
   &amp;lt;!-- Main Site Content --&gt;
  &amp;lt;/div&gt;

  &amp;lt;div class="sidenav"&gt;
   &amp;lt;h1&gt;Navigation&amp;lt;/h1&gt;
   &amp;lt;a href='#'&gt;Link 1&amp;lt;/a&gt;
   &amp;lt;a href='#'&gt;Link 2&amp;lt;/a&gt;
  &amp;lt;/div&gt;
 &amp;lt;/div&gt;
&amp;lt;/div&gt;
&amp;lt;/body&gt;

&amp;lt;/html&gt;
&lt;/pre&gt;
&lt;p&gt;Copy this template to &lt;strong&gt;app/views/layouts/default.ctp&lt;/strong&gt;.  If that file doesn't exist, create it.&lt;/p&gt;
&lt;p&gt;The first thing we have to change is the stylesheet.  We are currently including the stylesheet with: &lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;link rel='stylesheet' href='style.css'/&gt;
&lt;/pre&gt;
&lt;p&gt;We need to copy the file &lt;strong&gt;style.css&lt;/strong&gt; to &lt;strong&gt;app/webroot/css/style.css&lt;/strong&gt;.  Then replace the above code with:&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
&amp;lt;?php
 echo $html-&gt;css('style');
?&gt;
&lt;/pre&gt;
&lt;p&gt;The next step is to include the page content.  Edit the template as follows:&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;!-- Main Site Content --&gt;
&amp;lt;?php 
 $session-&gt;flash(); 
 echo $content_for_layout; 
?&gt; 
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;$session-&gt;flash();&lt;/strong&gt; prints error messages and alerts.&lt;br/&gt;
&lt;strong&gt;echo $content_for_layout;&lt;/strong&gt; prints the page content.&lt;/p&gt;
&lt;p&gt;Now for the side navigation.  To keep the code clean, let's create a new file that just holds the navigation bar.  Create the file &lt;strong&gt;app/views/elements/menus/sidenav.ctp&lt;/strong&gt;.  Here's some sample data for that page.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
&amp;lt;?php
 echo $html-&gt;link("Home","/pages/home");
 echo $html-&gt;link("About Us","/pages/about"); 

 //if user is logged in
 if($this-&gt;Session-&gt;check('User'))
  echo $html-&gt;link("Sign Out","/users/logout"); 
 else
  echo $html-&gt;link("Sign In","/users/login"); 
?&gt;
&lt;/pre&gt;
&lt;p&gt;Here is the current code for the side navigation bar:&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;div class="sidenav"&gt;
 &amp;lt;h1&gt;Navigation&amp;lt;/h1&gt;
 &amp;lt;a href='#'&gt;Link 1&amp;lt;/a&gt;
 &amp;lt;a href='#'&gt;Link 2&amp;lt;/a&gt;
&amp;lt;/div&gt;
&lt;/pre&gt;
&lt;p&gt;Change this to:&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;div class="sidenav"&gt;
 &amp;lt;h1&gt;Navigation&amp;lt;/h1&gt;
 &amp;lt;?php echo $this-&gt;renderElement('menus/sidenav'); ?&gt; 
&amp;lt;/div&gt;
&lt;/pre&gt;
&lt;p&gt;You can add other page elements the same way you did the side navigation bar.&lt;/p&gt;
&lt;p&gt;Hopefully this gives you an idea of how templates work in CakePHP.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-822612298762229316?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/822612298762229316'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/822612298762229316'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/10/site-templates-with-cakephp.html' title='Site Templates with CakePHP'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-864454505760929449</id><published>2008-10-05T21:48:00.000-07:00</published><updated>2008-10-05T23:24:28.455-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>My Experience with JQuery</title><content type='html'>&lt;p&gt;I had a project recently where I needed to make an online contact management website with similar functionality to Outlook and ACT!.&lt;/p&gt;
&lt;p&gt;I wrote all the javascript from scratch for the first version of the site.  I ended up writing over 1000 lines of code to add various features, such as ajax search, autocomplete, input masking, and security. &lt;/p&gt;
&lt;p&gt;The code was getting too complex to manage so I decided to look for an alternative.  I came across several javascript libraries, including &lt;a href='http://mootools.net/'&gt;mootools&lt;/a&gt; and &lt;a href='http://developer.yahoo.com/yui/'&gt;YUI&lt;/a&gt;.  I tried each of these, but the learning curve was too steep.  I would spend hours just trying to implement a simple feature.  This ended up being worse than my custom code.&lt;/p&gt;
&lt;p&gt;The next library I tried was &lt;a href='http://jquery.com/'&gt;jQuery&lt;/a&gt;.  At first this looked just as bad as the others, but after fooling around with it for a few hours, I fell in love.  The language is very simple to understand.  It all consists of a single $() function that replaces and improves document.getElementById().  Here are some examples.&lt;/p&gt;
&lt;pre name="code" class="jscript"&gt;
//Select the element with id "test"
$("#test")
//Select elements with a class of "style1"
$(".style1")
//Select all &amp;lt;p&gt; and &amp;lt;blockquote&gt; tags
$("p, blockquote")
//Select all input fields inside of a form
$("form input")
&lt;/pre&gt;
&lt;p&gt;Most selections that works in CSS work with the $() function.  All jQuery functions start with a selection like this.  Different functions are chained together after this to perform tasks.  For example:&lt;/p&gt;
&lt;pre name="code" class="jscript"&gt;
//Change the color of all &amp;lt;p&gt; tags to red and 
//add the word "hello" to the end of each.
$("p").css('color','red').append('hello');
&lt;/pre&gt;
&lt;p&gt;In the previous example, this paragraph would turn into:&lt;/p&gt;
&lt;p style='color:red;'&gt;In the previous example, this paragraph would turn into:hello&lt;/p&gt;
&lt;p&gt;The built-in functions can acomplish a lot, but the real power of jQuery comes from the plugins.  jQuery is one of the few frameworks that completely supports 3rd part plugins.  Some common ones add things like tabs, accordions, autocomplete, tooltips, rich text editing, and so on.&lt;/p&gt;
&lt;p&gt;All you need to use one of these plugins is to include the script.  Here's an example using the tooltips plugin.&lt;/p&gt;
&lt;pre name="code" class="jscript"&gt;
&amp;lt;script type='text/javascript' src='jquery.js'&gt;&amp;lt;/script&gt;
&amp;lt;script type='text/javascript' src='jquery.tooltips.js'&gt;&amp;lt;/script&gt;
&amp;lt;script type='text/javascript'&gt;
//Add a tooltip to every element with class 'tooltip'
$(".tooltip").tooltip();

//Add a tooltip with custom options
$(".custom").tooltip({ 
    track: true, //follow the mouse
    delay: 0, //show tooltip instantly
    fade: 250 //fade tooltip out
});
&amp;lt;/script&gt;
&lt;/pre&gt;
&lt;p&gt;Here's a list of the plugins I used for my contact management site:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href='http://www.meiocodigo.com/tag/mask/'&gt;Meio Mask&lt;/a&gt; - This conforms entered input to a specified format or "mask".  For example, if you enter a phone number, no matter how you enter it, it will always look like "(123) 456 - 7890".  I used this to standardize entered data and to make searching easier.&lt;/li&gt;
  &lt;li&gt;&lt;a href='http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/'&gt;Tooltip&lt;/a&gt; - This plugin lets you add tooltips to any element.  They can be anywhere from simple text to advanced layouts with images.  I used this on any icons to provide a nice user interface.&lt;/li&gt;
  &lt;li&gt;&lt;a href='http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/'&gt;Autocomplete&lt;/a&gt; - Provides an easy way to add autocomplete fields to your site.  I used this for my ajax search and to suggest city and country names to reduce misspellings and standardize data.&lt;/li&gt;
  &lt;li&gt;&lt;a href='http://code.google.com/p/jquery-jec/'&gt;Editable Combo Box&lt;/a&gt; - This lets you type your own value in a select box.  I used this along with some server side scripting to allow users to easily add options to drop down menus.&lt;/li&gt;
  &lt;li&gt;&lt;a href='http://www.kelvinluck.com/assets/jquery/datePicker/v2/demo/'&gt;Date Picker&lt;/a&gt; - This creates pop-up calendars for entering dates.  I used this on all my date fields to provide an alternate way to enter data.&lt;/li&gt;
  &lt;li&gt;&lt;a href='http://docs.jquery.com/UI/Tabs'&gt;Tabs&lt;/a&gt; - This creates an element with tabs containing different content.  I used this to fit more information in a single place.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The uncompressed jQuery framework with these plugins is close to 200kb.  This is a little much to load for every page.  Luckily, there are ways to reduce the size by more than half.  The easiest way is to use the &lt;a href='http://www.vlead.in/resources/tools/minify-javascript/'&gt;Javascript Minifier&lt;/a&gt;.  This removes all comments and unnecessary spaces and linefeeds.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-864454505760929449?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/864454505760929449/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=864454505760929449' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/864454505760929449'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/864454505760929449'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/10/my-experience-with-jquery.html' title='My Experience with JQuery'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-7111014963556602808</id><published>2008-09-30T22:22:00.000-07:00</published><updated>2008-09-30T23:37:00.516-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Making a Unit Converter</title><content type='html'>&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;INCHES_IN_FOOT = 12&lt;/li&gt;
  &lt;li&gt;CENTIMETERS_IN_FOOT = 30.48&lt;/li&gt;
  &lt;li&gt;MILES_IN_FOOT = 0.000189&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We could also define constants for the second step, but it is not really needed.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;FEET_IN_INCH = (INCHES_IN_FOOT)&lt;sup&gt;-1&lt;/sup&gt; = 1/12&lt;/li&gt;
  &lt;li&gt;FEET_IN_CENTIMETER = (CENTIMETERS_IN_FOOT)&lt;sup&gt;-1&lt;/sup&gt; = 1/30.48&lt;/li&gt;
  &lt;li&gt;FEET_IN_MILE = (MILES_IN_FOOT)&lt;sup&gt;-1&lt;/sup&gt; = 1/0.000189&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that we have constants, the first step is fairly simple.  I'll use a generic switch case statement to demonstrate.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
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);
&lt;/pre&gt;
&lt;p&gt;The second step is almost identical to the first.  We just multiply by the constant instead of divide.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
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);
&lt;/pre&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h3&gt;Java&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;pre name="code" class="java"&gt;
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;
  }
}
&lt;/pre&gt;
&lt;h3&gt;PHP&lt;/h3&gt;
&lt;p&gt;PHP does allow Stings in a switch statement, but I still embedded it in a helper function for readability.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
//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;
  }
}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-7111014963556602808?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/7111014963556602808/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=7111014963556602808' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/7111014963556602808'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/7111014963556602808'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/09/making-unit-converter.html' title='Making a Unit Converter'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-6542308592348844017</id><published>2008-09-30T00:54:00.000-07:00</published><updated>2008-09-30T00:55:01.769-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>The Easiest Way to Code CSS</title><content type='html'>&lt;p&gt;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 &lt;em&gt;"browser"&lt;/em&gt; that Dreamweaver uses only displays correctly for very simple websites.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Firebug lets you modify, add, and delete CSS properties from inside the browser and instantly update the page without reloading.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;You can download Firebug at &lt;a href='https://addons.mozilla.org/en-US/firefox/addon/1843'&gt;https://addons.mozilla.org/en-US/firefox/addon/1843&lt;/a&gt;.  Click below for a screenshot of me changing CSS on the fly with Firebug.&lt;/p&gt;
&lt;a href='http://jeremydorn.com/demos/firebug.gif'&gt;&lt;img src='http://jeremydorn.com/demos/firebug-small.gif'&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-6542308592348844017?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/6542308592348844017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=6542308592348844017' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/6542308592348844017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/6542308592348844017'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/09/easiest-way-to-code-css.html' title='The Easiest Way to Code CSS'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-7649991011381531764</id><published>2008-09-27T10:15:00.000-07:00</published><updated>2008-09-27T11:37:58.442-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Using Constants in PHP</title><content type='html'>&lt;p&gt;Constants are an underused part of PHP.  If you have a website with more than 5 pages, you should probably be using them.&lt;/p&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
function addPicture($picture) {
  //Picture bigger than 100,000 bytes
  if($picture=&gt;size &gt; 100000)
    return false;
  //Picture's name is larger than 20 characters
  else if(strlen($picture=&gt;name) &gt; 20)
    return false;
  //Picture is not an accepted file type
  else if(strpos('jpg, gif, png',$picture=&gt;type)===false)
    return false;
  //Passed all validation
  else
    return $picture=&gt;add();
}
&lt;/pre&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;p&gt;Your image must meet the following requirements:&amp;lt;/p&gt;
&amp;lt;ul&gt;
  &amp;lt;li&gt;It can't be larger than 100kb&amp;lt;/li&gt;
  &amp;lt;li&gt;The file name can't be longer than 20 characters&amp;lt;/li&gt;
  &amp;lt;li&gt;It must be in one of the following formats: jpg, gif, png&amp;lt;/li&gt;
&amp;lt;/ul&gt;
&lt;/pre&gt;
&lt;p&gt;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. &lt;/p&gt;
&lt;p&gt;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 &lt;a href='http://us2.php.net/manual/en/function.define.php'&gt;define()&lt;/a&gt; function.  Here's the constants.php script that declares all our constants:&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
//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));
&lt;/pre&gt;
&lt;p&gt;Now we need to add these constants into our pages.  Here's the revised addPicture() function:&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
//Load the constant declarations
include "constants.php";

function addPicture($picture) {
  //Picture bigger than MAX_FILE_SIZE
  if($picture=&gt;size &gt; MAX_FILE_SIZE)
    return false;
  //Picture's name is larger than MAX_FILENAME_LENGTH characters
  else if(strlen($picture=&gt;name) &gt; MAX_FILENAME_LENGTH)
    return false;
  //Picture is not an accepted file type
  else if(strpos(FILE_TYPES,$picture=&gt;type)===false)
    return false;
  //Passed all validation
  else
    return $picture=&gt;add();
}
&lt;/pre&gt;
&lt;p&gt;Here's our revised instruction page:&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
&amp;lt;?php 
//Load the constant declarations
include "constants.php"; 
?&gt;

&amp;lt;p&gt;Your image must meet the following requirements:&amp;lt;/p&gt;
&amp;lt;ul&gt;
  &amp;lt;li&gt;It can't be larger than &amp;lt;?php 
      //display max file size in kilobytes
      echo MAX_FILE_SIZE_KB;
    ?&gt;kb&amp;lt;/li&gt;
  &amp;lt;li&gt;The file name can't be longer than &amp;lt;?php
      //Display max number of characters for file name
      echo MAX_FILENAME_LENGTH;
    ?&gt; characters&amp;lt;/li&gt;
  &amp;lt;li&gt;It must be in one of the following formats: &amp;lt;?php
      //Display list of allowed file types
      echo FILE_TYPES;
    ?&gt;&amp;lt;/li&gt;
&amp;lt;/ul&gt;
&lt;/pre&gt;
&lt;p&gt;Hopefully this gives you an idea about how to use constants in your site.  Here are some things to keep in mind about constants:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;They are not variables and do not have a '$' in front.&lt;/li&gt;
  &lt;li&gt;They are generally named in all caps with underscores between words.&lt;/li&gt;
  &lt;li&gt;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 &lt;a href='http://us2.php.net/manual/en/function.serialize.php'&gt;serialize()&lt;/a&gt; function.&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-7649991011381531764?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/7649991011381531764/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=7649991011381531764' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/7649991011381531764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/7649991011381531764'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/09/using-constants-in-php.html' title='Using Constants in PHP'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-8366238780425493192</id><published>2008-09-16T23:58:00.000-07:00</published><updated>2008-09-16T23:59:18.388-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Real Time Stock Quotes with PHP</title><content type='html'>&lt;p&gt;I wanted to create a custom widget to put in a website that pulls and formats stock data in real time.  I came across &lt;a href='http://www.phptoys.com/e107_plugins/content/content.php?content.65'&gt;Micro Stock&lt;/a&gt;, a free PHP script that does just that.  This script was ok, but the code was written for PHP4 and not very flexible.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
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("/&amp;lt;h1&gt;([^&amp;lt;]*)&amp;lt;\/h1&gt;[\s]*&amp;nbsp;[\s]*\([a-zA-z]*,([^\)]*)\)/",$page,$temp);
    if(count($temp)&gt;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('/&amp;lt;span class="pr" id="'.$span_id.'"&gt;('.$decimal.')&amp;lt;\/span&gt;&amp;lt;br&gt;\n&amp;lt;span class=bld&gt;&amp;lt;span class="'.$span_class.'" id="'.$span_id.'"&gt;([\+\-]?)('.$decimal.')&amp;lt;\/span&gt;&amp;lt;\/span&gt;\n&amp;lt;span class="'.$span_class.'" id="'.$span_id.'"&gt;\([\+\-]?('.$decimal.')%\)&amp;lt;\/span&gt;/',$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)&gt;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;
}
&lt;/pre&gt;
&lt;p&gt;The process is fairly straight forward.  First, load the page into a variable.  Then, pick out key data by searching for unique patterns.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;My example function only works with Google Finance, but the regular expressions could be altered to work with other sites.&lt;/p&gt;
&lt;p&gt;Below is the return value when called with the following url: &lt;a href='http://finance.google.com/finance?q=goog'&gt;http://finance.google.com/finance?q=goog&lt;/a&gt;&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
Array
(
    [name] =&gt; Google Inc.
    [symbol] =&gt; NASDAQ:GOOG
    [price] =&gt; 442.93
    [diff] =&gt; 9.07
    [percent] =&gt; 2.09
    [dir] =&gt; +
    [text] =&gt; &amp;lt;span class="pr" id="ref_694653_l"&gt;442.93&amp;lt;/span&gt;&amp;lt;br&gt;
&amp;lt;span class=bld&gt;&amp;lt;span class="chg" id="ref_694653_c"&gt;+9.07&amp;lt;/span&gt;&amp;lt;/span&gt;
&amp;lt;span class="chg" id="ref_694653_cp"&gt;(2.09%)&amp;lt;/span&gt;
)
&lt;/pre&gt;
&lt;p&gt;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 &lt;a href='http://jeremydorn.com/demos/stocks.php'&gt;http://jeremydorn.com/demos/stocks.php&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-8366238780425493192?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/8366238780425493192/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=8366238780425493192' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/8366238780425493192'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/8366238780425493192'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/09/real-time-stock-quotes-with-php.html' title='Real Time Stock Quotes with PHP'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-2188637436449218389</id><published>2008-09-02T23:16:00.000-07:00</published><updated>2008-09-02T23:41:08.141-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>Using Javascript with Textfields</title><content type='html'>&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Here's what I'll cover:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Selecting text on focus&lt;/li&gt;
  &lt;li&gt;Creating a live preview&lt;/li&gt;
  &lt;li&gt;Keyboard shortcuts (ie. ctrl+b to bold text)&lt;/li&gt;
  &lt;li&gt;Advanced macros&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Selecting Text On Focus&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;textarea onFocus='this.select();'&gt;Click to select&amp;lt;/textarea&gt;
&lt;/pre&gt;
&lt;strong&gt;Example:&lt;/strong&gt;
&lt;textarea style='vertical-align:top;'cols=40 onFocus='this.select();'&gt;&lt;div class='clickhere'&gt;&lt;a href='clickhere.com'&gt;Click Here&lt;/a&gt;&lt;/div&gt;&lt;/textarea&gt;
&lt;h3&gt;Creating a Live Preview&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;First, the javascript.&lt;/p&gt;
&lt;pre name="code" class="js"&gt;
//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;
}
&lt;/pre&gt;
&lt;p&gt;Now, the html.&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;!--Text area that user types in--&gt;
&amp;lt;textarea onKeyUp='updatePreview(this.value);'&gt;&amp;lt;/textarea&gt;

&amp;lt;!--Element where preview is displayed--&gt;
&amp;lt;div id='preview'&gt;&amp;lt;/div&gt;
&lt;/pre&gt;
&lt;script type='text/javascript'&gt;//Puts html in the preview pane
function updatePreview01(html) {
  //Get preview element
  target = document.getElementById('preview01');
  //Replace element's contents with passed text
  target.innerHTML = html;
}&lt;/script&gt;
&lt;strong&gt;Example:&lt;/strong&gt;
&lt;div style='height:100px;'&gt;
&lt;div style='float:left;width:250px; height:100%;padding-right:20px;'&gt;
Preview
&lt;div id='preview01' style='border:1px solid gray;height:70px;'&gt;No Text&lt;/div&gt;
&lt;/div&gt;
&lt;div style='height:100%;'&gt;Type HTML code here:&lt;br/&gt;&lt;textarea onKeyUp='updatePreview01(this.value);' style='height:70px;'&gt;&lt;/textarea&gt;  &lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Keyboard Shortcuts&lt;/h3&gt;
&lt;p&gt;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".&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;pre name="code" class="js"&gt;
//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
}
&lt;/pre&gt;
&lt;p&gt;And here's the HTML.&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;textarea onKeyDown='interpretKey(event);'&gt;&amp;lt;/textarea&gt;
&lt;/pre&gt;
&lt;script type='text/javascript'&gt;
//Find out which key is pressed
function interpretKey01(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
      alert("Data saved");//Save the data
}
&lt;/script&gt;
&lt;strong&gt;Example:&lt;/strong&gt;
&lt;textarea style='vertical-align:top;' cols=40 onKeyDown='interpretKey01(event);'&gt;Press Ctrl+S while this textbox is in focus to save.&lt;/textarea&gt;
&lt;h3&gt;Advanced Macros&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Let's say you often type the following skeleton for an HTML file in a textfield:&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;html&gt;
&amp;lt;head&gt;
  &amp;lt;title&gt;title&amp;lt;/title&gt;
&amp;lt;/head&gt;

&amp;lt;body&gt;

&amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;First, the javascript.&lt;/p&gt;
&lt;pre name="code" class="js"&gt;
//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": 
    "&amp;lt;html&gt;\n"+
    "&amp;lt;head&gt;\n"+
    "  &amp;lt;title&gt;title&amp;lt;/title&gt;\n"+
    "&amp;lt;/head&gt;\n"+
    "&amp;lt;body&gt;\n\n&amp;lt;/body&gt;\n"+
    "&amp;lt;/html&gt;",
  //Where to place cursor after inserting (from start of insert)
  "cursor_offset":
    52 //Places cursor 52 characters from start (Between the &amp;lt;body&gt; 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&amp;lt;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 ();
}
&lt;/pre&gt;
&lt;p&gt;Now for the HTML.&lt;/p&gt;
&lt;pre name="code" class="js"&gt;
&amp;lt;textarea id='textbox' onKeyDown='handleMacro(event);'&gt;&amp;lt;/textarea&gt;
&lt;/pre&gt;
&lt;p&gt;Using this technique, you can easily define multiple macros.  For a list of javascript key codes, check out &lt;a href='http://www.cambiaresearch.com/c4/702b8cd1-e5b0-42e6-83ac-25f0306e3e25/Javascript-Char-Codes-Key-Codes.aspx'&gt;http://www.cambiaresearch.com/c4/702b8cd1-e5b0-42e6-83ac-25f0306e3e25/Javascript-Char-Codes-Key-Codes.aspx&lt;/a&gt;&lt;/p&gt;

&lt;script type='text/javascript'&gt;
//Define macros array
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": 
    "&lt;html&gt;\n"+
    "&lt;head&gt;\n"+
    "  &lt;title&gt;title&lt;/title&gt;\n"+
    "&lt;/head&gt;\n"+
    "&lt;body&gt;\n\n&lt;/body&gt;\n"+
    "&lt;/html&gt;",
  //Where to place cursor after inserting (from start of insert)
  "cursor_offset":
    52 //Places cursor 52 characters from start (Between the &lt;body&gt; tags)
};
function handleMacro01(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&lt;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("textbox01");
  
  //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 ();
}
&lt;/script&gt;
&lt;strong&gt;Example:&lt;/strong&gt;
&lt;textarea style='width:300px; height:150px; vertical-align:top;' id='textbox01' onKeyDown='handleMacro01(event);'&gt;Put your cursor somewhere and press "Alt+1" to insert an HTML document skeleton&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-2188637436449218389?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/2188637436449218389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=2188637436449218389' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/2188637436449218389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/2188637436449218389'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/09/using-javascript-with-textfields.html' title='Using Javascript with Textfields'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-777917149684993230</id><published>2008-08-21T21:40:00.000-07:00</published><updated>2008-08-21T21:52:38.320-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>MyISAM vs InnoDB</title><content type='html'>&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;To test these catagories, I created two identical tables in the same database.  One using MyISAM, one using InnoDB.&lt;/p&gt;
&lt;pre name="code" class="sql"&gt;
 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 
&lt;/pre&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;h3&gt;Speed&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h4&gt;Insert 1,000 rows in one query&lt;/h4&gt;
&lt;p&gt;
&lt;strong&gt;MyISAM&lt;/strong&gt;: An incredible .0094 second average&lt;br/&gt;&lt;strong&gt;InnoDB&lt;/strong&gt;: A respectable .0849 second average
&lt;/p&gt;
&lt;h4&gt;SELECT 10,000 Rows&lt;/h4&gt;
&lt;p&gt;
&lt;strong&gt;MyISAM&lt;/strong&gt;: A fast .0121 second average&lt;br/&gt;&lt;strong&gt;InnoDB&lt;/strong&gt;: A decent .0302 second average
&lt;/p&gt;
&lt;h4&gt;Advanced UPDATE Query&lt;/h4&gt;
&lt;pre name="code" class="sql"&gt;
/* 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%';
&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;MyISAM&lt;/strong&gt;: A good .0112 second average&lt;br/&gt;&lt;strong&gt;InnoDB&lt;/strong&gt;: A slightly worse .0282 second average
&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;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.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;h3&gt;Storage&lt;/h3&gt;
&lt;p&gt;Here I'm comparing the disk space the two tables use when storing data.&lt;/p&gt;
&lt;h4&gt;Empty Table&lt;/h4&gt;
&lt;p&gt;
&lt;strong&gt;MyISAM&lt;/strong&gt;: 1kb of disk space&lt;br/&gt;&lt;strong&gt;InnoDB&lt;/strong&gt;: 32kb of disk space
&lt;/p&gt;
&lt;h4&gt;1,000 Rows&lt;/h4&gt;
&lt;p&gt;
&lt;strong&gt;MyISAM&lt;/strong&gt;: 166kb of disk space&lt;br/&gt;&lt;strong&gt;InnoDB&lt;/strong&gt;: 256kb of disk space
&lt;/p&gt;
&lt;h4&gt;10,000 Rows&lt;/h4&gt;
&lt;p&gt;
&lt;strong&gt;MyISAM&lt;/strong&gt;: 1,341kb of disk space&lt;br/&gt;&lt;strong&gt;InnoDB&lt;/strong&gt;: 2,080kb of disk space
&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;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.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;h3&gt;Stability&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;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.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;h3&gt;Special Features&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h4&gt;Transaction Support&lt;/h4&gt;
&lt;p&gt;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.&lt;/p&gt;  
&lt;p&gt;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.&lt;/p&gt;
&lt;h4&gt;Full Text Support&lt;/h4&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Which feature is better depends entirely on what your application is.&lt;/p&gt;
  &lt;/li&gt; 
&lt;/ol&gt;
&lt;h3&gt;And the winner is...&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;There are certain types of applications that should use MyISAM, most notably Search Engines, but for most applications, InnoDB is the right choice.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-777917149684993230?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/777917149684993230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=777917149684993230' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/777917149684993230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/777917149684993230'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/08/myisam-vs-innodb.html' title='MyISAM vs InnoDB'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-8889810058600638151</id><published>2008-08-17T11:01:00.000-07:00</published><updated>2008-08-17T11:02:29.429-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Making a Captcha Verification Image</title><content type='html'>&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Let's start with generating the string.  This is the first part of captcha.php&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
&amp;lt;?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&amp;lt;5;$i++)
{
  $string .= $chars{rand(0,strlen($chars)-1)};
}

//Store our string in a session variable
$_SESSION['captcha'] = $string;
&lt;/pre&gt;
&lt;p&gt;Now let's generate the image of this string, which is a little more complicated.  This is part 2 of the captcha.php file.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
//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&amp;lt;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&amp;lt;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=&gt;bottom left x position,
  1=&gt;bottom left y position,
  2=&gt;bottom right x position,
  3=&gt;bottom right y position,
  4=&gt;top right x position,
  5=&gt;top right y position,
  6=&gt;top left x position,
  7=&gt;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);
?&gt;
&lt;/pre&gt;
&lt;p&gt;Here a link to the font file: &lt;a href='http://www.urbanfonts.com/fonts/Annifont.htm'&gt;http://www.urbanfonts.com/fonts/Annifont.htm&lt;/a&gt;.  Now you should be able to test your captcha image.  Here's a demo of what it should look like: &lt;a href='http://jeremydorn.com/demos/captcha.php'&gt;http://jeremydorn.com/demos/captcha.php&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we add it to our form.  This part is easy.&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;img src='captcha.php' /&gt;&amp;lt;br /&gt;
Enter the text above: 
&amp;lt;input type='text' name='captcha' /&gt;
&lt;/pre&gt;
&lt;p&gt;Finally, we check to see if they enter the right code.  This next part goes in your validation script.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
if(isset($_SESSION['captcha']) &amp;&amp;
$_REQUEST['captcha']==$_SESSION['captcha'])  {
  //correct code
}
else {
  //incorrect code
}
&lt;/pre&gt;
&lt;p&gt;To see a sample form, go to &lt;a href='http://jeremydorn.com/demos/captcha_form.php'&gt;http://jeremydorn.com/demos/captcha_form.php&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-8889810058600638151?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/8889810058600638151/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=8889810058600638151' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/8889810058600638151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/8889810058600638151'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/08/making-captcha-verification-image.html' title='Making a Captcha Verification Image'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-477026656755439012</id><published>2008-08-15T18:48:00.000-07:00</published><updated>2008-08-17T11:32:43.396-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Mysql Transactions with PHP</title><content type='html'>&lt;p&gt;One feature of Mysql that is often overlooked is transactions.  Transactions allow you to preform multiple queries and rollback the changes if any one fails.&lt;/p&gt;
&lt;p&gt;In mysql, only InnoDB tables have transaction support.  MyISAM, which is often the default table type, &lt;strong&gt;does not&lt;/strong&gt; support this.&lt;/p&gt;
&lt;p&gt;Let's say you are transferring funds between 2 accounts.  You withdraw the money in one query and deposit the money in another one.  What happens if the deposit query fails?  The money just disappears.  Transactions are an easy way to solve this problem.&lt;/p&gt;
&lt;p&gt;We must add some helper functions to our mysql config file.  First we'll make a wrapper function for mysql_query().  It takes a query and an optional error message.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
function query_db($query,$error="Error")
{
  //perform query
  $result = mysql_query($query);
  if(!$result) //query fails
  {
    //exit with error message
    exit($error."&amp;lt;br&gt;$query&amp;lt;br&gt; ".mysql_error());
  }
  else //query succeeds
    return $result;
}
&lt;/pre&gt;
&lt;p&gt;Next, are three transaction functions, begin_transaction(), commit_transaction(), and rollback_transaction().&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
//Begins the transaction
//Every query after this can be rolled back
function begin_transaction()
{
  query_db("SET AUTOCOMMIT=0");
  query_db("BEGIN");

  //Set the $begin_transaction variable to true 
  //so that other functions know a transaction started
  global $begin_transaction;
  $begin_transaction = true;
}

//Commits all queries.  Changes cannot be undone after this
function commit_transaction()
{
  //if transaction is started, commit changes
  global $begin_transaction;
  if($begin_transaction)
    query_db("COMMIT");

  //update global variable
  $begin_transaction = false;
}

//Rolls back all queries. All changes are canceled.
function rollback_transaction()
{
  //if transaction is started, roll back changes
  global $begin_transaction;
  if($begin_transaction)
    query_db("ROLLBACK");

  //update global variable
  $begin_transaction = false;
}
&lt;/pre&gt;
&lt;p&gt;We need to alter the query_db() function to rollback changes if an error occurs.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
function query_db($query,$error="Error")
{
  //get global variable
  global $begin_transaction;

  //perform query
  $result = mysql_query($query);
  if(!$result) //query fails 
  {
    //if transaction is started, rollback changes
    if($begin_transaction)
      rollback_transaction();

    //exit with error message
    exit($error."&amp;lt;br&gt;$query&amp;lt;br&gt; ".mysql_error());
  }
  else //query succeeds
    return $result;
}
&lt;/pre&gt;
&lt;p&gt;That's all you need to use transactions.  Here's an example of how to use this.&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
&amp;lt;?php
begin_transaction();
  query_db("UPDATE accounts 
    SET balance = balance-500
    WHERE id=1"
  );
  query_db("UPDATE accounts 
    SET balance = balance+500    
    WHERE id=2"
  );
commit_transaction();
?&gt;
&lt;/pre&gt;
&lt;p&gt;If either query fails, both accounts are unaffected.  If both queries succeed, the accounts are updated.&lt;/p&gt;
&lt;p&gt;Sometimes, you want to make sure a row is affected, not just that the query succeeded.  I didn't build this into the functions, but it's easy to implement.  Here's a simple way to do it:&lt;/p&gt;
&lt;pre name="code" class="php"&gt;
&amp;lt;?php
begin_transaction();
  query_db("UPDATE accounts 
    SET balance = balance-500
    WHERE id=1"
  );
  if(!mysql_affected_rows()) //no rows affected
    exit("error".rollback_transaction());

  query_db("UPDATE accounts 
    SET balance = balance+500    
    WHERE id=2"
  );
  if(!mysql_affected_rows()) //no rows affected
    exit("error".rollback_transaction());

commit_transaction();
?&gt;
&lt;/pre&gt;
&lt;p&gt;Transactions also help with testing an application.  You can perform 20 queries, do some tests, and then roll back the changes automatically.  Think of how much time this could save.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-477026656755439012?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/477026656755439012/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=477026656755439012' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/477026656755439012'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/477026656755439012'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/08/mysql-transactions-with-php.html' title='Mysql Transactions with PHP'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-5444536120459133020</id><published>2008-08-14T21:12:00.000-07:00</published><updated>2009-03-06T10:49:58.505-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>AJAX Form Validation</title><content type='html'>&lt;p&gt;Many sites today validate form inputs as the user types. These validations are usually simple, like checking if a username is available, but contribute to a great user interface. I'm going to walk through how to do this with PHP and AJAX, and the advantages and disadvantages.&lt;/p&gt;
&lt;p&gt;I'm going to make a simple form with two inputs, a username and a password. I'm then going to validate the inputs as the user types using 2 different methods.&lt;/p&gt;
&lt;p&gt;Let's start with the form.&lt;/p&gt;
&lt;pre class="html" name="code"&gt;
&amp;lt;html&gt;
&amp;lt;head&gt;
  &amp;lt;title&gt;Form&amp;lt;/title&gt;
&amp;lt;/head&gt;
&amp;lt;body&gt;
&amp;lt;form&gt;
  Username:
  &amp;lt;input type='text' id='username' /&gt;
  &amp;lt;br /&gt;Password:
  &amp;lt;input type='password' id='password' /&gt;
&amp;lt;/form&gt;
&amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;

&lt;p&gt;The first field we'll validate is the password field. We'll create a validatePassword() javascript function that makes sure the password is at least 6 characters long.&lt;/p&gt;

&lt;pre class="jscript" name="code"&gt;
&amp;lt;script type='text/javascript'&gt;
function validatePassword() {
  var field = document.getElementById('password');

  var test = true;

  //If password is less than 6 characters
  if(field.value.length &lt; test =" false;" backgroundcolor =" 'green';" backgroundcolor =" 'red';"&gt;
&lt;/pre&gt;
&lt;p&gt;Now we need to add a keyup event to our password input.&lt;/p&gt;
&lt;pre class="html" name="code"&gt;
&amp;lt;input type='password' id='password'
onKeyUp='validatePassword();' /&gt;
&lt;/pre&gt;
&lt;p&gt;Now we are going to validate the username by making sure it's available. We can't do this just using javascript since we have to check a database, so we use AJAX and PHP instead. Here's the php page (validateUsername.php).&lt;/p&gt;
&lt;pre class="php" name="code"&gt;
&amp;lt;?php
//include mysql config file
include 'mysql.php';

//get passed username
$username = $_REQUEST['username'];

//sanitize passed username
$username = mysql_real_escape_string($username);

//make sure username is not empty
if(empty($username))
  exit("false");

//query the database for matches
$query = "SELECT id FROM users WHERE username='$username'";
$return = mysql_query($query) or
  die ("Error getting matches: " . mysql_error());

//If a match exists, return false, else return true
if(mysql_num_rows($return)&gt;0)
  echo 'false';
else
  echo 'true';
?&gt;&lt;/pre&gt;
&lt;p&gt;If the username 'jsmith' is already taken, validateUsername.php?username=jsmith would output 'false'. Otherwise it would output 'true'. Now we need ajax to interact with this page. I'm going to use &lt;a href="http://www.prototypejs.org/" target="_blank"&gt;Prototype&lt;/a&gt; for the ajax to make the code easier to understand.&lt;/p&gt;
&lt;pre class="javascript" name="code"&gt;
&amp;lt;script type='text/javascript' src='prototype-1.6.0.2.js'&gt;&amp;lt;/script&gt;
&amp;lt;script type='text/javascript'&gt;
function validateUsername()
{
  //get username input
  var field = document.getElementById('username');

  //Don't use ajax if username is empty
  if(field.value.length == 0)
  {
    field.style.backgroundColor = 'red';
    return;
  }

  //Initialize new ajax request
  new Ajax.Request('validateUsername.php', {
    method: 'get',
    parameters: {
      username: field.value
    },
    //If a response is sent back
    onSuccess: function(transport){
      //get response text
      var response = transport.responseText;

      if(response=='true') //validation passed
        field.style.backgroundColor = 'green';
      else //validation failed
        field.style.backgroundColor = 'red';
    }
  });
}
&amp;lt;/script&gt;
&lt;/pre&gt;
&lt;p&gt;Once again we need to add a keyup event to our input.&lt;/p&gt;
&lt;pre class="html" name="code"&gt;
&amp;lt;input type='text' id='username'
onKeyUp='validateUsername();' /&gt;
&lt;/pre&gt;
&lt;p&gt;Now we have all out parts. Before I put it all together and show you a demo, understand that validating a form with Ajax should be the last resort. The only time you should use Ajax is if you must interact with a database. Anything else is unnecessary load on your servers. I do feel, however, that using Ajax &lt;em&gt;where needed&lt;/em&gt; is well worth the server load.&lt;/p&gt;

&lt;p&gt;Here is a link to a &lt;a href="http://jeremydorn.com/demos/ajax_validation.html"&gt;demo&lt;/a&gt;. Below is the complete source of the html page. The php source is above.&lt;/p&gt;
&lt;pre class="html" name="code"&gt;
&amp;lt;html&gt;
&amp;lt;head&gt;
  &amp;lt;title&gt;Form&amp;lt;/title&gt;

  &amp;lt;script type='text/javascript'&gt;
  function validatePassword() {
    var field = document.getElementById('password');

    var test = true;

    //If password is less than 6 characters
    if(field.value.length &amp;lt; 6)
   test = false;

    if(test) //validation passed
    {
     //Change input background green
     field.style.backgroundColor = 'green';
    }
    else  //Validation failed
    {
   //Change input background red
   field.style.backgroundColor = 'red';
    }
  }
  &amp;lt;/script&gt;
  &amp;lt;script type='text/javascript' src='prototype-1.6.0.2.js'&gt;&amp;lt;/script&gt;
  &amp;lt;script type='text/javascript'&gt;
  function validateUsername()
  {
    //get username input
    var field = document.getElementById('username');

    //Don't use ajax if username is empty
    if(field.value.length == 0)
    {
      field.style.backgroundColor = 'red';
      return;
    }

    //Initialize new ajax request
    new Ajax.Request('validateUsername.php', {
      method: 'get',
      parameters: {
        username: field.value
      },
      //If a response is sent back
      onSuccess: function(transport){
        //get response text
        var response = transport.responseText;
 
        if(response=='true') //validation passed
          field.style.backgroundColor = 'green';
        else //validation failed
          field.style.backgroundColor = 'red';
      }
    });
  }
  &amp;lt;/script&gt;
&amp;lt;/head&gt;
&amp;lt;body&gt;
&amp;lt;form&gt;
  Username:
  &amp;lt;input type='text' id='username'
onKeyUp='validateUsername();' /&gt;
  &amp;lt;br /&gt;Password:
  &amp;lt;input type='password' id='password'
onKeyUp='validatePassword();' /&gt;
&amp;lt;/form&gt;
&amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-5444536120459133020?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/5444536120459133020/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=5444536120459133020' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/5444536120459133020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/5444536120459133020'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/08/ajax-form-validation.html' title='AJAX Form Validation'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-5282394169963259716</id><published>2008-08-13T20:28:00.000-07:00</published><updated>2008-08-14T16:21:26.063-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xhtml'/><title type='text'>Converting HTML to XHTML</title><content type='html'>&lt;p&gt;Here are some tips on making an html page xhtml compliant.&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;Include a proper doctype&lt;/dt&gt;
&lt;dd&gt;A doctype is the first line of code in a web page that tells the browser the file type.  Here are two xhtml doctypes, strict and transitional:&lt;br /&gt;
&lt;pre name='code' class='xml'&gt;
&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&amp;gt;
&lt;/pre&gt;
&lt;pre name='code' class='xml'&gt;
&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;
&lt;/pre&gt;
&lt;/dd&gt;
&lt;dt&gt;Close all tags&lt;/dt&gt;
&lt;dd&gt;With HTML, you can get away with tags like &amp;lt;img ...&amp;gt; and &amp;lt;br&amp;gt;.  With XHTML, you must close these tags as follows: &amp;lt;img ... /&amp;gt; and &amp;lt;br /&amp;gt;.  In general, make sure every start tag has an end tag.&lt;br /&gt;
&lt;/dd&gt;
&lt;dt&gt;Remove deprecated tags&lt;/dt&gt;
&lt;dd&gt;Many tags can be replaced with css, such as &amp;lt;center&gt; and &amp;lt;font color=red&gt;.  Use &amp;lt;div style='text-align:center'&gt; or &amp;lt;span style='color:red;'&gt; instead.&lt;br /&gt;
&lt;/dd&gt;
&lt;dt&gt;Remove deprecated attributes&lt;/dt&gt;
&lt;dd&gt;As with tags, many attributes can be replaced with css.  &lt;br/&gt;Replace &amp;lt;img width=500 height=300 border=0 /&gt; with &amp;lt;img style='width:500px; height:300px; border:0px;' /&gt;&lt;br /&gt;
&lt;/dd&gt;
&lt;dt&gt;Include required attributes&lt;/dt&gt;
&lt;dd&gt;XHTML is very strict when it comes to attributes.  Some are forbidden (width, height) and some are required (alt).  The required attribute that most comes to mind is the alt tag for images.  If you don't have one, your site won't validate.&lt;br /&gt;
&lt;/dd&gt;
&lt;dt&gt;Use tables correctly&lt;/dt&gt;
&lt;dd&gt;Tables have one purpose and one purpose only, to display tabular data.  You wouldn't use Excel to layout a webpage, why use tables?  &lt;br /&gt;This is the hardest part to change if you're currently using a table for your site layout, seeing how at least half of your code is &amp;lt;tr&gt; and &amp;lt;td&gt; tags. It's often best to start from scratch with &amp;lt;div&gt; tags.
&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;These were off the top of my head, so I'm sure I missed a bunch of things. W3's site has a good XHTML validator that provides useful feedback.  Give it a try at &lt;a href='http://validator.w3.org/'&gt;http://validator.w3.org/&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-5282394169963259716?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/5282394169963259716/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=5282394169963259716' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/5282394169963259716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/5282394169963259716'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/08/converting-html-to-xhtml.html' title='Converting HTML to XHTML'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-7422490855098013423</id><published>2008-08-12T18:15:00.000-07:00</published><updated>2008-08-12T22:44:35.095-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Basic user accounts with PHP and MySQL</title><content type='html'>&lt;p&gt;This is a tutorial on adding basic user accounts to your site.&lt;/p&gt;

&lt;strong&gt;Objective:&lt;/strong&gt;
&lt;ol&gt;
&lt;li&gt;Create a registration and sign in page.&lt;/li&gt;
&lt;li&gt;Require users to log in to view designated pages.&lt;/li&gt;
&lt;/ol&gt;

&lt;strong&gt;Target Audience:&lt;/strong&gt;
&lt;ol&gt;
&lt;li&gt;Someone with basic MySQL and PHP knowledge.&lt;/li&gt;
&lt;li&gt;Someone who wants user accounts without using a framework like Joomla or CakePHP.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First, let's create the 'users' mysql table.&lt;/p&gt;
&lt;pre name='code' class='sql'&gt;
CREATE TABLE `users` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(20) NOT NULL,
`password_hash` varchar(40) NOT NULL,
`password_salt` varchar(8) NOT NULL,
`created` datetime default NULL,
PRIMARY KEY  (`id`)
);
&lt;/pre&gt;
&lt;p&gt;To add some security to the site, I'm encrypting the passwords (hence the password_salt and password_hash fields).  This stops anyone from knowing a password by looking in the database.&lt;/p&gt;

&lt;p&gt;Next, we'll make the sign up form (signup.php).  This is where users will register for your site.&lt;/p&gt;
&lt;pre name='code' class='php'&gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Sign Up&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;?php
if(isset($_REQUEST['username'])) //Form submitted
{
 //Connect to database
 $conn = mysql_connect('localhost', 'root', 'password') 
  or die('Could not connect: ' . mysql_error());
 mysql_select_db('database_name');

 //Sanitize entered info
 $username = mysql_real_escape_string($_REQUEST['username']);
 $password = mysql_real_escape_string($_REQUEST['password']);
 
 //Validate entered info
 $test = true;
 if(empty($username) || empty($password))
  $test = false;

 if($test) //Validation passed
 {
  //Generate random 8 character password salt
  $password_salt = "";

  //characters to choose from
  $chars = "0123456789abcdefghijklmnopqrstuvwxyz-_%#"; 
  for($C=0;$C&amp;lt;8;$C++)
  {
   $password_salt .= $chars{rand(0,strlen($chars)-1)};
  }
  
  //Generate hash based on entered password and salt
  $password_hash = md5($password_salt.$password);

  //Insert user in database
  $query = "INSERT INTO users 
    (username,password_salt,password_hash,created) VALUES 
    ('$username','$password_salt','$password_hash',NOW())";
  mysql_query($query) or 
    die ("Error creating new user: " . mysql_error());

  //Display success message and exit
  exit(
    "Account created successfully.  You may now 
    &amp;lt;a href='signin.php'&amp;gt;Sign In&amp;lt;/a&amp;gt;"
  );
 }
 else //Validation failed
  echo "Please enter all information";
?&amp;gt;
&amp;lt;h1&amp;gt;Sign Up&amp;lt;/h1&amp;gt;
&amp;lt;form action="signup.php" method="post"&amp;gt;
Username: 
&amp;lt;input type="text" name="username" /&amp;gt;&amp;lt;br /&amp;gt;
Password: 
&amp;lt;input type="password" name="password" /&amp;gt;&amp;lt;br /&amp;gt;
&amp;lt;input type="submit" value="Sign Up" /&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Make sure to change the mysql connection settings above to match your configuration.&lt;/p&gt;

&lt;p&gt;Lastly, we'll make the sign in page (signin.php).  If a user tries to access a restricted page, they will be redirected here.&lt;/p&gt;
&lt;pre name='code' class='php'&gt;
&amp;lt;?php
session_start(); //Start session so we can sign user in
?&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Sign In&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;?php
if(isset($_REQUEST['username'])) //Form submitted
{
 //Connect to database
 $conn = mysql_connect('localhost', 'root', 'password') 
  or die('Could not connect: ' . mysql_error());
 mysql_select_db('database_name');

 //Sanitize entered info
 $username = mysql_real_escape_string($_REQUEST['username']);
 $password = mysql_real_escape_string($_REQUEST['password']);
 
 //Select users with enetered username from database
 $query = "
    SELECT id,username,password_salt,password_hash 
    FROM users 
    WHERE username='$username'";
 $result = mysql_query($query) 
    or die("Error looking up user: " . mysql_error());
 
 
 if($row=mysql_fetch_assoc($result))//Row is returned
 {
  //Generate hash based on entered password and stored salt
  $password_hash = md5($row['password_salt'].$password);
  
  //If User entered correct password
  if($password_hash == $row['password_hash'])
  {
   //Sign them in by storing their id in a session variable
   $_SESSION['userid']=$row['id'];
   
   //Show message and exit
   exit( "You are successfully signed in." );
  }
  else //Incorrect password
  {
   echo "Incorrect Password";
  }
 }
 else //Incorrect Username
 {
  echo "Incorrect Username";
 }
}
//Show sign in form
?&amp;gt;
&amp;lt;h1&amp;gt;Sign In&amp;lt;/h1&amp;gt;
&amp;lt;form action="signin.php" method="post"&amp;gt;
Username: 
&amp;lt;input type="text" name="username" /&amp;gt;&amp;lt;br /&amp;gt;
Password: 
&amp;lt;input type="password" name="password" /&amp;gt;&amp;lt;br /&amp;gt;
&amp;lt;input type="submit" value="Sign In" /&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Again, make sure you change the mysql settings.&lt;/p&gt;

&lt;p&gt;Now that people can sign up and sign in, you need a way to require this.  Put this at the top of a page to require the user to sign in:&lt;/p&gt;
&lt;pre name='code' class='php'&gt;
&amp;lt;?php
session_start();//Start the session
if(!isset($_SESSION['userid']))//User not signed in
{
 header("Location: signin.php");//Redirect to sign in page
 exit();//Stop script from executing
}
?&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Here are a few things to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;"session_start();" must be the first line in a file.  If there are any characters or whitespace before the opening php tag, it will not work.&lt;/li&gt;
&lt;li&gt;This post is meant to explain how something works, not the best way to implement it.  There are many improvements to be made and I may address them in the future.&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-7422490855098013423?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeremydorn.blogspot.com/feeds/7422490855098013423/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1752795022597091910&amp;postID=7422490855098013423' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/7422490855098013423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/7422490855098013423'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/08/basic-user-accounts-with-php-and-mysql.html' title='Basic user accounts with PHP and MySQL'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1752795022597091910.post-7073172936259267155</id><published>2008-08-11T21:55:00.000-07:00</published><updated>2008-08-15T18:25:36.924-07:00</updated><title type='text'>Welcome</title><content type='html'>&lt;p&gt;Welcome to Jeremy Dorn Web Design&lt;/p&gt;

&lt;p&gt;I'm a web developer and student at Arizona State University.&lt;/p&gt;

&lt;p&gt;When I have a web design question, I often find the answer on various blogs.  This is my attempt to give back.&lt;/p&gt;

&lt;p&gt;I plan to include useful code snippets, commentary on different web technologies, and tutorials on various subjects.&lt;/p&gt;

&lt;p&gt;Check back soon.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1752795022597091910-7073172936259267155?l=jeremydorn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/7073172936259267155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1752795022597091910/posts/default/7073172936259267155'/><link rel='alternate' type='text/html' href='http://jeremydorn.blogspot.com/2008/08/welcom.html' title='Welcome'/><author><name>Jeremy Dorn</name><uri>http://www.blogger.com/profile/11188414799396662015</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry></feed>
