More Books
PHP 5 Unleashed
PHP 5 Unleashed
Table of Contents
Copyright
Lead Author
Contributing Authors
Acknowledgments
We Want to Hear from You!
Reader Services
Introduction
Organization of the Book
Part I. Working with PHP for General Web Development
Chapter 1. Basic PHP Development
How PHP Scripts Work
Basic PHP Syntax
Basic PHP Data Types
Variable Manipulation
Control Structures
User-Defined Functions
Dynamic Variables and Functions
Multiple File PHP Scripts
References
Strings in PHP
Comparing Strings
Advanced String Comparison
Search and Replacement
Formatting Strings
Strings and Locales
Formatting Date and Time Values
Summary
Chapter 2. Arrays
Basic Arrays
Implementing Arrays
More Array Materials
Chapter 3. Regular Expressions
The Basics of Regular Expressions
Limitations of the Basic Syntax
POSIX Regular Expressions
Perl-Compatible Regular Expressions (PCRE)
PCRE Modifiers
A Few Final Words
Chapter 4. Working with Forms in PHP
HTML Forms 101
Working with Form Submissions in PHP
Summary
Chapter 5. Advanced Form Techniques
Data Manipulation and Conversion
Form Data Integrity
Form Processing
Summary
Chapter 6. Persistent Data Using Sessions and Cookies
HTTP Cookies
PHP Sessions
Advanced Sessions
Summary
Chapter 7. Using Templates
The What and Why of Templates
The Smarty Template Engine
Summary
Part II. Advanced Web Development
Chapter 8. PEAR
What Is PEAR?
Getting and Installing PEAR
Using the PEAR Package Manager
Using the PEAR Website
Using PEAR Packages in Applications
Summary
Reference
Chapter 9. XSLT and Other XML Concerns
Relating XML to HTML
Using XSLT to Describe HTML Output Using XML Input
PHP4 and XSLT Using the DOM XML Module
PHP4 and XSLT Using the XSLT Module
PHP5 and XSLT
Accessing XML Data Using SimpleXML
Generating XML Documents Using PHP
Summary
References
Chapter 10. Debugging and Optimizations
Debugging Your PHP Scripts
Optimizing Your PHP Scripts
Summary
Chapter 11. User Authentication
Authenticating Users in PHP
Securing PHP Code
Summary
Chapter 12. Data Encryption
Shared Secret Versus Public Key
Shared Secret Algorithms
Public Key Cryptography
Using Public Keys in PHP
Summary
Chapter 13. Object-Oriented Programming in PHP
Why Objects?
Creating Basic Classes
Advanced Classes
Special Methods
Class Autoloading
Object Serialization
Exceptions
Iterators
Summary
Chapter 14. Error Handling
The PHP Error-Handling Model
What to Do About Errors
The Default Error Handler
Error Suppression
Custom Error Handlers
Causing Errors
Putting It All Together
Summary
Chapter 15. Working with HTML/XHTML Using Tidy
Introduction
Basic Tidy Usage
Tidy Configuration Options
Using the Tidy Parser
Applications of Tidy
Summary
Chapter 16. Writing Email in PHP
The MIME Protocol
Implementing MIME Email in PHP
Summary
Part III. Building Applications in PHP
Chapter 17. Using PHP for Console Scripting
Core CLI Differences
Working with PHP CLI
CLI Tools and Extensions
Summary
Chapter 18. SOAP and PHP
What Are Web Services?
Installation
Creating Web Services
Consuming Web Services
Looking for Web Services
Summary
Chapter 19. Building WAP-Enabled Websites
What Is WAP?
System Requirements
Introduction to WML
Serving WAP Content
Sample Applications
Summary
Part IV. I/O, System Calls, and PHP
Chapter 20. Working with the File System
Working with Files in PHP
File Permissions
File Access Support Functions
Summary
Chapter 21. Network I/O
DNS/Reverse DNS Lookups
Socket Programming
Network Helper Functions
Summary
Chapter 22. Accessing the Underlying OS from PHP
Introduction
Unix-Specific OS Functionality
Platform-Independent System Functions
A Brief Note About Security
Summary
Part V. Working with Data in PHP
Chapter 23. Introduction to Databases
Using the MySQL Client
Basic MySQL Usage
Summary
Chapter 24. Using MySQL with PHP
Performing Queries from PHP
A MySQLi Session Handler
What Is a Custom Session Handler?
Summary
Chapter 25. Using SQLite with PHP
What Makes SQLite Unique?
Basic SQLite Functionality
Working with PHP UDFs in SQLite
Odds and Ends
Summary
Chapter 26. PHP's dba Functions
Preparations and Settings
Creating a File-Based Database
Writing Data
Reading Data
Sample Application
Conclusion
Part VI. Graphical Output with PHP
Chapter 27. Working with Images
Basic Image Creation Using GD
Using the PHP/GD Drawing Functions
Working with Colors and Brushes
Using Fonts and Printing Strings
General Image Manipulation
Other Graphics Functions
Summary
Chapter 28. Printable Document Generation
A Note Regarding the Examples in This Chapter
Generating Dynamic RTF Documents
Generating Dynamic PDF Documents
Related Resources
Part VII. Appendixes
Appendix A. Installing PHP5 and MySQL
Installing PHP5
Installing MySQL and PHP Modules
Installing PEAR
Appendix B. HTTP Reference
What Is HTTP?
PHP Programming Libraries for HTTP Work
Understanding an HTTP Transaction
HTTP Client Methods
What Comes Back: Server Response Codes
HTTP Headers
Encoding
Identifying Clients and Servers
The "Referer"
Fetching Content from an HTTP Source
Media Types
Cookies: Preserving State and a Tasty Treat
Security and Authorization
Client-Side Caching of HTTP Content
Appendix C. Migrating Applications from PHP4 to PHP5
Configuration
Object-Oriented Programming (OOP)
New Behavior of Functions
Further Reading
Appendix D. Good Programming Techniques and Performance Issues
Common Style Mistakes
Common Security Concerns
Style and SecurityLogging
Summary
Appendix E. Resources and Mailing Lists
Relevant Websites
Mailing Lists and Newsgroups
Index
SYMBOL
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z

