Web Database Applications with PHP \& MySQLWeb Database Applications with PHP \& MySQLSearch this book

10.4. The Winestore Include Files

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.

10.4.1. Custom Error Handlers

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, "hugh");
?>
<h2>Hugh 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 hugh—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( ).



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.