Building Modern Web Applications – Second Edition
AJAX is a complex phenomenon that means different things to different people. Computer users appreciate that their favorite websites are now friendlier and feel more responsive. Web developers learn new skills that empower them to create sleek web applications with little effort. Indeed, everything sounds good about AJAX! At its roots, AJAX is a mix of technologies that lets you get rid of the evil page reload, which represents the dead time when navigating from one page to another. Eliminating page reloads is just one step away from enabling more complex features into websites, such as real-time data validation, drag-and-drop, and other tasks that weren’t traditionally associated with web applications. Although the AJAX ingredients are mature (the XMLHttpRequest object, which is the heart of AJAX, was created by Microsoft in 1999), their new role in the new wave of web trends is very young, and we’ll witness a number of changes before these technologies will be properly used to the best benefit of the end users.
also read:
AJAX isn’t, of course, the answer to all the Web’s problems, as the current hype around it may suggest. As with any other technology, AJAX can be overused, or used the wrong way. AJAX also comes with problems of its own: you need to fight with browser inconsistencies, AJAX-specific pages don’t work on browsers without JavaScript, they can’t be easily bookmarked by users, and search engines don’t always know how to parse them. Also, not everyone likes AJAX. While some are developing enterprise architectures using JavaScript, others prefer not to use it at all. When the hype is over, most will probably agree that the middle way is the wisest way to go for most scenarios.
In AJAX and PHP: Building Modern Web Applications – Second Edition, we take a pragmatic and safe approach by teaching relevant patterns and best practices that we think any web developer will need sooner or later. We teach you how to avoid the common pitfalls, how to write efficient AJAX code, and how to achieve functionality that is easy to integrate into current and future web applications, without requiring you to rebuild the whole solution around AJAX. You’ll be able to use the knowledge you learn from this book right away, in your PHP web applications.
What This Book Covers
Chapter 1: The World of AJAX and PHP is all about a quick introduction to the world of AJAX. In order to proceed with learning how to build AJAX applications, it’s important to understand why and where they are useful. It describes the XMLHttpRequest object, which is the key element that enables the client-side JavaScript code to call a page on the server asynchronously.
Chapter 2: JavaScript and the AJAX Client walks you through many fields such as working with HTML, JavaScript, CSS, the DOM, XML, and XMLHttpRequest. It discusses the theory (and practice) that you will need to know to make these components come together smoothly, and form a solid foundation for your future AJAX applications. It also shows you how to implement simple error-handling techniques, and how to write code efficiently.
Chapter 3: Object Oriented JavaScript covers a large area of what object-oriented programming means in the world of JavaScript starting from basic features and going far into the execution context of functions. It teaches you the basic OOP concepts—encapsulation, polymorphism, and inheritance, how to work with JavaScript objects, functions, classes, and prototypes, how to simulate private, instance, and static class members in JavaScript, what the JavaScript execution context is, how to implement inheritance by using constructor functions and prototyping, and the basics of JSON.
Chapter 4: Using PHP and MySQL on the Server starts putting the server to work, using PHP to generate dynamic output, and MySQL to manipulate and store the backend data. This chapter shows you how to use XML and JSON with PHP (so that you can create server-side code that communicates with your JavaScript client), how to implement errorhandling code in your server-side PHP code, and how to work with MySQL databases.
Chapter 5: AJAX Form Validation creates a form validation application that implements traditional techniques with added AJAX flavor, thereby making the form more userfriendly, responsive, and pleasing. The intention of this chapter isn’t to build the perfect validation technique but, rather, a working proof of concept that takes care of user input and ensures its validity.
Chapter 6: Debugging and Profiling AJAX Applications teaches how to enable and use Internet Explorer’s debugging capabilities. It shows how you can work with Web Development Helper, Developer Toolbar, and other Internet Explorer tools and with Firefox plugins such as Firebug, Venkman JavaScript Debugger, and Web Developer.
Chapter 7: Advanced Patterns and Techniques briefly covers some of the most important patterns and techniques covering usability, security, and techniques. Looking at methods, patterns, and techniques is so important that it has developed into its own science and has
created a set of guidelines for typical problems that offer us predictable results.
Chapter 8: AJAX Chat with jQuery teaches how to use AJAX to easily implement an online chat solution. This will also be your opportunity to use one of the most important JavaScript frameworks around—jQuery. More precisely, this chapter will explain the
basics of jQuery and show how to create a simple, yet efficient clientserver chat mechanism using AJAX.
Chapter 9: AJAX Grid explains the usage of an AJAX-enabled data grid plugin, jqGrid.
Appendix: Preparing Your Working Environment covers the installation instructions that set up your machine for the exercises in this book. It also covers preparing the database that is used in many examples throughout the book.
AJAX Form Validation
Input data validation is an essential feature for any modern software application. In the case of web applications, validation is an even more sensitive area because your application is widely reachable by many users with varying skill sets (and intentions). Validation is not something that you can play with—invalid data has the potential to harm the application’s functionality, result in errant and inaccurate reporting, and even corrupt the application’s most sensitive area—the database.
Validating data requires checking whether the data entered by the user complies with rules established in accordance with the business rules of your application before allowing it to be used. For example, if dates must be entered in the YYYY-MM-DD format, then a date of February 28 would be invalid. Email addresses and phone numbers are other examples of data that should be checked against valid formats.
In addition, validation must guard against “SQL injection”—which could corrupt, control, and/or access your data and database.
The importance of carefully defining input data validation rules
and consistently applying those rules cannot be overstated!
Historically, web form validation was implemented primarily on the server side, after the form was submitted. In some cases, on the client side, there was also some JavaScript code that performed simple validation such as checking whether the email address was valid or if a field had been left blank. But, there were a few problems with traditional web form validation techniques:
- Server-side form validation butted up against the limits of the HTTP protocol—a stateless protocol. Unless special code was written to deal with this issue, submitting a page with invalid data had the user receiving an empty form as a reply, and then, much to his chagrin, the entire form had to be filled in again from scratch. How annoying.
- After submitting the page, the user waited (not so) patiently for a full-page reload. With every mistake made in filling out the form, the annoying “new page reload with blank form” happened.
In this chapter, we will create a form validation application that implements traditional techniques with added AJAX fl avor, thereby making the form more user-friendly, responsive, and pleasing. In the AJAX world, entered data is validated on the fly, so the users are never confronted with waiting for full-page reloads or the rude “blank form” as a reply. The server is the last line of defense against invalid data, so even if you implement client-side validation, server-side validation is mandatory. The JavaScript code that
runs on the client can be disabled permanently from the browser’s settings and/or it can be easily modified or bypassed.
Implementing AJAX form validation
The form validation application we will build in this chapter validates the form at the server side on the classic form submit, implementing AJAX validation while the user navigates through the form. The final validation is performed at the server, as shown in Figure 5-1:
Doing a final server-side validation when the form is submitted should never be considered optional. If someone disables JavaScript in the browser settings, AJAX validation on the client side clearly won’t work, exposing sensitive data, and thereby allowing an evil-intentioned visitor to harm important data on the server (forexample, through SQL injection).
Always validate user input on the server.
As shown in the preceding figure, the application you are about to build validates a registration form using both AJAX validation (client side) and typical server-side validation:
- AJAX-style (client side): It happens when each form field loses focus (onblur). The field’s value is immediately sent to and evaluated by the server, which then returns a result (0 for failure, 1 for success). If validation fails, an error message will appear and notify the user about the failed validation, as shown in Figure 5-3.
- PHP-style (server side): This is the usual validation you would do on the server—checking user input against certain rules after the entire form is submitted. If no errors are found and the input data is valid, the browser is redirected to a success page, as shown in Figure 5-4. If validation fails, however, the user is sent back to the form page with the invalid fields highlighted, as shown in Figure 5-3.
Both AJAX validation and PHP validation check the entered data against our application’s rules:
- Username must not already exist in the database
- Name field cannot be empty
- A gender must be selected
- Month of birth must be selected
- Birthday must be a valid date (between 1-31)
- Year of birth must be a valid year (between 1900-2000)
- The date must exist in the number of days for each month (that is, there’s no February 31)
- E-mail address must be written in a valid email format
- Phone number must be written in standard US form: xxx-xxx-xxxx
- The I’ve read the Terms of Use checkbox must be selected
Watch the application in action in the following screenshots:



XMLHttpRequest, version 2
As in this book we do our best to combine theory and practice, before moving on to implementing the AJAX form validation script, we’ll have another quick look at our favorite AJAX object—XMLHttpRequest. On this occasion, we will step up the complexity (and functionality) a bit and use everything we have learned until now. We will continue to build on what has come before as we move on; so again, it’s important that you take the time to be sure you’ve understood what we are doing here. Time spent on digging into the materials
really pays off when you begin to build your own application in the real world. In Chapter 2, we took a sneak peak at the XMLHttpRequest object—the nexus of the AJAX world. Back then, we didn’t have any OOP JavaScript skills. We’ve seen the power hidden in JavaScript in Chapter 3. In Chapter 4, we saw how PHP works together with AJAX requests.
Our OOP JavaScript skills will be put to work improving the existing script that used to make AJAX requests. In addition to the design that we’ve already discussed, we’re creating the following features as well:
- Flexible design so that the object can be easily extended for future needs and purposes
- The ability to set all the required properties via a JSON object
We’ll package this improved XMLHttpRequest functionality in a class named XmlHttp that we’ll be able to use in other exercises as well. You can see the class diagram in the following screenshot, along with the diagrams of two helper classes:
- settings is the class we use to create the call settings; we supply an instance of this class as a parameter to the constructor of XmlHttp
- complete is a callback delegate, pointing to the function we want executed when the call completes
The final purpose of this exercise is to create a class named XmlHttp that we can easily use in other projects to perform AJAX calls. This class is an improvement of the async.js script that you built in Chapter 2, JavaScript and the AJAX Client.