Form Processing

Anytime you are working with HTML forms, using some method or another you have to deal with processing that form. Often, not only does the form have to be processed (meaning you have to do something with the form data), but it almost always has to be validated in some fashion. In fact, it is strongly recommended that all form data is validated prior to being used in your scripts.

Basic Form Processing and Validation

In the simplest sense, form validation and processing is nothing more than working with the appropriate superglobal array ($_GET or $_POST) to do something in your PHP script. However, for a form of any complexity, often a considerable amount more goes into the validation of the data. As previously stated, it is simply bad practice (and dangerous) to use user-submitted data without properly validating it. For anything beyond the most elementary validation, usually all form validation is done via regular expressions, as shown in Listing 5.6:

Listing 5.6. Elementary Form Validation
<?php
     if(isset($_GET['submit'])) {
          if(preg_match("/^\(([2-9][0-9]{2})\)[2-9][0-9]{2}-[0-9]{4}$/i",
                                  $_GET['phone']) != 1) {
               echo "The phone field was invalid<BR>";
          }
     } else {
          /* Code to process form here */
     }
?>
<HTML>
<HEAD><TITLE>Elementary form validation</TITLE></HEAD>
<BODY>
<FORM ACTION="<?php echo $_SERVER['PHP_SELF']; ?>" METHOD=GET>
<INPUT TYPE="hidden" NAME="submit" VALUE="1">
Phone: <INPUT TYPE="text" NAME="phone" SIZE=13 MAXLENGTH=13>
(ex. (810)555-1212)<BR>
<INPUT TYPE="submit" VALUE="Send">
</FORM>
</HTML>

