6.2 Processing User Input


In this section, we build on the querying techniques discussed so far in this chapter. We focus on user-driven querying , in which the user provides data that controls the query process. To input parameters into the querying process, the user usually selects or types data into an HTML form environment, or clicks on links that request scripts.

We show you user-driven querying by introducing how to:

  • Pass data from a web browser to a web server.

  • Access user data in scripts.

  • Secure interactive query systems.

  • Query databases with user data.

  • Process data using one-component querying , where the user clicks on a link that runs a query but leaves the user on the same page. This querying process is often used to add items to a shopping cart.

6.2.1 Passing Data from the Browser to the Server

Three techniques can be used to pass data that drives the querying process in a web database application:

  • Data entry through HTML form environments. For example, form environments can capture textual input, and input is made by selecting radio buttons, selecting one or more items from a drop-down menu, clicking on buttons, or through other data entry widgets.

  • Typing in a URL. For example, a user may open a URL using the Open Page option in the File menu of the Mozilla web browser, and typing in a URL such as http://www.webdatabasebook.com/example.6-10.php?regionName=Riverland.

  • Embedded hypertext links that can be clicked to retrieve a PHP script resource and provide parameters to the script.

Of these, using an HTML form and clicking on hypertext links are the two most common techniques for providing user input for querying in web database applications.

User data or parameters are passed from a web browser to a web server using HTTP; Chapter 1 contains an introduction to HTTP and more details can be found in Appendix D. Using HTTP, data is passed with one of two methods, GET or POST. In the GET method, data is passed as part of the requested URL; the GET method gets a resource with the parameters modifying how the resource is retrieved. In the POST method, the data is encoded separately from the URL and forms part of the body of the HTTP request; the POST method is used when data is to be posted or stored on the server, and when large amounts of data is being transferred.

The HTML form environment can specify either the GET or POST method, while an embedded link or a manually entered URL with parameters always uses the GET method. In any case, the browser looks after encoding the parameters and transferring them to the server.

6.2.2 Passing Data with the HTML Form Environment

The first technique that captures data passed from a browser to a server is the HTML form environment.

Users enter data into an HTML form that is then encoded by the browser as part of an HTTP request. Example 6-10 is an HTML document that contains a form in which to enter the name of a wine region.

Example 6-10. An HTML form for entry of a regionName
<!DOCTYPE HTML PUBLIC                  "-//W3C//DTD HTML 4.01 Transitional//EN"                  "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Explore Wines in a Region</title> </head> <body bgcolor="white">   <form action="example.6-11.php" method="GET">     <br>Enter a region to browse :     <input type="text" name="regionName" value="All">        (type All to see all regions)     <br><input type="submit" value="Show wines">   </form>   <br><a href="index.html">Home</a> </body> </html>

The page, rendered with a Mozilla browser, is shown in Figure 6-3.

Figure 6-3. A simple page to capture user input
figs/wda2_0603.gif


When the user presses the button labeled Show Wines, the data entered in the form is encoded in an HTTP request for the resource example.6-11.php. The resource to be requested is specified in the action attribute of the form tag, as is the method used for the HTTP request:

<form action="example.6-11.php" method="GET">

In this form, there is only one input widget with the attribute type="text" and name="regionName". When the GET method is used, the name of this attribute and its value result are appended to the URL as query string parameters. If the user types Riverland into the text widget and then clicks on Show Wines, the following URL is requested:

http://localhost/example.6-11.php?regionName=Riverland

The resource that's requested is example.6-11.php and it's separated from the parameters by a question mark character ?.

The script example.6-11.php is shown in Example 6-11. Before this script is processed by the PHP scripting engine, variables associated with any parameters to the resource are initialized and assigned values. In this example, the array $_GET is initialized and contains an element with the key regionName. The value of $_GET["regionName"] is then automatically initialized by the PHP engine to Riverland. This variable and its value are then accessible from within the script, making the data passed by the user available in PHP.

Example 6-11. Printing a parameter value passed to the script with an HTTP request
<!DOCTYPE HTML PUBLIC                  "-//W3C//DTD HTML 4.01 Transitional//EN"                  "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Parameter</title> </head> <body> <?php   require 'db.inc';   print "regionName is {$_GET["regionName"]}\n"; ?> </body> </html>