With our goals in mind, let’s get to it!
Time for action – the XmlHttp object
- In the ajax folder, create a folder named validate, which will host the exercises in this chapter.
- In the validate folder, create a new file named xhr.js and add the following code to it:
- To quickly test the functionality of your XmlHttp class, create a new file named xhrtest.html and add the following code to it:
- Now create async.txt with some text in it, and then load http://localhost/ajax/validate/xhrtest.html. Figure 5-6 shows our result:
// XmlHttp constructor can receive request settings: // url - the server url // contentType - request content type // type - request type (default is GET) // data - optional request parameters // async - whether the request is asynchronous (default is true) // showErrors - display errors // complete - the callback function to call when the request // completes function XmlHttp(settings) { // store the settings object in a class property this.settings = settings; // override default settings with those received as parameter // the default url points to the current page var url = location.href; if (settings.url) url = settings.url; // the default content type is the content type for forms var contentType = "application/x-www-form-urlencoded"; if (settings.contentType) contentType = settings.contentType; // by default the request is done through GET var type = "GET"; if(settings.type) type = settings.type; // by default there are no parameters sent var data = null; if(settings.data) { data = settings.data; // if we go through GET we properly adjust the URL if(type == "GET") url = url + "?" + data; } // by default the postback is asynchronous var async = true; if(settings.async) async = settings.async; // by default we show all the infrastructure errors var showErrors = true; if(settings.showErrors) showErrors = settings.showErrors; // create the XmlHttpRequest object var xhr = XmlHttp.create(); // set the postback properties xhr.open(type, url, async); xhr.onreadystatechange = onreadystatechange; xhr.setRequestHeader("Content-Type", contentType); xhr.send(data); // the function that displays errors <B>function displayError(message)</B> { // ignore errors if showErrors is false if (showErrors) { // display error message alert("Error encountered: \n" + message); } } // the function that reads the server response function readResponse() { try { // retrieve the response content type var contentType = xhr.getResponseHeader("Content-Type"); // build the json object if the response has one if (contentType == "application/json") { response = JSON.parse(xhr.responseText); } // get the DOM element if the response is XML else if (contentType == "text/xml") { response = xhr.responseXml; } // by default get the response as text else { response = xhr.responseText; } // call the callback function if any if (settings.complete) settings.complete (xhr, response, xhr.status); } catch (e) { displayError(e.toString()); } } // called when the request state changes function onreadystatechange() { // when readyState is 4, we read the server response if (xhr.readyState == 4) { // continue only if HTTP status is "OK" if (xhr.status == 200) { try { // read the response from the server readResponse(); } catch(e) { // display error message displayError(e.toString()); } } else { // display error message displayError(xhr.statusText); } } } } // static method that returns a new XMLHttpRequest object XmlHttp.create = function() { // will store the reference to the XMLHttpRequest object var xmlHttp; // create the XMLHttpRequest object try { // assume IE7 or newer or other modern browsers xmlHttp = new XMLHttpRequest(); } catch(e) { // assume IE6 or older try { xmlHttp = new ActiveXObject("Microsoft.XMLHttp"); } catch(e) { } } // return the created object or display an error message if (!xmlHttp) alert("Error creating the XMLHttpRequest object."); else return xmlHttp; }
<html> <head> <script type="text/javascript" src="xhr.js"></script> </head> <body> <div id="test"> </div> <script> XmlHttp ({url:'async.txt', complete:function(xhr,response,status) { document.getElementById("test").innerHTML = response; } }); </script> </body> </html>

