Web Database Applications With Php And Mysql (2nd Edition) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Web Database Applications With Php And Mysql (2nd Edition) [Electronic resources] - نسخه متنی

David Lane, Hugh E. Williams

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








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="149">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

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="> wines</a>
<br>Explore our
<a href="> red wines</a>
<br>Explore our
<a href="> premium
reds from the Riverland</a>
<br>
<a href="149">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

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="> 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 databasewe 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

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



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

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=">Add Item 1</a>
<br><a href=">Add Item 2</a>
<br><a href=">Add Item 3</a>
<br><a href=">Add Item 4</a>
<br><a href=">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.





/ 176