Because form validation is such an application-specific subject (every situation is different), there is little benefit to discussing more about the general validation and processing of forms. Instead, we'll try to kill multiple birds with one stone by creating a form-processing and validation architecture that is general enough to use on any form without sacrificing flexibility. Be forewarned that to create a processing script that is both flexible and easy to use requires some fancy PHP programming! Don't worry too much, however; I will be explaining the script extensively.

General-Purpose Form Validation

Before I actually discuss a line of code, let me first explain the concept behind this all-purpose form-validation script. When I set out to create this script, I had the following goals in mind:

  • Be flexible enough to validate and process any form data.

  • Encourage the separation of validation-related code from presentation-related code.

  • Most of all, be easy enough to use when implementing any type of HTML form.

To accomplish all three of these goals, it took a little planning (all solid scripts do); however, in the end I think you'll agree that all three goals are met!

The form processor works through a combination of hidden form elements and dynamic function calls (discussed in Chapter 1, "Basic PHP Development"). These hidden form elements will be used by the PHP script to both provide a human-friendly description of each field element (in case of an error) and identify those form fields that are "required." Specifically, for any given form element with a name of <name>, the description of that field is defined as being stored in a hidden element by the name of <name>_desc. The example that follows defines a text field named "phone" with an extra description field for use within our script:

<INPUT TYPE="text" NAME="myphone">
<INPUT TYPE="hidden" NAME="myphone_desc" VALUE="Phone Number">

The second hidden form element that the form-processing script uses is called required and should contain a comma-separated list of required elements. For instance, if you have three required elements whose NAME attributes are phone, email, and fax, the hidden required tag would be as follows:

<INPUT TYPE="hidden" NAME="required" VALUE="phone,email,fax">

Although not a strict requirement, the VALUE attribute of each visible element should be populated with its associated value in the appropriate superglobal (that is, $_GET['myvar']) if available. This is done so that if the form is submitted and not processed for whatever reason (for example, an error) the user will not have to retype everything.

The next issue to be tackled is how to deal with validation errors that may occur when the form is submitted. In the form validation script, this is handled through two global PHP variables: $form_errors and $form_errorlist. When the form validation script attempts to validate the data submitted to it, upon an error, it creates these two variables. The first variable $form_errors is a Boolean value indicating whether an error occurred during validate, and the second $form_errorlist is an array of error messages that occurred during the form validation. How these variables are used in your script to display validation errors to the user is subjective; however, one recommended method is as follows:

<?php if($form_errors): /* An error occurred processing the form */ ?>
<UL>
<?php foreach($form_errorlist as $val): ?>
<LI><?php echo $val; ?>
<?php endforeach; ?>
</UL>
<?php endif; ?>

By placing this immediately prior to the form, the result will be a nicely formatted bulleted list of every validation error that occurred.

A complete example of an HTML form that is used with our form validator is shown in Listing 5.7:

Listing 5.7. Form Example for the Form Validator Script
<?php if($form_errors): /* An error occurred processing the form */ ?>
<UL>
<?php foreach($form_errorlist as $val): ?>
<LI><?php echo $val;?>
<?php endforeach; ?>
</UL>
<?php endif; ?>

Please fill out the following form (* = Required)<BR>
<FORM ACTION="<?php echo $_SERVER['PHP_SELF']; ?>" METHOD=GET>
<INPUT TYPE="hidden" NAME="submit" VALUE="1">
<INPUT TYPE="hidden" NAME="required" VALUE="phone,email,fax">
<INPUT TYPE="hidden" NAME="phone_desc" VALUE="Phone Number">
<INPUT TYPE="hidden" NAME="email_desc" VALUE="Email Address">
<INPUT TYPE="hidden" NAME="fax_desc" VALUE="Fax Number">
Your Name: <INPUT TYPE="text" NAME="name"><BR>
* Your Phone Number:
<INPUT TYPE="text" NAME="phone" VALUE="<?php echo $_GET['phone']; ?>"><BR>
* Your Email Address:
<INPUT TYPE="text" NAME="email" VALUE="<?php echo $_GET['email']; ?>"><BR>
* Your Fax Number:
<INPUT TYPE="text" NAME="fax" VALUE="<?php echo $_GET['fax']; ?>"><BR>
<INPUT TYPE="submit" VALUE="Send">
</FORM>