Therefore, after submitting the form, the script in Example 6-11 outputs as a response an HTML document containing the phrase:

regionName is Riverland

The HTTP POST method can be used in a form instead of the GET method by changing the method="GET" attribute of the form tag to method="POST"; the merits of POST versus GET are discussed in more detail in Appendix D. This change of method has no effect on automatic variable initialization in PHP scripts, except that the data is stored in the array $_POST instead. You can change a script to process attributes that are passed with a POST request by changing all references to $_GET to $_POST.

All form fields are automatically stored in either the PHP array $_GET or $_POST for direct use in scripts.

This is one of the best features of PHP, making it far simpler to write web-enabled scripts in PHP than in other languages. However, it introduces a security risk discussed later in Section 6.2.7.


6.2.3 Passing Data with URLs

The second technique that passes data from a web browser to a web server is manual entry of a URL in a web browser.

Consider an example user request with a parameter. In this example, the user types the following URL directly into the location bar of a Mozilla browser:

http://localhost/example.6-11.php?regionName=Yarra+Valley

The URL specifies that the resource to be retrieved is example.6-11.php with a query string parameter of regionName=Yarra+Valley appended to the resource name. The user then presses the Enter key to issue an HTTP request for the resource and to use the GET method that passes the parameter to the resource. The query string parameter consists of two parts: a parameter name regionName and a value for that parameter of Yarra+Valley.

As with the form example in the previous section, an HTML document is created with the value of the query string parameter printed as part of the output:

regionName is Yarra Valley