What just happened?
The code listed above contains significant code from the previous examples and then some new code. Let’s break it down into small pieces and analyze it. The chosen name for our reusable object is XmlHttp. Its functionality is wrapped in two functions:
- XmlHttp.create(): A static method of the XmlHttp object that creates a XmlHttpRequest object
- XmlHttp(): The constructor of the XmlHttp object
From a design point of view, the XmlHttp object represents a wrapper around the XmlHttpRequest object. The XmlHttp.create() method contains the same code that we have previously seen in the createXmlHttpRequestObject() method. It simply acts like a factory for an XmlHttpRequest object. The constructor of the XmlHttp object, although it can look quite scary at first sight, actually contains very simple code—provided that you know the theory from Chapter 3, Object Oriented JavaScript. The constructor receives as a parameter a JSON object containing all the settings for the XmlHttp object. Choosing a JSON object is both convenient from the extensibility point of view and easy from the programming point of view. We store the settings in a property with the same name.
function XmlHttp(settings) { // store the settings object in a class property this.settings = settings;
The settings object contains the following properties that will be mainly used for the XmlHttpRequest object:
- url: The URL of the AJAX request
- type: The type of the request (GET or POST)
- contentType : The content type of the request
- data: The data to be sent to the server
- async: A fl ag that specifies whether the request is synchronous or asynchronous
- complete: The function called when the request completes
- showErrors: A fl ag that indicates whether infrastructure errors will be displayed or not
These are the parameters required to make an AJAX request. Even though the structure and the design of this object are simple, it can be easily extended with more advanced features, giving us the fl exibility feature we defined as a goal. The fl exibility offered by JSON objects means we don’t force the user to pass all the properties mentioned above each time the object is created. Instead, we created a
standard set of default values that the user can choose to overwrite when necessary. The next few lines simply implement this logic.
Making a request through GET or POST is different and we take care of it when setting the parameters for the request:
// by default there are no parameters sent var data = null; if(settings.data) { data = settings.data; // if we go through GET we properly adjust the URL if(type == "GET") url = url + "?" + data; }
After having all the settings for the AJAX request, we create the XmlHttpRequest and we open it.
// create the XmlHttpRequest object var xhr = XmlHttp.create(); // set the postback properties xhr.open(type, url, async);
The next step is to hook to the readystatechange event:
xhr.onreadystatechange = onreadystatechange;
The handler function is a inner function of the constructor and contains the same code as the handleRequestStateChange() method that you already know. Probably the most interesting piece of code is in the response handler. The readResponse() inner function is responsible for handling the response received from the server. It gets the content type of the response and, based on that, it builds the response JSON object or it retrieves the response as an XML element. If no matching content type is found, the raw text of the response is used instead.
// retrieve the response content type var contentType = xhr.getResponseHeader("Content-Type"); // build the json object if the response has one if (contentType == "application/json") { response = JSON.parse(xhr.responseText); } // get the DOM element if the response is XML else if (contentType == "text/xml") { response = xhr.responseXml; } // by default get the response as text else { response = xhr.responseText; }
After gathering the necessary data, the XmlHttp object passes it all to the callback function (settings.complete()) along with the XmlHttp object and the HTTP response code.
// call the callback function if any if (settings.complete) settings.complete (xhr, response, xhr.status);
All in all, the next time you need to call a server script asynchronously from a web page, you can count on XmlHttp to do all the dirty work. You just tell it what URL to contact, specifying the necessary parameters, and it fetches the response for you.
AJAX form validation
In the previous chapter, we talked about error handling and database operations. In this chapter, we redesigned the code for making AJAX requests when creating the XmlHttp class. The AJAX form validation application makes use of these techniques. The application contains three pages:
- One page renders the form to be validated
- Another page validates the input
- The third page is displayed if the validation is successful
The application will have a standard structure, composed of these files:
- index.php: It is the file loaded initially by the user. It contains references to the necessary JavaScript files and makes asynchronous requests for validation to validate.php.
- index_top.php: It is a helper file loaded by index.php and contains several objects for rendering the HTML form.
- validate.css: It is the file containing the CSS styles for the application.
- json2.js: It is the JavaScript file used for handling JSON objects.
- xhr.js: It is the JavaScript file that contains our XmlHttp object used for making AJAX requests.
- validate.js: It is the JavaScript file loaded together with index.php on the client side. It makes asynchronous requests to a PHP script called validate.php to perform the AJAX validation.
- validate.php: It is a PHP script residing on the same server as index.php, and it offers the server-side functionality requested asynchronously by the JavaScript code in index.php.
- validate.class.php: It is a PHP script that contains a class called Validate, which contains the business logic and database operations to support the functionality of validate.php.
- config.php: It will be used to store global configuration options for your application, such as database connection data, and so on.
- error_handler.php: It contains the error-handling mechanism that changes the text of an error message into a human-readable format.
- allok.php: It is the page to be displayed if the validation is successful.
Bearing all this in mind, it’s time to get to work!
Time for action – AJAX form validation
- If you missed the database exercise in Chapter 4, Using PHP and MySQL on the Server, connect to the ajax database and create a table named users with the following code; otherwise, skip to step 3.
- Execute the following INSERT commands to populate your users table with some sample data :
- Let’s start writing the code with the presentation tier. We’ll define the styles for our form by creating a file named validate.css, and adding the following code to it:
- Now create a new file named index_top.php, and add the following code to it. This script will be loaded from the main page index.php.
- Now create index.php, and add the following code to it:
- Create a new file named allok.php, and add the following code to it:
- Copy json2.js (which you downloaded in a previous exercise from http://json.org/json2.js) to your ajax/validate folder.
- Create a file named validate.js. This file performs the client-side functionality, including the AJAX requests:
- Now it’s time to add the server-side logic. Start by creating config.php, with the following code in it:
- Now create the error handler code in a file named error_handler.php:
- The PHP script that handles the client’s AJAX calls, and also handles the validation on form submit, is validate.php:
- The class that supports the validation functionality is called Validate, and it is hosted in a script file called validate.class.php, which looks like this:
- Test your script by loading http://localhost/ajax/validate/index.php in a web browser.
CREATE TABLE users ( user_id INT UNSIGNED NOT NULL AUTO_INCREMENT, user_name VARCHAR(32) NOT NULL, PRIMARY KEY (user_id) );
INSERT INTO users (user_name) VALUES ('bogdan'); INSERT INTO users (user_name) VALUES ('audra'); INSERT INTO users (user_name) VALUES ('cristian');
body { font-family: Arial, Helvetica, sans-serif; font-size: 0.8em; color: #000000; } label { float: left; width: 150px; font-weight: bold; } input, select { margin-bottom: 3px; } .button { font-size: 2em; } .left { margin-left: 150px; } .txtFormLegend { color: #777777; font-weight: bold; font-size: large; } .txtSmall { color: #999999; font-size: smaller; } .hidden { display: none; } .error { display: block; margin-left: 150px; color: #ff0000; }
<?php // enable PHP session session_start(); // Build HTML <option> tags <B>function buildOptions($options, $selectedOption)</B> { foreach ($options as $value => $text) { if ($value == $selectedOption) { echo '<option value="' . $value . '" selected="selected">' . $text . '</option>'; } else { echo '<option value="' . $value . '">' . $text . '</option>'; } } } // initialize gender options array <B>$genderOptions = array("0" => "[Select]",</B> "1" => "Male", "2" => "Female"); // initialize month options array <B>$monthOptions = array("0" => "[Select]",</B> "1" => "January", "2" => "February", "3" => "March", "4" => "April", "5" => "May", "6" => "June", "7" => "July", "8" => "August", "9" => "September", "10" => "October", "11" => "November", "12" => "December"); // initialize some session variables to prevent PHP throwing // Notices if (!isset($_SESSION['values'])) { $_SESSION['values']['txtUsername'] = ''; $_SESSION['values']['txtName'] = ''; $_SESSION['values']['selGender'] = ''; $_SESSION['values']['selBthMonth'] = ''; $_SESSION['values']['txtBthDay'] = ''; $_SESSION['values']['txtBthYear'] = ''; $_SESSION['values']['txtEmail'] = ''; $_SESSION['values']['txtPhone'] = ''; $_SESSION['values']['chkReadTerms'] = ''; } if (!isset($_SESSION['errors'])) { $_SESSION['errors']['txtUsername'] = 'hidden'; $_SESSION['errors']['txtName'] = 'hidden'; $_SESSION['errors']['selGender'] = 'hidden'; $_SESSION['errors']['selBthMonth'] = 'hidden'; $_SESSION['errors']['txtBthDay'] = 'hidden'; $_SESSION['errors']['txtBthYear'] = 'hidden'; $_SESSION['errors']['txtEmail'] = 'hidden'; $_SESSION['errors']['txtPhone'] = 'hidden'; $_SESSION['errors']['chkReadTerms'] = 'hidden'; } ?>
<?php require_once ('index_top.php'); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www. w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Degradable AJAX Form Validation with PHP and MySQL</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="validate.css" rel="stylesheet" type="text/css" /> </head> <body onload="setFocus();"> <script type="text/javascript" src="json2.js"></script> <script type="text/javascript" src="xhr.js"></script> <script type="text/javascript" src="validate.js"></script> <fieldset> <legend class="txtFormLegend"> New User Registratio Form </legend> <br /> <form name="frmRegistration" method="post" action="validate.php"> <input type="hidden" name="validationType" value="php"/> <!-- Username --> <label for="txtUsername">Desired username:</label> <input id="txtUsername" name="txtUsername" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values'] ['txtUsername'] ?>" /> <span id="txtUsernameFailed" class="<?php echo $_SESSION['errors']['txtUsername'] ?>"> This username is in use, or empty username field. </span> <br /> <!-- Name --> <label for="txtName">Your name:</label> <input id="txtName" name="txtName" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtName'] ?>" /> <span id="txtNameFailed" class="<?php echo $_SESSION['errors']['txtName']?>"> Please enter your name. </span> <br /> <!-- Gender --> <label for="selGender">Gender:</label> <select name="selGender" id="selGender" onblur="validate(this.value, this.id)"> <?php buildOptions($genderOptions, $_SESSION['values']['selGender']); ?> </select> <span id="selGenderFailed" class="<?php echo $_SESSION['errors']['selGender'] ?>"> Please select your gender. </span> <br /> <!-- Birthday --> <label for="selBthMonth">Birthday:</label> <!-- Month --> <select name="selBthMonth" id="selBthMonth" onblur="validate(this.value, this.id)"> <?php buildOptions($monthOptions, $_SESSION['values']['selBthMonth']); ?> </select> - <!-- Day --> <input type="text" name="txtBthDay" id="txtBthDay" maxlength="2" size="2" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtBthDay'] ?>" /> - <!-- Year --> <input type="text" name="txtBthYear" id="txtBthYear" maxlength="4" size="2" onblur="validate(document.getElementById ('selBthMonth').options[document.getElementById ('selBthMonth').selectedIndex].value + '#' + document.getElementById('txtBthDay').value + '#' + this.value, this.id)" value="<?php echo $_SESSION['values']['txtBthYear'] ?>" /> <!-- Month, Day, Year validation --> <span id="selBthMonthFailed" class="<?php echo $_SESSION['errors']['selBthMonth'] ?>"> Please select your birth month. </span> <span id="txtBthDayFailed" class="<?php echo $_SESSION['errors']['txtBthDay'] ?>"> Please enter your birth day. </span> <span id="txtBthYearFailed" class="<?php echo $_SESSION['errors']['txtBthYear'] ?>"> Please enter a valid date. </span> <br /> <!-- Email --> <label for="txtEmail">E-mail:</label> <input id="txtEmail" name="txtEmail" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtEmail'] ?>" /> <span id="txtEmailFailed" class="<?php echo $_SESSION['errors']['txtEmail'] ?>"> Invalid e-mail address. </span> <br /> <!-- Phone number --> <label for="txtPhone">Phone number:</label> <input id="txtPhone" name="txtPhone" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtPhone'] ?>" /> <span id="txtPhoneFailed" class="<?php echo $_SESSION['errors']['txtPhone'] ?>"> Please insert a valid US phone number (xxx-xxx-xxxx). </span> <br /> <!-- Read terms checkbox --> <input type="checkbox" id="chkReadTerms" name="chkReadTerms" class="left" onblur="validate(this.checked, this.id)" <?php if ($_SESSION['values']['chkReadTerms'] == 'on') echo 'checked="checked"' ?> /> I've read the Terms of Use <span id="chkReadTermsFailed" class="<?php echo$_SESSION['errors'] ['chkReadTerms'] ?>"> Please make sure you read the Terms of Use. </span> <!-- End of form --> <hr /> <span class="txtSmall">Note: All fields arerequired. </span> <br /><br /> <input type="submit" name="submitbutton" value="Register" class="left button" /> </form> </fieldset> </body> </html>
<?php // clear any data saved in the session session_start(); session_destroy(); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title><b>AJAX</b> Form Validation</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="validate.css" rel="stylesheet" type="text/css" /> </head> <body> Registration Successful!<br /> <a href="index.php" title="Go back"><< Go back</a> </body> </html>
// holds the remote server address var serverAddress = "validate.php"; // when set to true, display detailed error messages var showErrors = true; // the function handles the validation for any form field function validate(inputValue, fieldID) { // the data to be sent to the server through POST var data = "validationType=ajax&inputValue=" + inputValue + "&fieldID=" + fieldID; // build the settings object for the XmlHttp object var settings = { url: serverAddress, type: "POST", async: true, complete: function (xhr, response, status) { if (xhr.responseText.indexOf("ERRNO") >= 0 || xhr.responseText.indexOf("error:") >= 0 || xhr.responseText.length == 0) { alert(xhr.responseText.length == 0 ? "Server error." : response); } result = response.result; fieldID = response.fieldid; // find the HTML element that displays the error message = document.getElementById(fieldID + "Failed"); // show the error or hide the error message.className = (result == "0") ? "error" : "hidden"; }, data: data, showErrors: showErrors }; // make a server request to validate the input data var xmlHttp = new XmlHttp(settings); } // sets focus on the first field of the form function setFocus() { document.getElementById("txtUsername").focus(); }
<?php // defines database connection data define('DB_HOST', 'localhost'); define('DB_USER', 'ajaxuser'); define('DB_PASSWORD', 'practical'); define('DB_DATABASE', 'ajax'); ?>
<?php // set the user error handler method to be error_handler set_error_handler('error_handler', E_ALL); // error handler function function error_handler($errNo, $errStr, $errFile, $errLine) { // clear any output that has already been generated if(ob_get_length()) ob_clean(); // output the error message $error_message = 'ERRNO: ' . $errNo . chr(10) . 'TEXT: ' . $errStr . chr(10) . 'LOCATION: ' . $errFile . ', line ' . $errLine; echo $error_message; // prevent processing any more PHP scripts exit; } ?>
<?php // start PHP session session_start(); // load error handling script and validation class require_once ('error_handler.php'); require_once ('validate.class.php'); // Create new validator object $validator = new Validate(); // read validation type (PHP or <b>AJAX</b>?) $validationType = ''; if (isset($_POST['validationType'])) { $validationType = $_POST['validationType']; } // <b>AJAX</b> validation or PHP validation? if ($validationType == 'php') { // PHP validation is performed by the ValidatePHP method, //which returns the page the visitor should be redirected to //(which is allok.php if all the data is valid, or back to //index.php if not) header('Location:' . $validator->ValidatePHP()); } else { // <b>AJAX</b> validation is performed by the Validate<b>AJAX</b> method. //The results are used to form a JSON document that is sent //back to the client $response = array('result' => $validator->Validate<b>AJAX</b> ($_POST['inputValue'],$_POST['fieldID']), 'fieldid' => $_POST['fieldID'] ); // generate the response if(ob_get_length()) ob_clean(); header('Content-Type: application/json'); echo json_encode($response); } ?>
<?php // load error handler and database configuration require_once ('config.php'); // Class supports AJAX and PHP web form validation class Validate { // stored database connection private $mMysqli; // constructor opens database connection function __construct() { $this->mMysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE); } // destructor closes database connection function __destruct() { $this->mMysqli->close(); } // supports AJAX validation, verifies a single value public function ValidateAJAX($inputValue, $fieldID) { // check which field is being validated and perform // validation switch($fieldID) { // Check if the username is valid case 'txtUsername': return $this->validateUserName($inputValue); break; // Check if the name is valid case 'txtName': return $this->validateName($inputValue); break; // Check if a gender was selected case 'selGender': return $this->validateGender($inputValue); break; // Check if birth month is valid case 'selBthMonth': return $this->validateBirthMonth($inputValue); break; // Check if birth day is valid case 'txtBthDay': return $this->validateBirthDay($inputValue); break; // Check if birth year is valid case 'txtBthYear': return $this->validateBirthYear($inputValue); break; // Check if email is valid case 'txtEmail': return $this->validateEmail($inputValue); break; // Check if phone is valid case 'txtPhone': return $this->validatePhone($inputValue); break; // Check if "I have read the terms" checkbox has been // checked case 'chkReadTerms': return $this->validateReadTerms($inputValue); break; } } // validates all form fields on form submit public function ValidatePHP() { // error flag, becomes 1 when errors are found. $errorsExist = 0; // clears the errors session flag if (isset($_SESSION['errors'])) unset($_SESSION['errors']); // By default all fields are considered valid $_SESSION['errors']['txtUsername'] = 'hidden'; $_SESSION['errors']['txtName'] = 'hidden'; $_SESSION['errors']['selGender'] = 'hidden'; $_SESSION['errors']['selBthMonth'] = 'hidden'; $_SESSION['errors']['txtBthDay'] = 'hidden'; $_SESSION['errors']['txtBthYear'] = 'hidden'; $_SESSION['errors']['txtEmail'] = 'hidden'; $_SESSION['errors']['txtPhone'] = 'hidden'; $_SESSION['errors']['chkReadTerms'] = 'hidden'; // Validate username if (!$this->validateUserName($_POST['txtUsername'])) { $_SESSION['errors']['txtUsername'] = 'error'; $errorsExist = 1; } // Validate name if (!$this->validateName($_POST['txtName'])) { $_SESSION['errors']['txtName'] = 'error'; $errorsExist = 1; } // Validate gender if (!$this->validateGender($_POST['selGender'])) { $_SESSION['errors']['selGender'] = 'error'; $errorsExist = 1; } // Validate birth month if (!$this->validateBirthMonth($_POST['selBthMonth'])) { $_SESSION['errors']['selBthMonth'] = 'error'; $errorsExist = 1; } // Validate birth day if (!$this->validateBirthDay($_POST['txtBthDay'])) { $_SESSION['errors']['txtBthDay'] = 'error'; $errorsExist = 1; } // Validate birth year and date if (!$this->validateBirthYear($_POST['selBthMonth'] . '#' . $_POST['txtBthDay'] . '#' . $_POST['txtBthYear'])) { $_SESSION['errors']['txtBthYear'] = 'error'; $errorsExist = 1; } // Validate email if (!$this->validateEmail($_POST['txtEmail'])) { $_SESSION['errors']['txtEmail'] = 'error'; $errorsExist = 1; } // Validate phone if (!$this->validatePhone($_POST['txtPhone'])) { $_SESSION['errors']['txtPhone'] = 'error'; $errorsExist = 1; } // Validate read terms if (!isset($_POST['chkReadTerms']) || !$this->validateReadTerms($_POST['chkReadTerms']) { $_SESSION['errors']['chkReadTerms'] = 'error'; $_SESSION['values']['chkReadTerms'] = ''; $errorsExist = 1; } // If no errors are found, point to a successful validation // page if ($errorsExist == 0) { return 'allok.php'; } else { // If errors are found, save current user input foreach ($_POST as $key => $value) { $_SESSION['values'][$key] = $_POST[$key]; } return 'index.php'; } } // validate user name (must be empty, and must not be already // registered) private function validateUserName($value) { // trim and escape input value $value = $this->mMysqli->real_escape_string(trim($value)); // empty user name is not valid if ($value == null) return 0; // not valid // check if the username exists in the database $query = $this->mMysqli->query('SELECT user_name FROM users' 'WHERE user_name="' . $value . '"'); if ($this->mMysqli->affected_rows > 0) return '0'; // not valid else return '1'; // valid } // validate name private function validateName($value) { // trim and escape input value $value = trim($value); // empty user name is not valid if ($value) return 1; // valid else return 0; // not valid } // validate gender private function validateGender($value) { // user must have a gender return ($value == '0') ? 0 : 1; } // validate birth month private function validateBirthMonth($value) { // month must be non-null, and between 1 and 12 return ($value == '' || $value > 12 || $value < 1) ? 0 : 1; } // validate birth day private function validateBirthDay($value) { // day must be non-null, and between 1 and 31 return ($value == '' || $value > 31 || $value < 1) ? 0 : 1; } // validate birth year and the whole date private function validateBirthYear($value) { // valid birth year is between 1900 and 2000 // get whole date (mm#dd#yyyy) $date = explode('#', $value); // date can't be valid if there is no day, month, or year if (!$date[0]) return 0; if (!$date[2] || !is_numeric($date[2])) return 0; // check the date return (checkdate($date[0], $date[1], $date[2])) ? 1 : 0; } // validate email private function validateEmail($value) { // valid email formats: *@*.*, *@*.*.*, *.*@*.*, *.*@*.*.*) return (!preg_match('/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@ [a-z0-9-]+(\.[a-z0-9-]+)* (\.[a-z]{2,3})$/i', $value)) ? 0 : 1; } // validate phone private function validatePhone($value) { // valid phone format: ###-###-#### return (!preg_match('/^[0-9]{3}-*[0-9]{3}-*[0-9]{4}$/', $value)) ? 0 : 1; } // check the user has read the terms of use private function validateReadTerms($value) { // valid value is 'true' return ($value == 'true' || $value == 'on') ? 1 : 0; } } ?>
What just happened?
The AJAX validation technique allows us to validate form fields and at the same time inform users if there were any validation errors, and the icing on the cake is that we are doing it without interrupting the user’s activity! The client-side validation is combined with a pure server-side PHP validation that takes place when the user clicks on Submit and thereby submits the entire form to the server. Because of two PHP scripts, validate.php and validate.class.php, both validation types are supported at the server. Let’s examine the code, beginning with the script that handles client-side validation, index.php. The client page is not a simple HTML file but, rather, a PHP file; portions of it will be dynamically generated at the server side. In this way, we retain the form field values when the form is submitted and server-side validation fails. Without the server-side PHP code, if the index page is reloaded, all its fields would be empty.
index.php begins by loading a helper script named index_top.php to: start the session by calling session_start(), define some variables and a function that will be used later in index.php, and initialize some session variables ($_SESSION[‘values’] and $_SESSION[‘errors’]) to avoid PHP sending notices about uninitialized variables.
Note the onload event of the body tag in index.php. It calls the setFocus() function defined in validate.js, which places the input cursor in the first field of the form. In index.php, you see the following sequence of code. Later on, we will be using this same code with additional small changes:
<!-- Username --> <label for="txtUsername">Desired username:</label> <input id="txtUsername" name="txtUsername" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtUsername'] ?>" /> <span id="txtUsernameFailed" class="<?php echo $_SESSION['errors']['txtUsername'] ?>"> This username is in use, or empty username field. </span> <br />
This is the code that displays a form field with its corresponding label and displays an error message underneath when validation fails.
In this example, we display an error message right under
the validated field, but you can customize the position
and appearance of these error messages in validate.css
by changing the properties of the error CSS class.
The onblur event of the input element that is generated when the user leaves an input element triggers the validate() JavaScript function with two parameters: the field’s value and the field’s ID (the server script needs to know which field we need to validate and what the input value is). This function will handle AJAX validation, by making an asynchronous HTTP request to the validate.php script.
The value attributes should be empty on the initial page load, but after submitting the form it should hold the input values. We use session variables to save user input on form submit, in case validation fails and the form is re-displayed. The <span> element that follows contains the error message that gets displayed on failed validation. This span is initially hidden using the hidden CSS class, but we change its CSS class into error, if validation fails.
The validate() function inside validate.js, sends an AJAX request to the server by calling validate.php with three parameters—the field’s value, the field’s ID, and AJAX as the validation type. The data to be sent to the server is formatted accordingly.
// the data to be sent to the server through POST var data = "validationType=ajax&inputValue=" + inputValue + "&fieldID=" + fieldID;
Before making the AJAX request, we build the JSON settings object to be passed to the XmlHttp constructor function.
// build the settings object for the XmlHttp object var settings = { url: serverAddress, type: "POST", async: true, complete: function (xhr, response, status) { if (xhr.responseText.indexOf("ERRNO") >= 0 || xhr.responseText.indexOf("error:") >= 0 || xhr.responseText.length == 0) { alert(xhr.responseText.length == 0 ? "Server error." : response); } result = response.result; fieldID = response.fieldid; // find the HTML element that displays the error message = document.getElementById(fieldID + "Failed"); // show the error or hide the error message.className = (result == "0") ? "error" : "hidden"; }, data: data, showErrors: showErrors };
As all of the hard work is delegated to XmlHttp, all that’s left for our function to do is to correctly interpret the response. This is where the complete callback function is used to check for any PHP errors handled by the error_handler.php module and, when there is an error, to show the error(s) in a popup message. Next, the validation result is retrieved from the JSON object. If the validation was successful, we change the CSS class of the error message to hidden; if the validation failed, it is set to error. You change the element’s CSS class using its className property. The final step is the construction of an AJAX request using the XmlHttp object and passing the JSON settings object.
The PHP script that handles server-side processing is validate.php. It starts by loading the error handling script (error_handler.php) and the Validate class that handles the data validation (validate.class.php). Then, it looks for a POST variable named validationType. This exists both when an asynchronous request is made and when the form is submitted via a hidden input field.
// read validation type (PHP or <b>AJAX</b>?) $validationType = ''; if (isset($_POST['validationType'])) { $validationType = $_POST['validationType']; }
Then, based on the value of $validationType, we perform either AJAX validation or PHP validation.
// AJAX validation or PHP validation? if ($validationType == 'php') { // PHP validation is performed by the ValidatePHP method, which ..// returns the page the visitor should be redirected to (which is // allok.php if all the data is valid, or back to index.php if not) header('Location:' . $validator->ValidatePHP()); } else { // <b>AJAX</b> validation is performed by the Validate<b>AJAX</b> method. The // results are used to form a JSON object that is sent back to the // client $response = array('result' => $validator->Validate<b>AJAX</b> ($_POST['inputValue'], $_POST['fieldID']), 'fieldid' => $_POST['fieldID'] ); // generate the response if(ob_get_length()) ob_clean(); header('Content-Type: application/json'); echo json_encode($response); } ?>
For classic, server-side validation, we call the validatePHP() method, which returns the name of the page the browser should be redirected to (which will be allok.php if the validation was successful, or index.php if not). The validation results for each field are stored in the session and should it be reloaded; index.php will indicate the fields that didn’t pass the test. In the case of AJAX calls, the server composes a response that specifies if the field is valid. The response is a JSON object that looks like this:
{"result":"1","fieldid":"txtUsername"}
If the result is 0, then txtUsername isn’t valid and it should be marked accordingly. If the result is 1, the field’s value is valid. Next, let’s look into validate.class.php, referenced in validate.php. This is the workhorse of our PHP validation. The class constructor creates a
connection to the database and the destructor closes that connection. We then have two public methods: ValidateAJAX() (AJAX validation) and ValidatePHP() (server-side validation).
PHP constructors and destructors
In PHP, the constructor is implemented as a method named
__construct(), and is executed automatically when you create
new instances of a class. Just as in other programming languages, the
constructors are useful when you have code that initializes various class
members, because you can rely on it always executing as soon as a new
object of the class is created.
At the opposite side of the object life cycle, you have the destructor,
which is a method named __destruct(), and is called automatically
when the object is destroyed. Destructors are very useful for doing
housekeeping work. In most examples, we will close the database
connection in the destructor, ensuring that we don’t leave any database
connections open, consuming unnecessary resources.
AJAX validation requires two parameters, one that holds the value to be validated ($inputValue) and one that holds the form field’s ID ($fieldID). A switch block loads specific validation for each form field. This function will return 0 if validation fails or 1 if validation is successful. The PHP validation function takes no parameters, as it validates the entire form (after form submission). First we initialize the $errorsExist fl ag to 0. Whenever validation fails for a field, this fl ag will be set to 1 and we will know validation has failed. Then we need to make sure that older session variables are unset in order to ensure that older errors are cleared. We then check each form field against a set of custom rules. If validation fails, we raise the fl ag ($errorsExist = 1) and set the session variable that sets the CSS class for error message to error. If, in the end, the $errorsExist fl ag is still set to 0, it means that the entire validation was successful and so it returns the name of the success page, thus redirecting the browser to that page. If errors are found, we save current user input into session variables, which will be used by index.php to fill the form (remember that by default, when loading the page, all fields are empty). This is how we save current user input:
foreach ($_POST as $key => $value) { $_SESSION['values'][$key] = $_POST[$key]; } In other scenarios, you can save these values even if the validation is successful, so that should the user fill in another form on our site, say an order form, they can be reused for him.
$_POST is an array holding the names and values of all form elements, and it can be walked through with foreach. This means that for each element inside the $_POST array, we create a new element inside the $_SESSION[‘values’] array. There’s nothing special to mention about validate.css. The success page (allok.php) is very simple as well—it just displays a successful submission confirmation belying all the work that’s gone on before it!
also read:
Summary
We saw how to put into practice everything that we had learned so far in JavaScript by building our own flexible, extensible, reusable object for AJAX requests. We demonstrated the application structure that we specified as well. Our intention here wasn’t to build the perfect validation technique but, rather, a working proof of the concept that takes care of user input and ensures its validity.
This validation technique isn’t possible with JavaScript alone, nor would you want to wait for the fields to be validated only on form submit. Now that we’ve finished a complete, quite complex case study, it’s time to have a quick look at some useful tools that we can use to debug and profile our AJAX code.