Now that you have an idea of what a form to be used with the form validation script looks like, it's time to move on to the actual PHP script that will process the form. The form validation script is broken up into three separate functions: add_error(), _process_form(), and validate_form(). Of these three functions, validate_form() does the bulk of the work in validating the form data, and add_error() and _process_form() serve as support functions.

As I've already mentioned, validation errors that occur in the form validation script are recorded through the $form_errors and $form_errorlist variables. The add_error() function, as its name implies, is used to manipulate these two variables. Because this function is quite simple, displaying the function should be sufficient for an explanation (see Listing 5.8):

Listing 5.8. The add_error()_Function
<?php
    $form_errors = array();
    $form_errorlist = false;

    function add_error($error) {
        global $form_errorlist, $form_errors;
        $form_errorlist = true;
        $form_errors[] = $error;
    }
?>

The meat of the form validation script is in the validate_form() function. This function takes a single parameter (a reference to the superglobal array to validate). When executed, this function attempts to perform a number of tasks in the interest of validating the data. During the course of this function, if any validation errors occur, validate_form() calls the add_error() function with an appropriate error message (hence populating the error variables). When executed, the validate_form() starts by first processing the required hidden field and checks to make sure that all required fields are not empty. Following this check, the validate_form() attempts to process each individual form element according to the following rules:

  • If the element is named submit, required, or ends in _desc, it is ignored.

  • For all other elements, validate_form() attempts to call the function <name>_validate() (where <name> is the name of the current element).

Unless defined by the user, the <name>_validate() functions do not exist. These functions are your responsibility to create to validate each individual form element (or at least the elements you are concerned with validating). These functions should accept two parameters (the value submitted and a description of the field taken from the <name>_desc element) and should return true if the submitted value is valid or return an error message upon failure. For example, if you were validating a form element whose NAME attribute is phone (a phone number), the following function should be defined to validate that data (see Listing 5.9):

Listing 5.9. A Sample Form Element Validation Function
<?php
    function phone_validate($data, $desc) {
        $regex = "/^\([2-9][0-9]{2}\)[2-9][0-9]{2}-[0-9]{4}/i";
        if(preg_match($regex, $data) != 1) {
            return "The '$desc' field isn't valid!";
        }
        return true;
    }
?>

Assuming that each validate_form() executes and does not encounter any errors, it then calls the _process_form() function. This function is designed to clean up any nonrequired form elements (the _desc, submit, and required hidden elements) and call the function process_form(). As with the validation functions just discussed, the process_form() function must be defined by you and is designed to allow you to actually perform whatever action was desired after a successful validation. It accepts a single parameter (an array of the submitted data) and has no return value. If this function does not exist, nothing will be done with the submitted data upon a successful validation. A sample process_form() function is provided in Listing 5.10, which emails the contents of the form:

Listing 5.10. A sample process_form() Function
<?php
    function process_form($data) {

        $msg = "The form at {$_SERVER['PHP_SELF']}
                was submitted with these values: \n\n";
        foreach($data as $key=>$val) {
            $msg .= "$key => $val\n";
        }
        mail("joeuser@somewhere.com", "form submission", $msg);

    }
?>

Because the validate_form() function itself is best explained in conjunction with the other required functions, I will not attempt to explain the function further in text. Rather, see Listing 5.11, which contains a fully commented validate_form() function as a part of the complete form-validation script:

Listing 5.11. The Complete Form Validation Script
<?php

    /********** BEGIN FORM VALIDATION SCRIPT ***********/
    $form_errors = array();
    $form_errorlist = false;

    function add_error($error) {
        global $form_errorlist, $form_errors;
        $form_errorlist = true;
        $form_errors[] = $error;
    }

    function _process_form($method) {

        /** This function is called by the validate_form() function only! */

        /* Check to see if the process_form() function exists. If this
           function doesn't exist, there is no need to bother with cleaning
           up the form data. */
        if(function_exists("process_form")) {

            /* Make a copy of the submission data and iterate through it
               removing any elements that aren't part of the actual
               submission from the copy. */
            $data = $method;
            foreach($data as $key=>$val) {

                if(preg_match("/(submit|required)|(_desc$)/i", $key) == 1)
                    unset($data[$key]);
            }

            /* Call the process_form() function and pass it the cleaned
               up version of the form submission */
            process_form($data);
        }
    }

    function validate_form($method) {

        /* This variable is used to determine if any validation
           errors occurred during the course of the function.
           By default, we assume the form is valid */
        $process = true;

        /* Check for the existence of the 'required' form element.
           If this element does not exist the form is automatically
           invalid. */
        if(!isset($method['required'])) {

           add_error("Required hidden element 'required' missing!");
           $process = false;
        } else {

            /* Parse out the required field elements from the
               'required' form element and store them in an array*/
            $required = explode(',',$method['required']);

            /* Check to ensure each required element exists, and
               at least has some sort of data (not empty) */
            foreach($required as $val) {
                if(empty($method[$val])) {

                    /* This particular element should have some data,
                       but for some reason is empty. Hence, attempt to
                       get the human-friendly description of the element
                       and display an error to the user. If no human-friendly
                       description was provided use the element name instead */
                    if(isset($method[$val."_desc"])) {
                        $errormsg = "The required field '" . $method[$val."_desc"] .
                                    "' was empty!";
                    } else {
                        $errormsg = "The required field '$val' was empty!";
                    }
                    add_error($errormsg);
                    $process = false;
                }
            }

            /* Begin the iteration through all of the form elements */
            foreach($method as $key=>$val) {

                /* Because we are only concerned with validating the actual
                   form elements the user is editing, only check elements
                   that are not named 'submit', 'required' or end in '_desc' */
                if(preg_match("/(submit|required)|(_desc$)/i", $key) != 1) {

                    /* Construct the function name that will be called to
                       validate the data */
                    $func = $key."_validate";

                    /* Check to see if the validation function exists for this
                       form element. */
                    if(function_exists($func)) {

                        /* Since the validation function exists for this
                           element, call it passing it the value of the element
                           and the human-friendly description (if available) */
                        if(!isset($method[$key."_desc"])) {
                            $result = $func($val, $key);
                        } else {
                            $result = $func($val, $method[$key."_desc"]);
                        }

                        /* If the validation function does not return true,
                          then the form element is not valid and $return should
                          contain an error message. Add the error message to
                          the list of errors which occurred. */

                        if($result !== true) {
                            add_error($result);
                            $process = false;
                        }
                    }
                }
            }
        }

        /* Assuming no validation errors occurred, $process
           should still be true. If it is, call the _process_form()
           function and pass it the validated data and end the
           function by returning true. */
        if($process) {
            _process_form($method);
            return true;
        }

        /* Something went wrong in the validation, return false */
        return false;
    }

    /********** END FORM VALIDATION SCRIPT ***********/
    /********** BEGIN USER-DEFINED SCRIPT ***********/

    /* This is just a nicety. By only using $method
       any time we want to access the superglobal data
       we can quickly change the submission method from
       GET to POST (or the other way around) without
       changing multiple values. */

    $method = &$_GET;

    /* Check to see if the form was submitted, if so
       begin the validation process */
    if(isset($method['submit'])) {
        validate_form($method);
    }

    /* This function is called by validate_form() to
       validate the form element whose name is 'email'. */

    function email_validate($data, $desc) {
        $regex = "/^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,3}$/i";
        if(preg_match($regex, $data) != 1)
            return "The '$desc' field is invalid.";

        return true;
    }

    /* This function is called by validate_form() upon successful
       validation of the form. */
    function process_form($data) {

      $msg = "The form at {$_SERVER['PHP_SELF']} " .
             "was submitted with these values: \n\n";
      foreach($data as $key=>$val) {
          $msg .= "$key => $val\n";
      }
      mail("joeuser@somewhere.com", "form submission", $msg);

    }
    /********** END USER-DEFINED SCRIPT ***********/