The plus (+) character that was used instead of a space (since spaces aren't allowed in URLs) has been decoded back to a space character by the PHP scripting engine. A list of characters that must be encoded in URLs and an explanation of how encoding works can be found in Appendix D.

More than one parameter can be passed with an HTTP GET request by separating each parameter with the ampersand character; the browser performs this automatically when a form is used. For example, to pass two parameters regionName and type with the values Yarra and Red, respectively, the following URL can be created:

http://localhost/test.php?regionName=Yarra&type=Red

The values of these parameters can then be printed in the script test.php using the fragment:

print $_GET["regionName"]; print $_GET["type"];

6.2.4 Passing Data with Embedded Links

The third technique that passes data from a web browser to a web server is embedding links in an HTML document. It's conceptually similar to manually entering a URL.

Example 6-12 shows how embedded links in an HTML document are created in almost the same way as a URL is typed into a web browser.

Example 6-12. An HTML document with three links that pass two different parameters
<!DOCTYPE HTML PUBLIC                  "-//W3C//DTD HTML 4.01 Transitional//EN"                  "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Explore Wines</title> </head> <body bgcolor="#ffffff"> Explore all our  <a href="example.6-13.php?regionName=All&amp;wineType=All"> wines</a> <br>Explore our  <a href="example.6-13.php?regionName=All&amp;wineType=Red"> red wines</a> <br>Explore our  <a href="example.6-13.php?regionName=Riverland&amp;wineType=Red"> premium  reds from the Riverland</a> <br> <a href="index.html">Home</a></body> </html>

The script is rendered in a Mozilla browser in Figure 6-4.

Figure 6-4. The HTML document shown in Example 6-12 rendered in a Mozilla browser
figs/wda2_0604.gif


The script contains three links that can request the resource example.6-13.php and pass different parameters to the resource. For example, the first link in the HTML document is:

Explore all our  <a href="example.6-13.php?regionName=All&amp;wineType=All"> wines</a>

Clicking on this link creates an HTTP request for the URL:

http://localhost/example.6-13.php?regionName=All&wineType=All

The result of the request is that the script in Example 6-13 is run. The script doesn't query the database we show you how to do that in the next section. Instead, the following simple HTML document is created:

<!DOCTYPE HTML PUBLIC                  "-//W3C//DTD HTML 4.01 Transitional//EN"                  "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Parameters</title> </head> <body> regionName is All <br>wineType is All </body> </html>

Example 6-13. A simple script to print out HTTP attributes and values
<!DOCTYPE HTML PUBLIC                  "-//W3C//DTD HTML 4.01 Transitional//EN"                  "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Parameters</title> </head> <body> <?php   require 'db.inc';   print "regionName is {$_GET["regionName"]}\n";   print "<br>wineType is {$_GET["wineType"]}\n"; ?> </body> </html>

The ampersand characters in the URLs in the HTML document are replaced with &amp; because the ampersand character has a special meaning in HTML and should not be included directly in a document. When the link is clicked, the encoded &amp; is translated by the browser to & in forming the HTTP request.

6.2.5 More on Accessing User Data

As we discuss in this section, in PHP 4.2 or later, user data that is passed from the browser to the server using the GET or POST methods can be found in the PHP arrays $_GET and $_POST. Similarly:

  • Cookie variables can be found in the array $_COOKIE.

  • Environment variables can be found in the array $_ENV.

  • Session variables can be found in the array $_SESSION.

  • Server variables can be found in the array $_SERVER.

Cookies and sessions are discussed in Chapter 10.

The arrays that hold the external data are superglobals . This makes them a little different to the global variables that are discussed in Chapter 2. Superglobals are accessible anywhere within a script, even in functions, without declaring them using the global keyword. For example, the following code prints out the value of the variable input that was passed using the GET method:

function printout( ) {    print $_GET["input"]; }

The variable $_GET shouldn't be declared as global in the function.

6.2.5.1 Before PHP 4.2

Prior to PHP 4.2, variables were, by default, initialized differently. This behavior was controlled by the option register_globals=true in the php.ini configuration file; this option used to be set to true, but it is now set to false by default. The effect of this setting being on is that a PHP variable is automatically initialized for every external variable or parameter that is set. For example, if the user passes parameters with a URL:

http://localhost/example.4-11.php?regionName=Yarra+Valley

then a variable $regionName is automatically initialized and set to Yarra Valley when the script engine starts.

This feature is useful, and allows you to forget about the different arrays that contain external data. However, the problem is that it is a security risk if you're not careful: a user can override an internal parameter such as a path by passing a variable of the same name from the browser. The degree of risk depends on the configuration of the initialization process and how you go about validating the data. However, in this edition of the book, we follow the post-PHP 4.2 approach of accessing variables through their arrays. We recommend you leave the register_globals feature turned off.

If you are using a version of PHP prior to PHP 4.2 and you decide to turn off the register_globals feature, you'll find the arrays that contain the variables are different to PHP 4.2 and later. GET variables are found in $HTTP_GET_VARS, POST variables in $HTTP_POST_VARS, session variables in $HTTP_SESSION_VARS, environment variables in $HTTP_ENV_VARS, and server variables in $HTTP_SERVER_VARS. For backwards compatibility, you can still use these variable names in newer versions of PHP.

6.2.6 Processing Form Data

In this section, we discuss selected peculiarities of the HTML form environment and what is actually submitted from a form in an HTTP request.

6.2.6.1 The MULTIPLE attribute

As you've seen so far, simple form elements, such as the input element, allow only one value to be associated with them. For example, the tag <input name="surname"> may have an associated value of Smith, and a URL using the GET method, this association is represented as surname=Smith.

The <select multiple> tag allows users to select zero or more items from a list. When the selected values are sent through using the GET or POST methods, each selected item has the same variable name but a different value. For example, consider what happens when the user selects options b and c from the following:

<select multiple name="choice"> <option value="a">a</option> <option value="b">b</option> <option value="c">c</option> <option value="d">d</option> </select>

When the user clicks Submit, the following URL is requested with the GET method:

http://localhost/click.php?choice=b&choice=c

From a PHP perspective, this means that the variable $_GET["choice"] is overwritten as the request is decoded, and $_GET["choice"] has the last value that was selected. In this example, print $_GET["choice"] outputs c.

The most elegant and simple solution to the multiple choice problem is to use a PHP array feature. This works as follows. First, you modify the form and replace the name of the select multiple element with an array-like structure, name="choice[]". In the previous example, the select multiple element is renamed as choice[]:

<select multiple name="choice[]"> <option value="a">a</option> <option value="b">b</option> <option value="c">c</option> <option value="d">d</option> </select>

Then, the PHP engine treats the variable as an array and adds the multiple values to the array $_GET["choice"], and the elements can be accessed as, for example, $_GET["choice"][0] and $_GET["choice"][1].

If the user selects options b and c, the following PHP fragment prints out all selected values, in this case both b and c:

foreach($_GET["choice"] as $value)   print $value;

The bracket array notation in a form can cause some problems with client-side scripts (such as those written in JavaScript, which is discussed in Chapter 9) and such form elements should be referenced wrapped in single quotes in a JavaScript script.


Interestingly, the names of <textarea> and <input> tags can also be suffixed with brackets to put values into an array, should the need arise.

6.2.6.2 Other form issues

Checkbox elements in a form have the following format:

<input type="checkbox" name="showgraphics">

A checkbox has two states, on and off, and is usually rendered as a small clickable square in a graphical web browser. Assuming the form action requests the script click.php and the checkbox in the example is clicked, the following URL is requested:

http://localhost/click.php?showgraphics=on

However, if the checkbox isn't clicked, the URL requested is as follows:

http://localhost/click.php

The important difference is that a checkbox is never submitted with a value of off. If the checkbox isn't clicked, no variable or value is submitted to the server. Therefore, in a PHP script, a checkbox should be tested with the following fragment:

if ($_GET["showgraphics"] == "on")    echo "Checkbox is on"; else    echo "Checkbox is off";

Sometimes, if a checkbox is the only widget in a form and it isn't clicked, it isn't possible to determine whether the form has been submitted or has never been displayed. An easy solution is to add a name attribute to the submit input element. For example:

<form method="GET" action="click.php"> <input type="checkbox" name="showgraphics"> <input type="submit" name="submit" value="Submit Query"> </form>

If this form is submitted with the checkbox in the off state, the following URL is requested:

http://localhost/click.php?submit=Submit+Query

The variable $_GET["submit"] is now set when the form is submitted, even when the checkbox is in the off state. You can use this to identify when the checkbox is off using a PHP fragment such as the following:

// Was the form submitted but the checkbox not clicked? if (isset($_GET["submit"]) && !isset($_GET["showgraphics"]))   print "Checkbox wasn't clicked";

Multiple select elements have the same property as checkboxes: if no item in the list is selected, no variable or value is submitted to the server.

6.2.7 Security and User Data

This section introduces simple techniques that preprocess user data to solve many common security holes in web database applications. User data that has not been preprocessed or cleaned is often known as tainted data, a term originating from the Perl scripting language. Rectifying this through the processing we describe untaints user data. You should untaint user data before using it in your application.

Using the techniques described here doesn't completely secure a system. Remember that securing a web database application is important, and that the advice offered here isn't a complete solution. A discussion of other security issues is presented in Chapter 11.


Data that is passed from a web browser to a web server should be secured using the steps described here. For this purpose, we have authored the shellclean( ) and mysqlclean( ) functions to ensure that the data passed to a script is of the correct length and that special characters aren't misused to attack the system. To understand why the functions are needed, we describe example attacks throughout this section. The functions are part of the require file db.inc that is used in all example scripts in Chapter 6 through Chapter 13.

Consider the following script. It uses the PHP exec( ) library function to run a program on the web server. The exec( ) function takes two parameters, the program to run and an array that is subsequently populated with any output of the program. In this example, the script uses exec( ) to run the unix cal program and to pass the user-entered parameter $_GET["userString"] to the program. The information in the parameter userString can be provided by using an HTML form with a text input widget, by manually creating a URL, or by embedding a link in an HTML document.

<?php   /* DO NOT INSTALL THIS SCRIPT ON A WEB SERVER */ ?> <!DOCTYPE HTML PUBLIC                  "-//W3C//DTD HTML 4.01 Transitional//EN"                  "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">     <title>Calendar</title>   </head> <body> <pre> <?php    // Run "cal" with the parameter $userString    // Store the results in the array $result    exec("/usr/bin/cal {$_GET["userString"]}", $result);    // Print out each line of the calendar    foreach($result as $element)       echo  "$element\n"; ?> </pre> </body> </html>

Never use exec( ) or other commands to run programs from a web script or to query a database without untainting the user data. Do not install the calendar example on a web server.


The Unix cal program is a useful utility that produces monthly or yearly calendars for any date. For example, to produce a calendar for the whole of 2003, a user could request the URL:

http://localhost/cal.php?userString=2003

This runs the command /usr/bin/cal 2003 and outputs the complete 2003 calendar, as shown in Figure 6-5.

Figure 6-5. Output of the dangerous calendar example when the user requests a 2003 calendar
figs/wda2_0605.gif


To produce a calendar for February 2007, the user requests:

http://localhost/cal.php?userString=2+2007

Requesting the URL without any parameters produces the calendar for the current month:

http://localhost/cal.php

While this script might seem useful and innocuous, this script is a major security hole and should never be installed on a web server.

Consider how the script can be misused. If a user wants to enter two or more commands on a single line, he can do so by separating the commands with a semicolon character. For example, to see who is logged in and then to list the files in the current directory, he can type the following commands at a Unix shell:

% who ; ls

Now consider what happens if he exploits this feature by requesting this URL:

http://localhost/cal.php?userString=2004;cat+/etc/passwd

The script produces a 2004 calendar, followed by the system password file, as shown in Figure 6-6! The script allows a creative user to do things the web server process can do. The identity of the owner of the web server process affects the severity of the actions that can be performed, but this is at best a major security hole. Similar problems can occur on a Microsoft Windows machine.

Figure 6-6. Output when the user requests a 2004 calendar and the system password file
figs/wda2_0606.gif


Semicolons, colons, greater-than and less-than signs, and other special characters can cause a script or a query to provide undesirable functions. This is especially a problem if the script uses the PHP library functions system( ) , shell_exec( ) , passthru( ) , and exec( ), because these functions potentially give hackers access to programs on the server. Even if a form makes it difficult for a user to enter undesirable data, he can manually create his own request by entering a URL and authoring a query string.

Never trust anything you don't have control of, which is anything not in the middle or database tiers.


To improve security and prevent special-character attacks, user data that is passed to programs should be processed with the shellclean( ) function:

function shellclean($array, $index, $maxlength) {   if (isset($array["{$index}"]))   {     $input = substr($array["{$index}"], 0, $maxlength);     $input = EscapeShellArg($input);     return ($input);   }   return NULL; }

The function expects an array (usually $_GET or $_POST) as the first parameter, and a name of a user variable as an index into the array as the second parameter. The third parameter specifies the maximum allowed length of the variable.

The first line of shellclean( ) checks if there's an element in $array with the name $index. If so, the second line uses the substr( ) function to reduce the variable $input to a maximum length of $maxlength by taking a substring beginning at the first character. For the calendar example you might use a maximum length of seven. The third line calls the library function EscapeShellArg( ), which encloses the string argument $input in single quotation marks. This has the same effect on a shell command as it does in PHP: it causes all characters except the single quotation to be treated as strings of symbols with no function. This makes special characters harmless when they're passed as parameters to programs.

For many purposes, the shellclean( ) steps are sufficient to ensure data is safe. As an example, if a parameter userString is passed with the GET method and has a value of:

2001;cat /etc/passwd

then a call of:

shellclean($_GET, "userString", 7)

produces the harmless single-quoted string '2001;cat'. This string has no detrimental effect and provides the user with no hidden data.

Our philosophy for processing data is to allow all input except the subset of strings that may cause problems. A stricter approach is to deny all strings except the subset of strings that are allowed for a particular field. For example, in our calendar example, we might only allow strings that consist entirely of numbers and at most one space that match a template of allowed strings. We could do this with a regular expression such as:

if (ereg("^(([0-9]{1,2}[ ][0-9]{4})|([0-9]{4}))$", $_GET["userString"]))   // Parameter is OK

We show you field validation techniques, including using regular expressions, in Chapter 9.

SQL querying also has problems. For example, a user can guess the structure of database tables and how a query is formed from user input. A user might guess that a query uses an AND clause and that a particular form text widget provides one of the values to the query. The user might then add additional AND and OR clauses to the query by entering a partial SQL query in the text widget. While such tricks may expose data that should remain hidden from the user, problems compound if the user inserts or deletes data with the techniques discussed in Chapter 8.

To deal with attacks that change your SQL statements, you can use the shellclean( ) function to enclose the user string in single quotations. This works reasonably well, but a better special-purpose approach is to make use of the mysql_real_escape_string( ) function that we discuss later in this chapter. This function inserts a backslash character before each special character, taking into consideration the character set being used on the current connection. We use this function together with substr( ) in our mysqlclean( ) function that we include in the db.inc file:

function mysqlclean($array, $index, $maxlength, $connection) {   if (isset($array["{$index}"]))   {      $input = substr($array["{$index}"], 0, $maxlength);      $input = mysql_real_escape_string($input, $connection);      return ($input);   }   return NULL; }

As with running shell programs, many of the problems of SQL attacks can also be solved with careful server-side validation, and we return to this in Chapter 9.

6.2.8 Querying with User Input

To introduce querying with user input, we begin by explaining a script that retrieves the wines made in a wine region that is specified by a user. This script, shown in Example 6-14, is a companion to the HTML form from Example 6-10. (If you've installed our examples using the instructions in Appendix A through Appendix C, you'll find a modified version of Example 6-10 in the file example.6-14b.php. Load the file example.6-14b.php in your browser to test Example 6-14.)

Example 6-14. A script to display all wineries in a region
<!DOCTYPE HTML PUBLIC                  "-//W3C//DTD HTML 4.01 Transitional//EN"                  "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Exploring Wines in a Region</title> </head> <body bgcolor="white"> <?php   require 'db.inc';   // Show all wines in a region in a <table>   function displayWinesList($connection,                             $query,                             $regionName)   {      // Run the query on the server      if (!($result = @ mysql_query ($query, $connection)))         showerror( );      // Find out how many rows are available      $rowsFound = @ mysql_num_rows($result);      // If the query has results ...      if ($rowsFound > 0)      {          // ... print out a header          print "Wines of $regionName<br>";          // and start a <table>.          print "\n<table>\n<tr>" .                "\n\t<th>Wine ID</th>" .                "\n\t<th>Wine Name</th>" .                "\n\t<th>Year</th>" .                "\n\t<th>Winery</th>" .                "\n\t<th>Description</th>\n</tr>";          // Fetch each of the query rows          while ($row = @ mysql_fetch_array($result))          {             // Print one row of results             print "\n<tr>\n\t<td>{$row["wine_id"]}</td>" .                   "\n\t<td>{$row["wine_name"]}</td>" .                   "\n\t<td>{$row["year"]}</td>" .                   "\n\t<td>{$row["winery_name"]}</td>" .                   "\n\t<td>{$row["description"]}</td>\n</tr>";          } // end while loop body          // Finish the <table>          print "\n</table>";      } // end if $rowsFound body      // Report how many rows were found      print "{$rowsFound} records found matching your criteria<br>";   } // end of function   // Connect to the MySQL server   if (!($connection = @ mysql_connect($hostName, $username, $password)))      die("Could not connect");   // Secure the user parameter $regionName   $regionName = mysqlclean($_GET, "regionName", 30, $connection);   if (!mysql_select_db($databaseName, $connection))      showerror( );   // Start a query ...   $query = "SELECT wine_id, wine_name, description, year, winery_name             FROM   winery, region, wine             WHERE  winery.region_id = region.region_id              AND    wine.winery_id = winery.winery_id";    // ... then, if the user has specified a region, add the regionName     // as an AND clause ...    if (isset($regionName) && $regionName != "All")      $query .= " AND region_name = \"{$regionName}\"";    // ... and then complete the query.    $query .= " ORDER BY wine_name";    // run the query and show the results    displayWinesList($connection, $query, $regionName); ?> </body> </html>

The script in Example 6-14 uses the querying techniques discussed so far in this chapter. However, this example differs from the previous ones in several ways:

  • It expects input of a wine region to be provided through a form input element with the name regionName.

  • The automatically initialized variable $_GET["regionName"] is untainted with the mysqlclean( ) function we discussed in the previous section and then stored in $regionName.

  • The value of the variable $regionName is used in querying.

The script builds an SQL query to find wine and winery information for the region entered by the user through the form in Example 6-10. If the user enters a regionName into the form, an additional AND clause is added to the query that restricts the r.region_name to be equal to the user-supplied region name. For example, if the user enters Margaret River, the clause:

AND r.region_name = "Margaret River"

is added to the query.

If the $regionName is All, no restriction on region is made, and the query retrieves wines for all regions.

The function displayWinesList( ) is called to run the query. It produces a table with headings, processes the result set and produces table rows, and finishes the table with a message indicating how many rows are present in the table. This is similar functionality to that discussed earlier in this chapter.

Other than the processing of the user parameter and the handling of the All regions option, no new functionality is introduced in allowing the user to drive the query process in this example.

6.2.9 One-Component Querying

Many applications allow the user to click on a link that redisplays the same resource but incorporates a change, such as adding a shopping item chosen by the user. This is one-component querying, in which the query input component and the results are displayed on the same page. In this section, we discuss how one-component querying is used and the principles of adding one-component queries to an application.

Figure 6-7 illustrates the principle of one-component querying. Let's assume the user is viewing the page browse.php in which we refer to this as the calling page. When the user selects a link on the calling page, an HTTP request for a PHP script addcart.php is sent to the server. At the server, the script addcart.php is interpreted by the PHP script engine and, after carrying out the database actions in the script, no output is produced. Instead (and this is the key to one-component querying) an HTTP Location: header is sent as a response to the web browser, and this header causes the browser to request the original calling page, browse.php. The result is that the calling page is redisplayed, and the user has the impression that she remained on the query input component page.

Figure 6-7. The principle of one-component querying
figs/wda2_0607.gif


A good example of an application of one-component querying is adding items to a shopping cart. One excellent way to support this in our winestore would be to author a script that adds the wine to the user's cart and then redirects the user back to continue shopping. The cart is updated after a click, and the user can continue purchasing wines. We use this technique in Chapter 17.

Example 6-15 shows a one-component script that is requested by a calling page. In practice, the script adds items to a user's shopping cart. However, for simplicity the database queries are not included here.

Example 6-15. Implementing one-component querying
<?php    require 'db.inc';    // Database activity occurs here -- process $_GET["input"]    // This is the key to one-component querying:    // Redirect the browser back to the calling page, using     // the HTTP response header "Location:" and the PHP server     // variable $_SERVER["HTTP_REFERER"]    header("Location: {$_SERVER["HTTP_REFERER"]}");    exit; ?>

The key to Example 6-15 is the final two lines of a successful execution of the script:

header("Location: {$_SERVER["HTTP_REFERER"]}"); exit;

The header( ) function sends an additional HTTP response header. In one-component querying, the response includes the Location header that redirects a browser to another URL, in this case the URL of the calling page. The URL of the calling page is automatically initialized into the PHP web server environment variable $_SERVER["HTTP_REFERER"]. The exit statement causes the script to abort after sending the header so any further statements in the script won't be executed.

We've used the superglobal array element $_SERVER["HTTP_REFERER"] in conjunction with the header( ) function to redirect to the calling page. This doesn't work on all Microsoft Windows environments. To fix this problem, you need to replace $_SERVER["HTTP_REFERER"] with a script file name. For example, in Example 6-15, replace it with example.6-16.php.


Example 6-16 shows an example calling page for the script in Example 6-15. By clicking on the links in the page, the user can submit different values for the input variable to the one-component script for processing. In practice, the links themselves might be generated using an SQL query.

Example 6-16. An example calling page for one component querying.
<!DOCTYPE HTML PUBLIC                  "-//W3C//DTD HTML 4.01 Transitional//EN"                  "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>One Component Test Page</title> </head> <body> <br><a href="example.6-15.php?input=item1">Add Item 1</a> <br><a href="example.6-15.php?input=item2">Add Item 2</a> <br><a href="example.6-15.php?input=item3">Add Item 3</a> <br><a href="example.6-15.php?input=item4">Add Item 4</a> <br><a href="example.6-15.php?input=item5">Add Item 5</a> </body> </html>

The header( ) command can be issued only before data is sent. In one-component querying, the script that carries out the database actions shouldn't produce any output, so this usually isn't a problem. A call to the header( ) function should also be followed by an exit statement if no further processing of statements after the header( ) function call is desired. We discuss the symptoms of header( ) function problems and how to solve them in Chapter 12.


One-component querying is useful in situations where only the query screen is required, or the results page and the query page are the same resource. For example, in the winestore, one-component querying is used to update quantities in the shopping cart when the user alters the quantities of wine. In general, one-component querying works well for simple update operations; these are the subject of Chapter 8.



Web Database Application with PHP and MySQL
Web Database Applications with PHP & MySQL, 2nd Edition
ISBN: 0596005431
EAN: 2147483647
Year: 2003
Pages: 176

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net