17.2 Customer Validation

Example 17-1 lists the customer/validate.php script. It is based on Example 10-4 and has the same structure with four exceptions:

  • It validates a larger set of fields.

  • It manages the creation of digests from passwords and user account allocation.

  • It uses the includes/validate.inc include file, which contains general-purpose validation functions based on those in Chapter 9. The file is discussed in Chapter 16.

  • If the validation succeeds and the member isn't logged in, the user is authenticated and logged in using functions in includes/authenticate.inc, which is discussed in Chapter 20.

Example 17-1. The winestore customer validation script, customer/validate.php

require_once "DB.php";

require_once "../includes/winestore.inc";

require_once "../includes/validate.inc";

require_once "../includes/authenticate.inc";


session_start( );

$connection = DB::connect($dsn, true);

if (DB::isError($connection))

   trigger_error($connection->getMessage( ), E_USER_ERROR);

// Clear and register an error array - just in case!

$_SESSION["custErrors"] = array( );

// Set up a custFormVars array for the POST variables

$_SESSION["custFormVars"] = array( );

// Clean and load the POST variables

foreach($_POST as $varname => $value)

   $_SESSION["custFormVars"]["{$varname}"] =

     pearclean($_POST, $varname, 50, $connection);

// Validate the firstname

checkMandatory("firstname", "first name", "custErrors", "custFormVars");

// Validate the Surname

checkMandatory("surname", "surname", "custErrors", "custFormVars");

// Validate the Address

checkMandatory("address", "address", "custErrors", "custFormVars");

// Validate the Initial

if (!empty($_SESSION["custFormVars"]["initial"]) &&

    !eregi("^[[:alpha:]]{1}$", $_SESSION["custFormVars"]["initial"]))

   $_SESSION["custErrors"]["initial"] = 

     "The initial field must be empty or one " .

     "alphabetic character in length.";

// Validate the City

checkMandatory("city", "city", "custErrors", "custFormVars");

// Validate Zipcode

if (checkMandatory("zipcode", "Zip code", "custErrors", "custFormVars"))

   checkZipcode("zipcode", "Zip code", "custErrors", "custFormVars");

// Phone is optional, but if it is entered it must have correct format

if (!empty($_SESSION["custFormVars"]["phone"]))

   checkPhone("phone", "telephone", "custErrors", "custFormVars");

// Validate Date of Birth

if (checkMandatory("birth_date", "date of birth", "custErrors",


   checkDateAndAdult("birth_date", "date of birth", "custErrors",


// Only validate email if this is an INSERT

if (!isset($_SESSION["loginUsername"]))


   if (checkMandatory("loginUsername", "email/username",

                 "custErrors", "custFormVars") &&

       emailCheck("loginUsername", "email/username",

                  "custErrors", "custFormVars"))


      // Check if the email address is already in use in

      //  the winestore

      $query = "SELECT * FROM users WHERE user_name =


      $result = $connection->query($query);

      if (DB::isError($result))

         trigger_error($result->getMessage( ), E_USER_ERROR);

      if ($result->numRows( ) == 1)

         $_SESSION["custErrors"]["loginUsername"] =

            "A customer already exists with this " .

            "email address.";


   // Validate password - between 6 and 8 characters

   if (checkMandatory("loginPassword", "password",

                 "custErrors", "custFormVars"))

      checkMinAndMaxLength("loginPassword", 6, 8, "password",

                      "custErrors", "custFormVars");


// Now the script has finished the validation,

// check if there were any errors

if (count($_SESSION["custErrors"]) > 0)


    // There are errors.  Relocate back to the client form

    header("Location: " . S_DETAILS);



// Is this an update?

if (isset($_SESSION["loginUsername"]))


   // Check the user is properly logged in


   $cust_id = getCust_id($_SESSION["loginUsername"], $connection);

   $query = "UPDATE customer SET

             title_id =     {$_SESSION["custFormVars"]["title_id"]},

             surname =     '{$_SESSION["custFormVars"]["surname"]}',

             firstname =   '{$_SESSION["custFormVars"]["firstname"]}',

             initial =     '{$_SESSION["custFormVars"]["initial"]}',

             address =     '{$_SESSION["custFormVars"]["address"]}',

             city =        '{$_SESSION["custFormVars"]["city"]}',

             state =       '{$_SESSION["custFormVars"]["state"]}',

             zipcode =     '{$_SESSION["custFormVars"]["zipcode"]}',

             country_id =   {$_SESSION["custFormVars"]["country_id"]},

             phone =       '{$_SESSION["custFormVars"]["phone"]}',

             birth_date =  '{$_SESSION["custFormVars"]["birth_date"]}'

             WHERE cust_id = {$cust_id}";

   $result = $connection->query($query);

   if (DB::isError($result))

      trigger_error($result->getMessage( ), E_USER_ERROR);




   // Lock to get the next available customer ID

   $result = $connection->query("LOCK TABLES customer WRITE");

   if (DB::isError($result))

      trigger_error($result->getMessage( ), E_USER_ERROR);

   // Find the max cust_id

   $result = $connection->query("SELECT max(cust_id) FROM customer");

   if (DB::isError($result))

      trigger_error($result->getMessage( ), E_USER_ERROR);

   $row = $result->fetchRow(DB_FETCHMODE_ASSOC);

   // Work out the next available ID

   $cust_id = $row["max(cust_id)"] + 1;

   // Insert the new customer

   $query = "INSERT INTO customer VALUES ({$cust_id},












   $result = $connection->query($query);

   if (DB::isError($result))

      trigger_error($result->getMessage( ), E_USER_ERROR);

   // Unlock the customer table

   $result = $connection->query("UNLOCK TABLES");

   if (DB::isError($result))

      trigger_error($result->getMessage( ), E_USER_ERROR);

   // As this was an INSERT, we need to INSERT into the users table too



           $cust_id, $connection);

   // Log the user into their new account



// Clear the custFormVars so a future form is blank



// Now show the customer receipt

header("Location: " . S_CUSTRECEIPT);


The presence of the session variable $_SESSION["loginUsername"] indicates whether or not the user is logged in. The script adds a new customer to the customer table and a row to the users table if the user isn't logged in, because the user must be applying for membership (otherwise, she's updating details of her membership). We don't use MySQL's auto_increment feature to obtain the next available cust_id, but instead use locking to figure out the maximum value and reserve the next value for this customer. We've avoided auto_increment because it's proprietary and you'd have to change it if you migrated the winestore to a different database server. We could have used an auxiliary table to manage the key fields (as discussed in Chapter 8), but we decided to keep things simple.

If the user is logged in, the member must be updating her details. In this case, before updating the row, the external function calls getCust_id( ) to get the customer cust_id associated with the $_SESSION["loginUsername"] session variable. The function is defined in the winestore.inc file discussed in Chapter 16.

If validation fails in Example 17-1, the script redirects the browser to the customer/details.php script shown in Example 17-2. Any validation error messages are recorded in the array $_SESSION["custErrors"] and this array is used to display the messages interleaved with the customer form widgets. The values that are passed from the form in the $_POST superglobal are themselves stored in the $_SESSION["custFormVars"] array so that they can be returned to the form for redisplay. This process of using session arrays to store intermediate data is discussed in Chapter 10.

If validation and the database write succeed, the session variables are emptied and the script redirects to the customer/receipt.php script shown in Example 17-3. Also, if it's a new user that's been created, they're logged in using the registerLogin( ) function that's discussed in Chapter 20.