?>
<HTML>
<HEAD><TITLE>Form Validation Example</TITLE></HEAD>
<BODY>
<?php
    /* Display any errors that occurred during validation */

    if($form_errorlist): ?>
    Please correct the following errors:<BR>
    <UL>
    <?php foreach($form_errors as $val): ?>
    <LI><?=$val?>
    <?php endforeach; ?>
    </UL>
<?php endif; ?>
<FORM ACTION="<?php echo $_SERVER['PHP_SELF']; ?>" METHOD=GET>
<INPUT TYPE="hidden" NAME="required" VALUE="first,last,email">
<INPUT TYPE="hidden" NAME="submit" VALUE="1">
<TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0>
<TR>
    <TD COLSPAN=2>Please fill out the following fields. (* = Required)</TD>
</TR>
<TR>
    <TD>*First Name:</TD>
    <TD><INPUT TYPE="text" NAME="first"
               VALUE="<?php echo @$method['first']; ?>">
        <INPUT TYPE="hidden" NAME="first_desc" VALUE="First name"></TD>
</TR>
<TR>
    <TD>*Last Name:</TD>
    <TD><INPUT TYPE="text" NAME="last" VALUE="<?php echo @$method['last']; ?>">
        <INPUT TYPE="hidden" NAME="last_desc" VALUE="Last name"></TD>
</TR>
<TR>
    <TD>Phone Number:</TD>
    <TD><INPUT TYPE="text" NAME="phone"
               VALUE="<?php echo @$method['phone']; ?>">
        <INPUT TYPE="hidden" NAME="phone_desc" VALUE="Phone number"></TD>
</TR>
<TR>
    <TD>*E-mail:</TD>
    <TD><INPUT TYPE="text" NAME="email"
               VALUE="<?php echo @$method['email']; ?>">
        <INPUT TYPE="hidden" NAME="email_desc" VALUE="E-mail address"></TD>
</TR>
<TR>
    <TD COLSPAN=2><INPUT TYPE="submit" VALUE="Send"></TD>
</TR>
</TABLE>
</FORM>
</BODY>
</HTML>

Separation of Presentation from Validation

You may have noticed in Listing 5.11 that I made it a point to use comments to define where the "user-defined" and form-validation sections began and ended. As I mentioned previously, one of the goals of the form-validation script is to separate presentation code from validation code. Looking at Listing 5.11, you may notice that this script can actually be divided into three separate files: one for the HTML form itself, one for the user-defined validation functionality, and a third for the form-validation script.

For argument's sake, let's assume everything between the form-validation script comment markers is in the file formvalidate.php and the HTML form is in the file htmlform.php. By placing the remainder of the code in a third file with a few includes, you would have something resembling Listing 5.12 (comments removed for the sake of space):

Listing 5.12. Separating the HTML and Validation Code
<?php

    include_once('formvalidate.php');

    $method = &$_GET;

    if(isset($method['submit'])) {
        validate_form($method);
    }

    function email_validate($data, $desc) {
        $regex = "/^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,3}$/i";
        if(preg_match($regex, $data) != 1)
            return "The '$desc' field is invalid.";

        return true;
    }

    function process_form($data) {

      $msg = "The form at {$_SERVER['PHP_SELF']}" .
             " was submitted with these values: \n\n";
      foreach($data as $key=>$val) {
          $msg .= "$key => $val\n";
      }
      mail("joeuser@somewhere.com", "form submission", $msg);

    }

    include("htmlform.php");

?>

Clearly, separating the script into multiple files has made it much more manageable. Furthermore it can be applied to any HTML form simply by including formvalidate.php at the beginning of the script, defining the necessary validation and process functions, and including the HTML form at the end of the script! This type of form validation is ideal, and you are encouraged to use the form validation script I have developed for this chapter (or your own similar facility) in your own scripts.