• home
  • forum
  • my
  • kt
  • download
  • Customer Management

    Author: 2007-08-24 08:59:46 From:

    This tutorial is the first of four that outline the case study winestore application. It contains an overview of the complete application, as well as the customer management scripts for the winestore. We also introduce the include files that store common functionality used throughout the application.

    This chapter is the first of four that outline the case study winestore application. It contains an overview of the complete application, as well as the customer management scripts for the winestore. We also introduce the include files that store common functionality used throughout the application.

    The material presented here doesn't fully explain the scripts from the online winestore. The descriptions are outlines, and careful reading of the scripts is required to fully understand the functionality. Also, we avoid duplicating our discussions of the principles and basic techniques for building web database applications. Chapter 2 through Chapter 9 are required background reading to fully understand the implementations outlined here.

    The online winestore illustrates the practice of developing web database applications and isn't a production e-commerce application. It is a complete application but doesn't have all the features of a full production system. Such a system would include features such as credit-card processing, a password change facility, prompts that confirm whether a user wishes to proceed with updates or orders, more comprehensive search features, and an administrative interface.

    The scripts outlined in this chapter perform the following functions:

    Becoming a member

    The complete customer <form> based on the simplified version presented in Chapter 6 and 8.

    Updating customer details

    This functionality is integrated into the script used for becoming a member, and is again an extension of the customer <form> from Chapter 6 and Chapter 8.

    Checking customer details

    The complete validation and database management processes for updating and creating new customers. These processes extend the customer <form> introduced in Chapter 6 by applying the validation techniques from Chapter 7. The process includes creating and storing passwords using the encryption techniques discussed in Chapter 9.

    Providing a customer receipt

    A receipt page that presents the results of the customer membership processing and avoids the reload problem discussed in Chapter 6.

    Logging in

    Authenticating a user and using sessions to track the user login status. This is an application of the techniques discussed in Chapter 8 and Chapter 9.

    Miscellaneous database functions and common functions

    An introduction to the include.inc file used throughout the winestore application, as well as the customer error handler implemented using the PHP error library.

    The winestore application was developed to meet the requirements outlined in Chapter 1. It has four separate modules that we discuss in this and the next three chapters:

    Customer management

    Becoming a member, amending membership details, logging in, and logging out. The scripts that implement this functionality are in this chapter. The web database application techniques illustrated include querying and writing data, sessions, post validation, batch error reporting, encryption of passwords, receipt pages to avoid the reload problem, and managing user authentication.

    Shopping cart

    Adding wines to a shopping cart, deleting items from the cart, adjusting quantities, and emptying the cart. The shopping cart is discussed in Chapter 11.

    Ordering and shipping

    Processing the cart so that it becomes an order, confirming shipping details by email, and confirming shipping details with an HTML receipt. These scripts are the subject of Chapter 12.

    Browsing and searching

    Searching and browsing the wines. The searching and browsing module is briefly outlined in Chapter 13, along with related topics.

    Figure 10-1 and Figure 10-2 show the scripts developed for the winestore application and how they interact. The three key user interface scripts, cart.1 , search.1, and cart.2, are shown in both figures. cart.5, the key script that manages browser redirection using the header( ) function, is omitted from the figures; we discuss why later in this section. Figure 10-1 shows the cart, customer, searching, ordering, and shipping scripts. Scripts are shown as boxes. Solid boxes indicate scripts that interact with the user, while dashed boxes don't produce output but instead redirect to the script shown.

    Figure 10-1. The winestore application architecture
    figs/wda_1001.gif

    The main or home page of the online winestore is shown in Figures 10-1 and 10-2 and is labeled cart.1. This page allows the user to add bottles and cases of the three selected "hot new wines" to his shopping cart; this functionality is shown by the double-ended arrow to the add to cart script labeled cart.3. The cart.3 script is shown as a dashed rectangle in Figure 10-1, indicating that it's a one-component query module that has no output and instead redirects to the calling page.

    The front page also allows the user to view his shopping cart by clicking on the cart icon or the View Cart button at the base of the page. View-the-cart functionality is provided by the cart.2 script introduced later in this section. Four other actions are also possible from the front page:

    • Searching the wines using the script search.1

    • Becoming a member or changing customer details using the customer.2 script

    • Emptying the cart using the cart.4 script

    • Logging in or logging out using the scripts order.1 and order.2, respectively

    The customer management process is provided by five scripts. The customer.1, customer.2, and customer.3 scripts provide the become-a-member and change details features. The script customer.2 presents an empty customer <form> to new customers. The <form> allows entry of all customer details, including an email address that is used as the login name of the user and a password for future visits to the site. The customer.1 script validates customer data and, on success, writes to the database and redirects to the customer receipt script customer.3. On validation failure, customer.1 redirects to customer.2, where the validation errors are reported interleaved with the <form> widgets.

    For customers who are amending their details, the password and email <input> widgets are omitted from the customer <form>. For compactness, we have omitted password-change functionality from the online site. Other possible extensions to the password module include emailing password reminders to the user and emailing account activation details.

    The remaining two customer scripts are shown in Figure 10-2. The login and logout scripts are shown, along with the three scripts from Figure 10-1 that interact with these scripts. The login script interacts with the user, while the logout script doesn't produce output but instead redirects to the home page.

    Figure 10-2. More winestore application architecture
    figs/wda_1002.gif

    The script order.1 allows a user to provide his username and password credentials. On successful processing of credentials, the user is logged in. A logged-in user can then log out using the order.2 script. The order.2 logout script is a one-component script that always redirects the browser to the front page after the logout action.

    The view cart script cart.2 shows the user the contents of his shopping cart. If the cart contains items, the quantities are presented in a <form> environment that allows the user to make changes to quantities. Changing a quantity to zero deletes the item. To update changes in quantities, the cart.6 script is requested by clicking the Update Quantities button; this script redirects to cart.2, and either shows the user the correctly updated quantities or reports an error describing why the update failed. The user can also empty his cart completely by clicking on a button that requests the cart.4 script.

    When logged in, orders are placed by clicking on the Make Purchase button in the view cart screen. When the button is clicked, the script order.3 is requested and the complex database processing used to finalize an order is performed; an overview of the purchasing process is presented in Chapter 3, and we outline the script in Chapter 12. If the ordering process fails, order.3 redirects to cart.2, where errors are reported. If the ordering process succeeds, order.3 redirects to shipping.1. The shipping.1 script sends the user an email confirmation of his order and redirects to shipping.2 which shows the user the same receipt as an HTML page. From shipping.2, the user can return to the home page.

    The view cart script cart.2 also allows the user to return to the home page, search, log in, log out, or use the customer module.

    The search.1 script allows the user to browse wines that are in stock. The user can also choose to browse a specific wine type-such as Red or White-and a specific region such as Margaret River. Bottles or cases of wine can be added to the shopping cart by clicking on a link that requests and passes parameters to the cart.3 script. As on the other main pages, the user can also click on buttons to view his cart, empty his cart, return to the home page, become a member, log in, log out, or update his membership details.

    At the beginning of this section we stated that one important script was omitted from Figure 10-1 and Figure 10-2. This script is cart.5. This script is requested when a button is pressed on all pages that have more than one button, and it's responsible for redirecting the browser to the correct script. Each button is an <input> element of type submit, and all are elements of one <form>. As there is only one <form>, only one script can be specified as the action attribute, and this script is cart.5. The script processes requests, identifies which button was clicked by inspecting the name attribute of the <input> element, and then redirects the browser to the appropriate script. Therefore, in practice, many arrows shown in Figure 10-1 and Figure 10-2 should actually pass via cart.5.

    An alternative approach to redirection via cart.5 is to include multiple <form> elements in a script or use embedded links instead of buttons. Both alternatives work well, but all approaches have drawbacks. The advantage of our approach is that buttons are intuitive for the user and the HTML is kept simple. The disadvantage is the extra HTTP response and request required for each redirection.

    The winestore application can be used at either the book's web site or on your local server, if you have followed the instructions to install the examples in Appendix A. The source code described can also be viewed at the book's web site and-if the installation instructions have been followed-can be edited and viewed in the directory /usr/local/apache/htdocs/wda/ on your local server. A summary of the winestore scripts, their filenames, and functions is shown in Table 10-1.

    Table 10-1. The winestore scripts, filenames, and functions

    ScriptFilenameFunction
    cart.1example.cart.1.phpHome page and Hot New Wines panel
    cart.2example.cart.2.phpCart contents view
    cart.3example.cart.3.phpAdd to cart
    cart.4example.cart.4.phpEmpty cart
    cart.5example.cart.5.phpManage redirection
    cart.6example.cart.6.phpUpdate cart quantities
    customer.1example.customer.1.phpValidate and update customer
    customer.2example.customer.2phpCustomer entry <form>
    customer.3example.customer.3.phpCustomer receipt
    order.1example.order.1.phpLog in
    order.2example.order.2.phpLog out
    order.3example.order.3.phpFinalize order
    shipping.1example.shipping.1.phpEmail order confirmation
    shipping.2example.shipping.2.phpOrder receipt
    search.1example.search.1.phpBrowse and search wines
    include.incinclude.incCommon functionality
    db.incdb.incDBMS parameters
    error.incerror.incCustom error handler

    In this section, we outline the customer management scripts customer.1, customer.2, and customer.3. The customer.2 script is for data entry of customer details and reporting errors and is based on the customer <form> case study from Chapter 6 and Chapter 8. The customer.1 script performs data validation and writes the customer details to the winestore database. It is also based on the case study examples from Chapter 6 and Chapter 8. The customer.3 receipt is designed to avoid the reload problem after writing to the database.

    Example 10-1 lists the customer.1 script. It is based on Example 8-4 and has the same structure with two exceptions:

    • It validates a superset of customer fields, that is, all the fields listed in Chapter 1.

    • It manages the encryption of passwords and user account allocation.

    The validation techniques used for the additional fields-such as the telephone and fax numbers, email address, zip code, and so on-are discussed in Chapter 7.

    Example 10-1. The complete winestore customer validation script, customer.2
    <?php
      // This script validates customer data entered into
      // example.customer.2.php.
      // If validation succeeds, it INSERTs or UPDATEs
      // a customer and redirect to a receipt page; if it
      // fails, it creates error messages and these are later
      // displayed by example.customer.2.php.
    
      include 'include.inc';
    
      set_error_handler("errorHandler");
    
      // Initialize a session
      session_start(  );
    
      // Register an error array - just in case!
      if (!session_is_registered("errors"))
         session_register("errors");
    
      // Clear any errors that might have been
      // found previously
      $errors = array(  );
    
      // Set up a $formVars array with the POST variables
      // and register with the session.
      if (!session_is_registered("formVars"))
         session_register("formVars");
    
      foreach($HTTP_POST_VARS as $varname => $value)
          $formVars[$varname] = trim(clean($value, 50));
    
      // Validate the firstName
      if (empty($formVars["firstName"]))
          // First name cannot be a null string
          $errors["firstName"] =
              "The first name field cannot be blank.";
    
      elseif (!eregi("^[a-z'-]*$", $formVars["firstName"]))
          // First name cannot contain white space
          $errors["firstName"] =
          "The first name can only contain alphabetic " .
             "characters or \"-\" or \"'\"";
    
      elseif (strlen($formVars["firstName"]) > 50)
          $errors["firstName"] =
          "The first name can be no longer than 50 " .
             "characters";
    
      // Validate the Surname
      if (empty($formVars["surname"]))
          // the user's surname cannot be a null string
          $errors["surname"] =
              "The surname field cannot be blank.";
    
      elseif (strlen($formVars["surname"]) > 50)
          $errors["surname"] =
              "The surname can be no longer than 50 " .
              "characters";
    
      // Validate the Address
      if (empty($formVars["address1"]) &&
          empty($formVars["address2"]) &&
          empty($formVars["address3"]))
          // all the fields of the address cannot be null
          $errors["address"] =
              "You must supply at least one address line.";
      else
      {
          if (strlen($formVars["address1"]) > 50)
            $errors["address1"] =
              "The address line 1 can be no longer " .
              "than 50 characters";
          if (strlen($formVars["address2"]) > 50)
            $errors["address2"] =
              "The address line 2 can be no longer " .
              "than 50 characters";
          if (strlen($formVars["address3"]) > 50)
            $errors["address3"] =
              "The address line 3 can be no longer " .
              "than 50 characters";
      }
    
      // Validate the user's Initial
      if (!empty($formVars["initial"]) &&
          !eregi("^[a-z]{1}$", $formVars["initial"]))
          // If there is a middle initial, it must be
          // one character in length
          $errors["initial"] =
             "The initial field must be empty or one " .
             "character in length.";
    
      // Validate the City
      if (empty($formVars["city"]))
          // the user's city cannot be a null string
          $errors["city"] = "You must supply a city.";
    
      elseif (strlen($formVars["city"]) > 20)
          $errors["city"] =
            "The city can be no longer than 20 characters";
    
      // Validate State - any string less than 21 characters
      if (strlen($formVars["state"]) > 20)
          $errors["state"] =
             "The state can be no longer than 20 characters";
    
      // Validate Zipcode
      if (!ereg("^([0-9]{4,5})$", $formVars["zipcode"]))
          $errors["zipcode"] =
             "The zipcode must be 4 or 5 digits in length";
    
        // Validate Country
      if (strlen($formVars["country"]) > 20)
          $errors["country"] =
             "The country can be no longer than 20 characters";
    
      // Phone is optional, but if it is entered it must have
      //  correct format
      $validPhoneExpr =
         "^([0-9]{2,3}[ ]?)?[0-9]{4}[ ]?[0-9]{4}$";
    
      if (!empty($formVars["phone"]) &&
          !ereg($validPhoneExpr, $formVars["phone"]))
          $errors["phone"] =
            "The phone number must be 8 digits in length, " .
            "with an optional 2 or 3 digit area code";
    
      // Fax is optional, but if it is entered it must
      // have correct format
      if (!empty($formVars["fax"]) &&
          !ereg($validPhoneExpr, $formVars["fax"]))
          $errors["fax"] =
            "The fax number must be 8 digits in length, with " .
            "an optional 2 or 3 digit area code";
    
      // Validate Date of Birth
      if (empty($formVars["dob"]))
          // the user's date of birth cannot be a null string
          $errors["dob"] = "You must supply a date of birth.";
    
      elseif (!ereg("^([0-9]{2})/([0-9]{2})/([0-9]{4})$",
              $formVars["dob"], $parts))
          // Check the format
          $errors["dob"] =
            "The date of birth is not a valid date in the " .
            "format DD/MM/YYYY";
    
      elseif (!checkdate($parts[2],$parts[1],$parts[3]))
          $errors["dob"] =
             "The date of birth is invalid. Please check " .
             "that the month is between 1 and 12, and the " .
             "day is valid for that month.";
    
      elseif (intval($parts[3]) < 1890)
          // Make sure that the user has a reasonable birth year
          $errors["dob"] =
             "You must be alive to use this service!";
    
      elseif
        // Check whether the user is 18 years old.
        // If all the following are NOT true, then report
        // an error.
        // Were they born more than 19 years ago?
        (!((intval($parts[3]) < (intval(date("Y") - 19))) ||
    
        // No, so were they born exactly 18 years ago, and
        // has the month they were born in passed?
        (intval($parts[3]) == (intval(date("Y")) - 18) &&
        (intval($parts[2]) < intval(date("m")))) ||
    
        // No, so were they born exactly 18 years ago in this
        // month, and was the day today or earlier in the month?
        (intval($parts[3]) == (intval(date("Y")) - 18) &&
        (intval($parts[2]) ==  intval(date("m"))) &&
        (intval($parts[1]) <= intval(date("d"))))))
          $errors["dob"] =
              "You must be 18+ years of age to use this ".
              "service.";
    
      // Only validate email if this is an INSERT
      if (!session_is_registered("loginUsername"))
      {
         // Check syntax
         $validEmailExpr =
             "^[0-9a-z~`!#$%&_-]([.]?[0-9a-z~!#$%&_-])*" .
             "@[0-9a-z~!#$%&_-]([.]?[0-9a-z~!#$%&_-])*$";
    
         if (empty($formVars["email"]))
             // the user's email cannot be a null string
             $errors["email"] =
                "You must supply an email address.";
    
         elseif (!eregi($validEmailExpr, $formVars["email"]))
             // The email must match the above regular
             // expression
             $errors["email"] =
                "The email address must be in the " .
                "name@domain format.";
    
         elseif (strlen($formVars["email"]) > 50)
             // The length cannot exceed 50 characters
             $errors["email"] =
                "The email address can be no longer than " .
                "50 characters.";
    
         elseif (!(getmxrr(substr(strstr($formVars["email"],
                   '@'), 1), $temp)) ||
                  checkdnsrr(gethostbyname(
                             substr(strstr($formVars["email"],
                             '@'), 1)),"ANY"))
             // There must be a Domain Name Server (DNS)
             // record for the domain name
             $errors["email"] =
                "The domain does not exist.";
    
         else
         {
            // Check if the email address is already in use in
            //  the winestore
            if (!($connection = @ mysql_pconnect($hostName,
                                                 $username,
                                                 $password)))
               showerror(  );
    
            if (!mysql_select_db($databaseName, $connection))
               showerror(  );
    
            $query = "SELECT * FROM users
                      WHERE user_name = '" .
                      $formVars["email"] . "'";
    
            if (!($result = @ mysql_query ($query,
                                           $connection)))
               showerror(  );
    
            // Is it taken?
            if (mysql_num_rows($result) == 1)
               $errors["email"] =
                  "A customer already exists with this " .
                  "login name.";
         }
      }
    
      // Only validate password if this is an INSERT
      // Validate password - between 6 and 8 characters
      if (!session_is_registered("loginUsername") &&
         (strlen($formVars["loginPassword"]) < 6 ||
          strlen($formVars["loginPassword"] > 8)))
          $errors["loginPassword"] =
             "The password must be between 6 and 8 " .
             "characters in length";
    
      // Now the script has finished the validation,
      // check if there were any errors
      if (count($errors) > 0)
      {
          // There are errors.  Relocate back to the
          // customer <form>
          header("Location: example.customer.2.php");
          exit;
      }
    
      // If we made it here, then the data is valid
    
      if (!isset($connection))
      {
         if (!($connection = @ mysql_pconnect($hostName,
                                              $username,
                                              $password)))
            showerror(  );
    
         if (!mysql_select_db($databaseName, $connection))
            showerror(  );
      }
    
      // Reassemble the date of birth into database format
      $dob = "\"$parts[3]-$parts[2]-$parts[1]\"";
    
      // Is this an update?
      if (session_is_registered("loginUsername"))
      {
         $custID = getCustomerID($loginUsername, $connection);
    
         $query = "UPDATE customer SET ".
         "title = \"" . $formVars["title"] . "\", " .
         "surname = \"" . $formVars["surname"] . "\", " .
         "firstname = \"" . $formVars["firstName"] . "\", " .
         "initial = \"" . $formVars["initial"] . "\", " .
         "addressline1 = \"" . $formVars["address1"] . "\", " .
         "addressline2 = \"" . $formVars["address2"] . "\", " .
         "addressline3 = \"" . $formVars["address3"] . "\", " .
         "city = \"" . $formVars["city"] . "\", " .
         "state = \"" . $formVars["state"] . "\", " .
         "zipcode = \"" . $formVars["zipcode"] . "\", " .
         "country = \"" . $formVars["country"]. "\", " .
         "phone = \"" . $formVars["phone"] . "\", " .
         "fax = \"" . $formVars["fax"] . "\", " .
         "birth_date = " . $dob .
         " WHERE cust_id = $custID";
      }
      else
         $query = "INSERT INTO customer VALUES (NULL, " .
                  "\"" . $formVars["surname"] . "\", " .
                  "\"" . $formVars["firstName"] . "\", " .
                  "\"" . $formVars["initial"] . "\", " .
                  "\"" . $formVars["title"] . "\", " .
                  "\"" . $formVars["address1"] . "\", " .
                  "\"" . $formVars["address2"] . "\", " .
                  "\"" . $formVars["address3"] . "\", " .
                  "\"" . $formVars["city"] . "\", " .
                  "\"" . $formVars["state"] . "\", " .
                  "\"" . $formVars["zipcode"] . "\", " .
                  "\"" . $formVars["country"] . "\", " .
                  "\"" . $formVars["phone"] . "\", " .
                  "\"" . $formVars["fax"] . "\", " .
                  "\"" . $formVars["email"] . "\", " .
                  $dob . ", " .
                  0 . ")";
    
      // Run the query on the customer table
      if (!(@ mysql_query ($query, $connection)))
         showerror(  );
    
      // If this was an INSERT, we need to INSERT
      // also into the users table
      if (!session_is_registered("loginUsername"))
      {
         // Get the customer id that was created
         $custID = @ mysql_insert_id($connection);
    
         // Use the first two characters of the
         // email as a salt for the password
         $salt = substr($formVars["email"], 0, 2);
    
         // Create the encrypted password
         $stored_password =
             crypt($formVars["loginPassword"], $salt);
    
         // Insert a new user into the user table
         $query = "INSERT INTO users
                   SET cust_id = $custID,
                      password = '$stored_password',
                      user_name = '" . $formVars["email"] . "'";
    
         if (!($result = @ mysql_query ($query, $connection)))
            showerror(  );
    
         // Log the user into their new account
         session_register("loginUsername");
    
         $loginUsername = $formVars["email"];
      }
    
      // Clear the formVars so a future <form> is blank
      session_unregister("formVars");
      session_unregister("errors");
    
      // Now show the customer receipt
      header("Location: example.customer.3.php?custID=$custID");
    ?>

    The following fragment of Example 10-1 manages the creation of a new user account but only if this is a new customer:

      if (!session_is_registered("loginUsername"))
      {
         // Get the customer id that was created
         $custID = @ mysql_insert_id($connection);
    
         // Use the first two characters of the
         // email as a salt for the password
         $salt = substr($formVars["email"], 0, 2);
    
         // Create the encrypted password
         $stored_password =
             crypt($formVars["loginPassword"], $salt);
    
         // Insert a new user into the user table
         $query = "INSERT INTO users
                   SET cust_id = $custID,
                      password = '$stored_password',
                      user_name = '" . $formVars["email"] . "'";
    
         if (!($result = @ mysql_query ($query, $connection)))
            showerror(  );
    
         // Log the user into their new account
         session_register("loginUsername");
    
         $loginUsername = $formVars["email"];
      }

    The session variable loginUsername indicates whether or not the user is logged in. Therefore, the fragment adds a new row to the users table only if the user isn't logged in. To store the password, the techniques from Chapter 9 are applied, and the password is encrypted using crypt( ) with the first two characters of the email address as the seed. After adding the row, the user is logged in by registering the session variable loginUsername and assigning the email address value to it.

    For updates of customer details, the external function getCustomerID( ) is called prior to updating the row. The function returns the customer cust_id associated with the loginUsername session variable passed as a parameter. The function is defined in the include.inc file.

    If validation fails in Example 10-1, the script redirects to the customer.2 script shown in Example 10-2. Any validation error messages are recorded in the array errors and this array is used to display the messages interleaved with the customer <form> widgets. If validation and the database write succeed, the script redirects to the customer.3 script shown in Example 10-3.

    The script customer.2 is shown in Example 10-2. The script displays a <form> for customer data entry. If the user is logged in and validation has not previously failed, the customer data is retrieved from the customer table and used to populate the <form> widgets. If the user isn't logged in, and validation has not previously failed, a blank <form> is shown to collect new member details. If data has failed validation, the formVars array that is registered as a session variable is used to repopulate the <form>, and the error messages from the errors array are displayed.

    Two external functions from include.inc are used in Example 10-2:

    void showMessage( )

    This function outputs any errors or notices created by other scripts. These messages include login errors, cart update problems, ordering problems, etc.

    void showLogin( )

    This function outputs in the top-right corner of the browser whether the user is logged in or not. If the user is logged in, it outputs his email address.

    The country widget has only three possible values: Australia, United States, and Zimbabwe, In a full implementation of our case study, a database table of country names would be maintained, and the function selectDistinct( ) would present the <select> list. The function selectDistinct( ) is discussed in Chapter 5.

    Example 10-2. The customer <form> script customer.1
    <?php
      // This script shows the user a customer <form>.
      // It can be used both for INSERTing a new customer and
      // for UPDATE-ing an existing customer. If the customer
      // is logged in, then it is an UPDATE; otherwise, an
      // INSERT.
      // The script also shows error messages above widgets
      // that contain erroneous data; errors are generated
      // by example.customer.1.php
    
      include 'include.inc';
    
      set_error_handler("errorHandler");
    
      // Show an error in a red font
      function fieldError($fieldName, $errors)
      {
        if (isset($errors[$fieldName]))
          echo "<font color=\"red\">" .
               $errors[$fieldName] .
               "</font><br>";
      }
    
      // Connect to a session
      session_start(  );
    
      // Is the user logged in and were there no errors from
      // a previous validation?
      // If so, look up the customer for editing
      if (session_is_registered("loginUsername") &&
          empty($errors))
      {
         if (!($connection = @ mysql_pconnect($hostName,
                                              $username,
                                              $password)))
            showerror(  );
    
         if (!mysql_select_db($databaseName, $connection))
            showerror(  );
    
         $custID = getCustomerID($loginUsername, $connection);
    
         $query = "SELECT * FROM customer
                   WHERE cust_id = " . $custID;
    
         if (!($result = @ mysql_query($query, $connection)))
            showerror(  );
    
         $row = mysql_fetch_array($result);
    
         // Reset $formVars, since we're loading from
         // the customer table
         $formVars = array(  );
    
         // Reset the errors
         $errors = array(  );
    
         // Load all the form variables with customer data
         $formVars["title"] = $row["title"];
         $formVars["surname"] = $row["surname"];
         $formVars["firstName"] = $row["firstname"];
         $formVars["initial"] = $row["initial"];
         $formVars["address1"] = $row["addressline1"];
         $formVars["address2"] = $row["addressline2"];
         $formVars["address3"] = $row["addressline3"];
         $formVars["city"] = $row["city"];
         $formVars["state"] = $row["state"];
         $formVars["zipcode"] = $row["zipcode"];
         $formVars["country"] = $row["country"];
         $formVars["phone"] = $row["phone"];
         $formVars["fax"] = $row["fax"];
         $formVars["email"] = $row["email"];
         $formVars["dob"] = $row["birth_date"];
         $formVars["dob"] = substr($formVars["dob"], 8, 2) .
                            "/" .
                            substr($formVars["dob"], 5, 2) .
                            "/" .
                            substr($formVars["dob"], 0, 4);
      }
    
    ?>
    <!DOCTYPE HTML PUBLIC
       "-//W3C//DTD HTML 4.0 Transitional//EN"
       "http://www.w3.org/TR/html4/loose.dtd" >
    <html>
    <head><title>Customer Details</title></head>
    <body bgcolor="white">
    <?php
      // Show the user login status
      showLogin(  );
    ?>
    <form method="post" action="example.customer.1.php">
    <h1>Customer Details</h1>
    <?php
      // Display any messages to the user
      showMessage(  );
    
      // Show meaningful instructions for UPDATE or INSERT
      if (session_is_registered("loginUsername"))
         echo "<h3>Please amend your details below as " .
              "required. Fields shown in " .
              "<font color=\"red\">red</font> are " .
              "mandatory.</h3>";
      else
         echo "<h3>Please fill in the details below to " .
              "join. Fields shown in " .
              "<font color=\"red\">red</font> are ".
              "mandatory.</h3>";
    ?>
    <table>
    <col span="1" align="right">
    
        <tr><td><font color="red">Title:</font></td>
       <td><select name="title">
           <option <?php if ($formVars["title"]=="Mr")
                     echo "selected";?>>Mr
           <option <?php if ($formVars["title"]=="Mrs")
                     echo "selected";?>>Mrs
           <option <?php if ($formVars["title"]=="Ms")
                     echo "selected";?>>Ms
           <option <?php if ($formVars["title"]=="Dr")
                     echo "selected";?>>Dr
           </select><br></td>
        </tr>
    
        <tr><td><font color="red">First name:</font></td>
       <td><? echo fieldError("firstName", $errors); ?>
           <input type="text" name="firstName"
          value="<? echo $formVars["firstName"]; ?>"
          size=50></td>
        </tr>
    
        <tr><td><font color="red">Surname:</font></td>
       <td><? echo fieldError("surname", $errors); ?>
           <input type="text" name="surname"
          value="<? echo $formVars["surname"]; ?>"
          size=50></td>
        </tr>
    
        <tr><td>Initial: </td>
       <td><? echo fieldError("initial", $errors); ?>
           <input type="text" name="initial"
          value="<? echo $formVars["initial"]; ?>"
          size=1></td>
        </tr>
    
        <tr><td><font color="red">Address:</font></td>
       <td><? echo fieldError("address", $errors); ?>
           <? echo fieldError("address1", $errors); ?>
           <input type="text" name="address1"
          value="<? echo $formVars["address1"]; ?>"
          size=50></td>
        </tr>
    
        <tr><td></td>
       <td><? echo fieldError("address2", $errors); ?>
           <input type="text" name="address2"
          value="<? echo $formVars["address2"]; ?>"
          size=50></td>
        </tr>
    
        <tr><td></td>
       <td><? echo fieldError("address3", $errors); ?>
           <input type="text" name="address3"
          value="<? echo $formVars["address3"]; ?>"
          size=50></td>
        </tr>
    
        <tr><td><font color="red">City:</font></td>
       <td><? echo fieldError("city", $errors); ?>
           <input type="text" name="city"
          value="<? echo $formVars["city"]; ?>"
          size=20></td>
        </tr>
    
        <tr><td>State: </td>
        <td><? echo fieldError("state", $errors); ?>
       <input type="text" name="state"
           value="<? echo $formVars["state"]; ?>"
           size=20></td>
        </tr>
    
        <tr><td><font color="red">Zipcode:</font></td>
        <td><? echo fieldError("zipcode", $errors); ?>
       <input type="text" name="zipcode"
           value="<? echo $formVars["zipcode"]; ?>"
           size=5></td>
        </tr>
    
        <tr><td>Country: </td>
       <td><? echo fieldError("country", $errors); ?>
          <select name="country">
          <option <?php
              if ($formVars["country"]=="Australia")
                 echo "selected";?>>Australia
          <option <?php
              if ($formVars["country"]=="United States")
                 echo "selected";?>>United States
          <option <?php
             if ($formVars["country"]=="Zimbabwe")
                echo "selected";?>>Zimbabwe
           </select></td>
        </tr>
    
        <tr><td>Telephone: </td>
       <td><? echo fieldError("phone", $errors); ?>
       <input type="text" name="phone"
           value="<? echo $formVars["phone"]; ?>"
           size=15></td>
        </tr>
    
        <tr><td>Fax: </td>
       <td><? echo fieldError("fax", $errors); ?>
           <input type="text" name="fax"
          value="<? echo $formVars["fax"]; ?>"
          size=15></td>
        </tr>
    
        <tr><td><font color="red">Date of birth
                                 (dd/mm/yyyy):</font> </td>
       <td><? echo fieldError("dob", $errors); ?>
           <input type="text" name="dob"
          value="<? echo $formVars["dob"]; ?>"
          size=10></td>
        </tr>
    
    <?php
      // Only show the username/email and password
      // <input> widgets to new users
      if (!session_is_registered("loginUsername"))
      {
    ?>    <tr><td><font color="red">Email/username:</font></td>
       <td><? echo fieldError("email", $errors); ?>
           <input type="text" name="email"
          value="<? echo $formVars["email"]; ?>"
          size=50></td>
        </tr>
    
        <tr><td><font color="red">Password:</font></td>
       <td><? echo fieldError("loginPassword", $errors); ?>
           <input type="password" name="loginPassword"
          value="<? echo $formVars["loginPassword"]; ?>"
          size=8></td>
        </tr>
    <?php
      }
    ?>
    <tr>
       <td><input type="submit" value="Submit"></td>
    </tr>
    </table>
    </form>
    <br><a href="http://validator.w3.org/check/referer">
        <img src="http://www.w3.org/Icons/valid-html401"
         height="31" width="88" align="right" border="0"
         alt="Valid HTML 4.01!"></a>
    </body>
    </html>
    

    Example 10-3 shows the customer receipt script, customer.3, that is called after a database write to insert or update a customer. The script is a receipt page that can be bookmarked-it expects a cust_id as a GET method parameter-and the script does nothing but read details from the database. Reloading of the page therefore has no undesirable side effects. Customer receipts can be viewed only when logged in, and a user is permitted to view only her own customer receipts; if the user attempts to retrieve another user's details, a warning message is shown to the user, and the cust_id is updated to be her own.

    Example 10-3. The customer.3 customer receipt page
    <?php
      // This script shows the user a receipt for their customer
      // UPDATE or INSERT.
      // It carries out no database actions and can be
      // bookmarked. The user must be logged in to view it.
    
      include 'include.inc';
    
      set_error_handler("errorHandler");
    
      // Show the user a customer INSERT or UPDATE receipt
      function show_HTML_receipt($custID, $connection)
      {
        $query = "SELECT * FROM customer
                 WHERE cust_id = $custID";
    
        if (!($result = @ mysql_query ($query, $connection)))
           showerror(  );
    
        // There is only one matching row
        $row = @ mysql_fetch_array($result);
    
        echo "\n<h1>Account details for " .
             "<font color=\"red\">" . $row["email"] .
             "</font></h1>\n";
    
        echo "<p><i>Please record your password " .
             "somewhere safe for future use</i>\n";
    
        echo "<p>Your shipping and billing details are " .
             "as follows:\n<br><b> " .
             $row["title"] . " " .
             $row["firstname"] . " " .
             $row["initial"] . " " .
             $row["surname"] . "\n<br>" .
             $row["addressline1"] . "\n";
    
        if ($row["addressline2"] != "")
           echo "\n<br>" .
                $row["addressline2"];
    
        if ($row["addressline3"] != "")
           echo "\n<br>" .
                $row["addressline3"];
    
        echo "\n<br>" .
             $row["city"] . " " .
             $row["state"] . " " .
             $row["zipcode"] . "\n<br>" .
             $row["country"] . "</b><br>\n";
    
        if ($row["phone"] != "")
           echo "\n<br><b>Telephone: " .
                $row["phone"] . "</b>";
    
        if ($row["fax"] != "")
           echo "\n<br><b>Fax: " .
                $row["fax"] . "</b>";
    
        $row["dob"] = substr($row["birth_date"], 8, 2) . "/" .
                      substr($row["birth_date"], 5, 2) . "/" .
                      substr($row["birth_date"], 0, 4);
    
        echo "\n<br><b>Date of Birth: " .
             $row["dob"] . "</b>\n<br>";
    
      }
    
      // Main ----------
    
       // Re-establish the existing session
       session_start(  );
    
       // Check if the user is logged in - this should never
       // fail unless the script is run incorrectly
       if (!session_is_registered("loginUsername"))
       {
          session_register("message");
          $message = "You must login to view your " .
                     "customer receipt.";
    
          header("Location: example.cart.1.php");
          exit;
       }
    
       // Check the correct parameters have been passed
       if (!isset($custID))
       {
          session_register("message");
    
          $message = "Incorrect parameters to " .
                     "example.customer.3.php";
    
          // Redirect the browser back to the calling page,
          // using the HTTP response header "Location:"
          // and the PHP environment variable $HTTP_REFERER
          header("Location: $HTTP_REFERER");
          exit;
       }
    
       // Check this customer matches the custID
       if ($custID != getCustomerID($loginUsername, NULL))
       {
          session_register("message");
    
          $message = "You can only view your own " .
                     "customer receipt!";
    
          $custID = getCustomerID($loginUsername, NULL);
       }
    
       // Open a connection to the DBMS
       if (!($connection = @ mysql_pconnect($hostName,
                                            $username,
                                            $password)))
          showerror(  );
    
       if (!mysql_select_db($databaseName, $connection))
          showerror(  );
    
    ?>
    <!DOCTYPE HTML PUBLIC
       "-//W3C//DTD HTML 4.01 Transitional//EN"
       "http://www.w3.org/TR/html401/loose.dtd">
    <html>
    <head>
      <title>Alexa and Dave's Online Wines</title>
    </head>
    <body bgcolor="white">
    <?php
       // Show the user login status
       showLogin(  );
    
       // Show the user any messages
       showMessage(  );
    
       // Show the customer confirmation
       show_HTML_receipt($custID, $connection);
    
       // Show buttons
       echo "<form action=\"example.cart.5.php\"" .
            " method=\"GET\">";
       echo "<table>";
    
       echo "<td><input type=\"submit\" name=\"home\"" .
            " value=\"Home\"></td>";
    
    ?>
    </table>
    </form>
    <br><a href="http://validator.w3.org/check/referer"><img
         src="http://www.w3.org/Icons/valid-html401"
         height="31" width="88" align="right" border="0"
         alt="Valid HTML 4.01!"></a>
    </body>
    </html>
    

    Example 10-4 shows the order.1 script that is used for logging into the winestore application. The script is based on Example 9-8 and Example 9-9 from Chapter 9. If the user isn't logged in-which should always be the case unless the script is unexpectedly called-and no credentials have been provided from a previous login attempt, the script displays a login <form> to the user. When the user successfully logs in, the script redirects to the calling page that's stored in the session variable referer; if referer isn't set, it redirects to the home page.

    When the user provides credentials-a username and a password-the script is re-requested through the <form> submission process. The script encrypts the password provided by the user and checks if this matches the password stored in the users table. If it matches, the user is logged in by registering the session variable loginUsername and unregistering any session variables associated with failed attempts to update customer details. The session variable loginUsername stores the user's email address, which, as discussed earlier, is the same as his username. If the password is incorrect, an error is generated, and the login <form> is redisplayed so the user can try again.

    The framework used here is typical of authentication in a web database application. However, possible improvements to the process can include limiting the number of failed login attempts, a password changing feature, a password reminder module-where the user is sent a password hint such as "What is your mother's maiden name?"-and security restrictions such as requiring that a password contain a mixture of uppercase, lowercase, numeric, and special characters.

    Example 10-4. The order.1 login script for logging into the winestore application
    <?php
    // This script manages the login process.
    // It should only be called when the user is not
    // logged in.
    // If the user is logged in, it will redirect back
    // to the calling page.
    // If the user is not logged in, it will show a login
    // <form>
    
    include 'include.inc';
    
    set_error_handler("errorHandler");
    
    function check_login($loginUsername, $loginPassword)
    {
      global $referer;
      global $username;
      global $password;
      global $hostName;
      global $databaseName;
      global $message;
    
      // Get the two character salt from the
      // user-name collected from the challenge
      $salt = substr($loginUsername, 0, 2);
    
      // Encrypt the loginPassword collected from
      // the challenge
      $crypted_password = crypt($loginPassword, $salt);
    
      // Formulate the SQL find the user
      $query = "SELECT password FROM users
                   WHERE user_name = '$loginUsername'
                   AND password = '$crypted_password'";
    
      // Open a connection to the DBMS
      if (!($connection = @ mysql_pconnect($hostName,
                                          $username,
                                          $password)))
         showerror(  );
    
      if (!mysql_select_db($databaseName, $connection))
         showerror(  );
    
      // Execute the query
      if (!($result = @ mysql_query($query, $connection)))
         showerror(  );
    
      // exactly one row? then we have found the user
      if (mysql_num_rows($result) == 1)
      {
         // Register the loginUsername to show the user
         // is logged in
         session_register("loginUsername");
    
         // Clear any other session variables
         if (session_is_registered("errors"))
            // Delete the form errors session variable
            session_unregister("errors");
    
         if (session_is_registered("formVars"))
            // Delete the formVars session variable
            session_unregister("formVars");
    
         // Do we need to redirect to a calling page?
         if (session_is_registered("referer"))
         {
            // Delete the referer session variable
            session_unregister("referer");
    
            // Then, use it to redirect
            header("Location: $referer");
            exit;
         }
         else
         {
            header("Location: example.cart.1.php");
            exit;
         }
      }
      else
      {
         // Ensure loginUsername is not registered, so
         // the user is not logged in
         if (session_is_registered("loginUsername"))
            session_unregister("loginUsername");
    
         // Register an error message
         session_register("message");
         $message = "Username or password incorrect. " .
                    "Login failed.";
    
         // Show the login page
         // so the user can have another go!
         login_page(  );
         exit;
      }
    }
    
    // Function that shows the HTML <form> that is
    // used to collect the username and password
    function login_page(  )
    {
      global $message;
    
      ?>
    <!DOCTYPE HTML PUBLIC
        "-//W3C//DTD HTML 4.0 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd" >
    <html>
      <head>
         <title>Winestore Login Page</title>
      </head>
    <body bgcolor="white">
    <?php
      // Show login status (should be logged out!)
      showLogin(  );
    ?>
        <h2>Winestore Login Page</h2>
        <form method="POST" action="example.order.1.php">
    <?php
      // Show messages
      showMessage(  );
      ?>
    <table>
    <tr>
        <td>Enter your username:</td>
        <td><input type="text" size=15
             maxlength=30
             name="loginUsername"></td>
    </tr>
    <tr><td>Enter your password:</td>
        <td><input type="password" size=15
             maxlength=8
             name="loginPassword"></td>
    </tr>
    <tr>
        <td><input type="submit" value="Log in"></td>
    </tr>
    </table>
    <br><a href="http://validator.w3.org/check/referer">
        <img src="http://www.w3.org/Icons/valid-html401"
         height="31" width="88" align="right" border="0"
         alt="Valid HTML 4.01!"></a>
    </form>
    </body>
    </html>
    <?php
    }
    
    // ------------------
    
    // Initialise the session
    session_start(  );
    
    if (isset($HTTP_POST_VARS["loginUsername"]))
       $loginUsername =
          clean($HTTP_POST_VARS["loginUsername"], 30);
    
    if (isset($HTTP_POST_VARS["loginPassword"]))
       $loginPassword =
          clean($HTTP_POST_VARS["loginPassword"], 8);
    
    // Check if the user is already logged in
    if (session_is_registered("loginUsername"))
    {
      // If they are, then just bounce them back where
      // they came from
      if (session_is_registered("referer"))
      {
         session_unregister("referer");
         header("Location: $referer");
         exit;
      }
      else
      {
         header("Location: example.cart.1.php");
         exit;
      }
    }
    
    // Have they provided only one of a username and
    // password?
    if ((empty($HTTP_POST_VARS["loginUsername"]) &&
        !empty($HTTP_POST_VARS["loginPassword"])) ||
        (!empty($HTTP_POST_VARS["loginUsername"]) &&
        empty($HTTP_POST_VARS["loginPassword"])))
    {
         // Register an error message
         session_register("message");
         $message = "Both a username and password must " .
                    "be supplied.";
    }
    
    // Have they not provided a username/password,
    // or was there an error?
    if (!isset($loginUsername) ||
        !isset($loginPassword) ||
         session_is_registered("message"))
      login_page(  );
    else
      // They have provided a login. Is it valid?
      check_login($loginUsername, $loginPassword);
    ?>

    Example 10-5 lists the winestore order.2 logout script. The script is simple: if the session variable loginUsername is registered-this variable indicates the user is logged in-it's unregistered; if it isn't registered, the script has been unexpectedly called, and an error message is generated. In either case, the script then redirects back to the calling page stored in the session variable referer, or to the home page if referer isn't set. The script is a one-component script; that is, it carries out a function, produces no output, and redirects back to the calling page.

    Example 10-5. The order.2 logout script for logging out of the winestore application
    <?php
      // This script logs a user out and redirects
      // to the calling page.
    
      include 'include.inc';
    
      set_error_handler("errorHandler");
    
      // Restore the session
      session_start(  );
    
      // Is the user logged in?
      if (session_is_registered("loginUsername"))
         session_unregister("loginUsername");
      else
      {
         // Register a message to show the user
         session_register("message");
         $message = "Error: you are not logged in!";
      }
    
      // Redirect the browser back to the calling page
      if (session_is_registered("referer"))
      {
         // Delete the redirection session variable
         session_unregister("referer");
    
         // Then, use it to redirect to the calling page
         header("Location: $referer");
         exit;
      }
      else
         header("Location: example.cart.1.php");
    ?>
    
    

    The winestore include files are shown in Example 10-6, Example 10-7, and Example 10-8. The db.inc include file in Example 10-6 and the error.inc include file in Example 10-8 are both included in the include.inc file in Example 10-7.

    Example 10-6 shows the db.inc file that lists the DBMS credentials for connecting to the winestore database. The settings must be changed for a local installation of the winestore application.

    Example 10-6. The db.inc include file
    <?php
      $hostName = "localhost";
      $databaseName = "winestore";
      $username = "fred";
      $password = "shhh";
    ?>

    The db.inc include file stores the DBMS and database credentials to access the online winestore. The hostName setting is the server name of the DBMS, the databaseName setting is the winestore database name, and the username and password are those used to access the MySQL DBMS. This file is identical to Example 4-7 and is discussed in Chapter 4.

    The include.inc file shown in Example 10-7 stores the common function used throughout the winestore application.

    Example 10-7. The include.inc file
    <?php
       // This file contains functions used in more than
       // one script in the cart module
    
       include 'db.inc';
       include 'error.inc';
    
       // Untaint user data
       function clean($input, $maxlength)
       {
         $input = substr($input, 0, $maxlength);
         $input = EscapeShellCmd($input);
         return ($input);
       }
    
       // Print out the varieties for a wineID
       function showVarieties($connection, $wineID)
       {
          // Find the varieties of the current wine,
          // and order them by id
          $query = "SELECT gv.variety
                    FROM grape_variety gv,
                         wine_variety wv, wine w
                    WHERE w.wine_id = wv.wine_id
                    AND wv.variety_id = gv.variety_id
                    AND w.wine_id = $wineID
                    ORDER BY wv.id";
    
          // Run the query
          if (!($result = @ mysql_query($query, $connection)))
             showerror(  );
    
          $varieties = "";
    
          // Retrieve and print the varieties
          while ($row = @ mysql_fetch_array($result))
             $varieties .= " " . $row["variety"];
    
          return $varieties;
       }
    
       // Show the user the details of one wine in their
       // cart
       function showWine($wineId, $connection)
       {
          global $username;
          global $password;
          global $databaseName;
    
          $wineQuery = "SELECT year, winery_name, wine_name
                        FROM winery, wine
                        WHERE wine.winery_id = winery.winery_id
                        AND wine.wine_id = $wineId";
    
          $open = false;
    
          // If a connection parameter is not passed, then
          // use our own connection to avoid any
          // locking problems
          if (!isset($connection))
          {
             if (!($connection = @ mysql_connect($hostName,
                                                 $username,
                                                 $password)))
                showerror(  );
    
             if (!mysql_select_db($databaseName, $connection))
                showerror(  );
    
             $open = true;
          }
    
          // Run the query created above on the database
          // through the connection
          if (!($result = @ mysql_query ($wineQuery,
                                         $connection)))
             showerror(  );
    
          $row = @ mysql_fetch_array($result);
    
          // Print the wine details
          $result = $row["year"] . " " .
                    $row["winery_name"] . " " .
                    $row["wine_name"];
    
          // Print the varieties for this wine
          $result .= showVarieties($connection, $wineId);
    
          if ($open == true)
             @ mysql_close($connection);
    
          return $result;
       }
    
       // Print out the pricing information for a wineID
       function showPricing($connection, $wineID)
       {
          // Find the price of the cheapest inventory
          $query = "SELECT min(cost)
                    FROM inventory
                    WHERE wine_id = $wineID";
    
          // Run the query
          if (!($result = @ mysql_query($query,
                                        $connection)))
             showerror(  );
    
          // Retrieve the cheapest price
          $row = @ mysql_fetch_array($result);
    
          printf("<b>Our price: </b>$%.2f",
                 $row["min(cost)"]);
          printf(" ($%.2f a dozen)",
                 ($row["min(cost)"] * 12));
       }
    
       // Show the total number of items and dollar value of
       // the shopping cart, as well as a clickable cart icon
       function showCart($connection)
       {
          global $order_no;
    
          // Initialise an empty cart
          $cartAmount = 0;
          $cartCount = 0;
    
          // If the user has added items to their cart,
          // then the variable order_no will be registered
          if (session_is_registered("order_no"))
          {
             $cartQuery = "SELECT qty, price " .
                          "FROM items " .
                          "WHERE cust_id = -1 " .
                          "AND order_id = " . $order_no;
    
             // Find out the number and the dollar value of
             // the items in the cart. To do this, we run
             // the cartQuery through the connection on
             // the database
             if (!($result = @ mysql_query ($cartQuery,
                                            $connection)))
                showerror(  );
    
             while ($row = @ mysql_fetch_array($result))
             {
                $cartAmount += $row["price"] * $row["qty"];
                $cartCount += $row["qty"];
             }
          }
    
          // This sets up the cart picture.
          // The user can click on it to see the contents of
          // their cart. It also contains JavaScript, so that
          // the cart highlights
          // when the mouse is over it (a "roll-over")
          echo "<table>\n<tr>\n\t<td>";
          echo "<a href=\"example.cart.2.php\" " .
               "onMouseOut=\"cart.src='cart_off.jpg'\" " .
               "onMouseOver=\"cart.src='cart_on.jpg'\"> " .
               "<img src=\"cart_off.jpg\" vspace=0 border=0 " .
               "alt=\"cart picture\" name=\"cart\"></a>\n";
          echo "\t</td>\n";
          printf("\t<td>Total in cart: $%.2f (%d items)</td>\n",
                 $cartAmount, $cartCount);
          echo "</tr>\n</table>";
       }
    
       // Display any messages that are set, and then
       // clear the message
       function showMessage(  )
       {
          global $message;
    
          // Is there an error message to show the user?
          if (session_is_registered("message"))
          {
             echo "<h3>";
             echo "<font color=\"red\">$message</font></h3>";
             // Clear the error message
             session_unregister("message");
             $message = "";
          }
       }
    
       // Show whether the user is logged in or not
       function showLogin(  )
       {
          global $loginUsername;
    
          // Is the user logged in?
          if (session_is_registered("loginUsername"))
             echo "<p align=\"right\">You are currently " .
                  "logged in as <b>$loginUsername</b></p>\n";
          else
             echo "<p align=\"right\">You are currently " .
                  "not logged in</p>\n";
       }
    
       // Show the user a login or logout button.
       // Also, show them membership buttons as appropriate.
       function loginButtons(  )
       {
          if (session_is_registered("loginUsername"))
          {
             echo "\n\t<td><input type=\"submit\"" .
                  " name=\"logout\" value=\"Logout\"></td>\n";
             echo "\n\t<td><input type=\"submit\"" .
                  "name=\"account\" value=\"Change " .
                  "Details\"></td>\n";
          }
          else
          {
             echo "\t<td><input type=\"submit\" " .
                  "name=\"login\" value=\"Login\"></td>\n";
             echo "\n\t<td><input type=\"submit\" " .
                  "name=\"account\" value=\"Become " .
                  "a Member\"></td>\n";
          }
       }
    
       // Get the cust_id using loginUsername
       function getCustomerID($loginUsername, $connection)
       {
          global $databaseName;
          global $username;
          global $password;
          global $hostName;
    
          $open = false;
    
          // If a connection parameter is not passed, then
          // use our own connection to avoid any locking
          // problems
          if (!isset($connection))
          {
              if (!($connection = @ mysql_connect($hostName,
                                                 $username,
                                                 $password)))
                 showerror(  );
    
              if (!mysql_select_db($databaseName,
                                   $connection))
                 showerror(  );
    
              $open = true;
          }
    
          // We find the cust_id through the users table,
          // using the session variable holding their
          // loginUsername.
          $query = "SELECT cust_id
                    FROM users
                    WHERE user_name = \"$loginUsername\"";
    
          if (($result = @ mysql_query ($query, $connection)))
             $row = mysql_fetch_array($result);
          else
             showerror(  );
    
          if ($open == true)
             @ mysql_close($connection);
    
         return($row["cust_id"]);
       }
    
       // Produce a <select> list containing database
       // elements
       function selectDistinct ($connection,
                                $tableName,
                                $columnName,
                                $pulldownName,
                                $additionalOption,
                                $defaultValue)
      {
         $defaultWithinResultSet = FALSE;
    
         // Query to find distinct values of $columnName
         // in $tableName
         $distinctQuery = "SELECT DISTINCT $columnName
                           FROM $tableName";
    
         // Run the distinctQuery on the databaseName
         if (!($resultId = @ mysql_query ($distinctQuery,
                                          $connection)))
            showerror(  );
    
         // Retrieve all distinct values
         $i = 0;
         while ($row = @ mysql_fetch_array($resultId))
            $resultBuffer[$i++] = $row[$columnName];
    
         // Start the select widget
         echo "\n<select name=\"$pulldownName\">";
    
         // Is there an additional option?
         if (isset($additionalOption))
            // Yes, but is it the default option?
            if ($defaultValue == $additionalOption)
               // Show the additional option as selected
               echo "\n\t<option selected>$additionalOption";
            else
               // Just show the additional option
               echo "\n\t<option>$additionalOption";
    
         // check for a default value
         if (isset($defaultValue))
         {
            // Yes, there's a default value specified
    
            // Check if the defaultValue is in the
            // database values
            foreach ($resultBuffer as $result)
               if ($result == $defaultValue)
                  // Yes, show as selected
                  echo "\n\t<option selected>$result";
               else
                  // No, just show as an option
                  echo "\n\t<option>$result";
         }  // end if defaultValue
         else
         {
            // No defaultValue
    
            // Show database values as options
            foreach ($resultBuffer as $result)
               echo "\n\t<option>$result";
         }
         echo "\n</select>";
      } // end of function
    ?>

    The include.inc file shown in Example 10-7 contains the following functions that are used throughout the winestore application:

    string clean(string   input, integer   maxlength)  

    Untaints a user-supplied input string by processing it with EscapeShellCmd( ) and takes a substring of length maxlength. Returns the untainted string. This function is discussed in Chapter 5.

    void showVarieties(resource   connection, int   wineID)  

    Queries the winestore database through the DBMS connection resource. Prints the wine varieties associated with the wine identified by the wine_id wineID.

    string showWine(int   wineID, resource   connection)  

    Queries the winestore database through the DBMS connection resource. Returns the year, winery name, wine details, and varieties of the wine identified by wineID. The function showVarieties( ) is called to output the varieties. If the connection resource is NULL, a new nonpersistent connection to the DBMS is opened and closed; this can be used to avoid having to lock the tables associated with a wine if the calling function requires locks for other operations.

    void showPricing(resource   connection, int   wineID)  

    Queries the winestore database through the DBMS connection resource. Prints the price of the wine identified by wineID and the cost of a case of that wine where-for simplicity-a case of 12 bottles costs 12 times as much as 1 bottle.

    void showCart(resource   connection)  

    Produces a shopping cart icon that is an embedded link to the script cart.2. The icon is a rollover, in which JavaScript loads a highlighted cart image when the mouse is over the image. The script also queries the winestore database through the DBMS connection resource and sums the total number of items and the dollar value of the items in the user's shopping cart. These total values are reported next to the cart.

    void showMessage( )

    Reports any messages registered in the session variable message. If a message is displayed, the session variable message is unregistered so that a message appears only once.

    void showLogin( )

    Reports whether the user is logged in or not based on whether the loginUsername session variable is registered. If the user is logged in, the message includes the user's login name.

    void loginButtons( )

    Displays <form> buttons. If the user is logged in, the "logout" and "customer change details" buttons are shown. If the user isn't logged in, the "login" and "become a member" buttons are shown.

    string getCustomerID(string   loginUsername, resource   connection)  

    Returns the cust_id associated with the user's email address or login name loginUsername. Queries the winestore database through the DBMS connection resource. If the connection resource is NULL, a new, nonpersistent connection to the DBMS is opened and closed; this can be used to avoid having to lock the tables associated with a wine if the calling function requires locks for other operations.

    void selectDistinct (resource   connection, string   tableName, string   columnName, string   pulldownName, string   additionalOption, string   defaultValue)  

    Produces a drop-down list using the HTML <select> element. Values from the columnName attribute of the table tableName are used to populate the <select> element with the name pulldownName. The <option> defaultValue is shown selected, and an additional nondatabase value-such as All-can be added with the additionalOption parameter. This function is described in detail in Chapter 5.

    A custom error handler is used in the winestore in preference to the built-in PHP error handler. Example 10-8 shows this handler incorporated in the include file error.inc.

    Example 10-8. The error.inc custom error handler
    <?
       // Trigger an error condition
       function showerror(  )
       {
          if (mysql_errno() || mysql_error(  ))
             trigger_error("MySQL error: " .
                            mysql_errno(  ) .
                            " : " . mysql_error(  ),
                            E_USER_ERROR);
          else
             trigger_error("Could not connect to DBMS",
                            E_USER_ERROR);
       }
    
       // Abort on error. Deletes session variables to leave
       // us in a clean state
       function errorHandler($errno, $errstr,
                             $errfile, $errline)
       {
          switch ($errno)
          {
             case E_USER_NOTICE:
             case E_USER_WARNING:
             case E_WARNING:
             case E_NOTICE:
             case E_CORE_WARNING:
             case E_CORE_NOTICE:
             case E_COMPILE_WARNING:
                break;
             case E_USER_ERROR:
             case E_ERROR:
             case E_PARSE:
             case E_CORE_ERROR:
             case E_COMPILE_ERROR:
                session_start(  );
    
                if (session_is_registered("message"))
                   session_unregister("message");
    
                if (session_is_registered("order_no"))
                   session_unregister("order_no");
    
                $errorString =
      "Winestore system error: $errstr (# $errno).<br>\n" .
      "Please report the following to the administrator:<br>\n" . "Error in line $errline of file $errfile.<br>\n";
    
                // Send the error to the administrator by email
                error_log($errorString, 1, "Alexa");
    ?>
    <h2>Alexa and Dave's Online wines is temporarily unavailable</h2>
    The following has been reported to the administrator:
    <br><b><font color="red"><?=$errorString;?></b></font>
    <?php
                // Stop the system
                die(  );
    
             default:
                break;
          }
       }
    ?>

    At the beginning of each script in the winestore application, the handler is registered:

    set_error_handler("errorHandler");
    
    

    After this registration, any error, warning, or notice encountered in the script will cause the function errorHandler( ) to be called.

    The function set_error_handler( ) has the following prototype:

    string set_error_handler(string error_handler)
    
    

    On success, the function returns the previously defined error handler function name as a string. The parameter error_handler is the name of the user-defined handler function, in our example errorHandler. The returned value can be used later to restore the previous error handler with set_error_handler( ).

    PHP requires that the user-defined errorHandler( ) function have at least two parameters: an error number and an error string. Three additional optional parameters can be included in a custom error handler: the script file that caused the error, the line number with the error, and additional variable context information. Our handler supports the first two of the three optional parameters.

    Eight different errors, warnings, and notices can be generated by PHP during script processing or during the precompilation process, generated by the PHP script engine itself, or triggered manually by the developer. Our errorHandler( ) function ignores all notices and warnings by returning if the error number errno parameter falls into the WARNING or NOTICE classes. However, for all errors in the ERROR class and for PARSE errors, our custom error handler carries out several actions:

    1. It logs out the user and deletes any registered session messages.

    2. It creates a string that incorporates details of the error.

    3. It emails the error message to the system administrator-in this case to the email account Alexa-using the PHP library error_log( ) function.

    4. It outputs the error message to the browser.

    The advantage of a custom error handler is that additional features, such as deleting session variables, closing database connections, and sending email messages, can be incorporated in the error process.

    The error_log( ) function has the following prototype:

    int error_log (string message, int message_type [, string destination [, string extra_headers]])
    
    

    The string message is the error message to be logged. The message_type can be 0, 1, or 3. A setting of 0 sends the message to the PHP system error logger, which is configured using the error_log directive in the php.ini file. A setting of 1 sends an email to the destination email address using the mail( ) function with any additional email extra_headers that are provided; the mail( ) function and the use of extra headers is discussed in Chapter 12. A setting of 3 appends the message to the file destination. A setting of 2 isn't available in PHP4.

    The showerror( ) function is also part of error.inc. This function is called whenever a MySQL function fails throughout the winestore scripts. The function tests if mysql_error( ) or mysql_errno( ) return nonzero values and, if so, it triggers a user-generated error using the trigger_error( ) PHP library function:

    void trigger_error (string error_message [, int error_type])
    
    

    The trigger_error( ) function has two parameters: an error_message-which is created using the return values of the MySQL error functions-and an optional error_type that's set to E_USER_ERROR. If MySQL hasn't reported an error, the mysql_connect( ) or mysql_pconnect( ) functions have failed, and a message indicating this is manually created. The result of calling trigger_error( ) is that the PHP script engine calls our custom registered handler, errorHandler( ).

    discuss this topic to forum

    relation tutorial

    No relevant information

    Category

      Ad Management (4)
      Calendars (3)
      Chat Systems (7)
      Content Management (6)
      Cookies and Sessions (8)
      Counters (8)
      Database Related (8)
      Date and Time (9)
      Development (6)
      Discussion Boards (7)
      E Commerce (6)
      Email Systems (9)
      Error Handling (5)
      File Manipulation (10)
      Flash and PHP (4)
      Form Processing (7)
      Guestbooks (8)
      Image Manipulation (3)
      Installing PHP (5)
      Introduction to PHP (9)
      Link Indexing (6)
      Mailing List Management (8)
      Miscellaneous (10)
      Networking (6)
      News Publishing (6)
      OOP (8)
      PEAR (6)
      PHP vs Other Languages (2)
      Polls and Voting (5)
      Postcards (0)
      Randomizing (8)
      Redirection (8)
      Searching (6)
      Security (6)
      Site Navigation (7)
      User Authentication (10)
      WAP and WML (7)
      Web Fetching (0)
      Web Traffic Analysis (11)
      XML and PHP (0)

    New

    Hot