. 45
( 132 .)


it can query MySQL to check if the values are stored in the database. If they are not,
the password form is displayed again.
248 Part III: Simple Applications

Figure 8-7: Results of a 401 Unauthorized header

PRINT_ENTRY() This prints the results of a query within a table.

function print_entry($row,$preserve=™™)
if (!is_assoc($row))
return FALSE;

// walk through any arguments passed in after the first two
$numargs = func_num_args();
for ($i = 2; $i < $numargs; $i++)
$field = func_get_arg($i);

// This will transform a label string to a valid database
// field name - e.g., ˜Last Name™ becomes ˜last_name™
$dbfield = str_replace(˜ ˜, ˜_™, strtolower($field));

$dbvalue = cleanup_text($row[$dbfield],$preserve);
$name = ucwords($field);
print <<<EOQ
Chapter 8: Guestbook 2003, the (Semi-)Bulletproof Guestbook 249

<td valign=”top” align=”right”><b>$name:</b></td>
<td valign=”top” align=”left”>$dbvalue</td>

The easiest way to see how this function works is to take a look at the line of
code that calls a function. The following snippet was taken from the view.php file:


Notice that the function itself has only two default arguments ($row and
$preserve), while the call to the function has nine arguments. The first argument,
$row, is a row from a database call. It is expecting that a row was taken from a
query by means of mysql_fetch_array() so that the contents of the row are an
associative array, the keys of which are equal to the column names of the database
table. The second argument, $preserve, is needed for the cleanup_text() func-
tion, which we discussed previously in the chapter. The rest of the arguments are
equivalent to associative keys in $row.
The arguments sent to any user-defined function make up an array. The number
of elements in the array can be retrieved with func_num_args(). If we were to use
the call to print_entry() in this example, func_num_args() would return 9.
The value of each argument can then be accessed with func_get_arg(). This
allows for a structure like the one used here, where a loop accesses and then
processes each argument sent to the function. The first time through the for loop,
$field is assigned the third element in the array, name. You can use the value in
$field to access an element in the associative array $row ($row[˜name™]).
After you make sure the argument contains no capital letters or spaces, the value
is sent to the cleanup_text function and printed.
It™s nice to structure a function this way because it allows an arbitrary number of
arguments to be sent to the function. We can include one or many fields to print.

PRINT_INPUT_FIELDS() This function works much like print_entry(). func_
get_args() makes $fields an array, each element of which is an argument sent to
the function. The foreach() structure moves through all elements in the array and
prints a text field for each. The name of the field will be in one table cell, and the
input box will be in an adjoining cell.

function print_input_fields()
$fields = func_get_args();
250 Part III: Simple Applications

foreach ($fields as $field)
$value = array_key_value($_POST,$field,™™);
$label = ucwords(str_replace(˜_™,™ ˜,$field));
print <<<EOQ
<td valign=”top” align=”right”>
<td valign=top align=left>
<input type=”text” name=”$field” size=”40” value=”$value”>

Notice that we check the $_POST global array for the default value of the text
field. The check is here in the event that the user enters bad information and the
information needs to be re-presented with the values he or she entered. Why would
information need to be printed a second time? That should make perfect sense after
you read about the next function, create_entry().

CREATE_ENTRY() We are not going to simply dump user information into the
database. First it needs to be verified.

function create_entry(
$name=™™, $location=™™,$email=™™,$url=™™,$comments=™™
// remove all HTML tags, and escape any other special characters
$name = cleanup_text($name);
$location = cleanup_text($location);
$email = cleanup_text($email);
$url = cleanup_text($url);
$comments = cleanup_text($comments);

// start out with an empty error message.
// as validation tests fail, add errors to it.
$errmsg = ˜™;
if (empty($name))
$errmsg .= “<li>you have to put in a name, at least!\n”;
Chapter 8: Guestbook 2003, the (Semi-)Bulletproof Guestbook 251

// do a very simple check on the format of the email address
// supplied by the user. an email address is required.
if (!empty($email) && !preg_match(
˜/^[\w_-.+]+@[\w_-]+(\.[\w_-])+$/™, $email

$errmsg .= “<li>$email doesn™t look like a valid email
// if the format is OK, check to see if this user has
// signed the guestbook. multiple entries are not allowed.
$query = “select * from guestbook where email = ˜$email™”;
$result = safe_mysql_query($query);
if (!$result)
$errmsg .= “<li>couldn™t check the guestbook for
elseif (mysql_num_rows($result) > 0)
$errmsg .= “<li>The email address ˜$email™ has
already signed this guestbook.\n”;
die(˜<li>no previous entry found:™

// perform a very simple check on the format of the url supplied
// by the user (if any)

if (!empty($url) && !eregi(˜^http://[A-Za-z0-9\%\?\_\:\˜\/\.-
$errmsg .= “<li>$url doesn™t look like a valid URL\n”;
252 Part III: Simple Applications

if (empty($errmsg))
$query = <<<EOQ
insert into guestbook (name,location,email,url,comments,remote_addr)
values (˜%s™,™%s™,™%s™,™%s™,™%s™,™%s™)
$query = sprintf($query
, mysql_real_escape_string($name)
, mysql_real_escape_string($location)
, mysql_real_escape_string($email)
, mysql_real_escape_string($url)
, mysql_real_escape_string($comments)
, mysql_real_escape_string($_SERVER[˜REMOTE_ADDR™])

print “<h2>Thanks, $name!!</h2>\n”;
print <<<EOQ
<font color=red>
Please try again
return $errmsg;

This function is going to make sure that the information entered is moderately
useful. If there is a problem with the information, a text string describing the prob-
lem will be assigned to the variable $errmsg. If, after the function is executed,
$errmsg is empty, the values will be inserted into the database. Otherwise the error
message(s) will be printed, and the values the user entered will be assigned to glob-
als so that they can be printed as the default values in the text fields the next time
Chapter 8: Guestbook 2003, the (Semi-)Bulletproof Guestbook 253

This function checks for the following in the following order:

— That the name field contains something

— That the email address is potentially a proper address (contains text, an @,
and a period (.)) Note that this is not very strong validation of email. It
takes a very long and complicated script to thoroughly validate an email,
as you will see in later chapters.
— If the email looks OK, that this email address hasn™t been entered in the
database already
— That the URL is potentially valid

Check Appendix G for more detail on regular expressions.

SELECT_ENTRIES() This function™s sole purpose is to put together your database call.

function select_entries ($offset=0, $limit=DEFAULT_LIMIT)
// cast to make sure that these are integer values
$limit = (int)$limit;
$offset = (int)$offset;

$query = <<<EOQ
select *, date_format(created,™%e %M, %Y %h:%i %p™) as entry_date
from guestbook
order by created desc
limit $offset, $limit
$result = safe_mysql_query($query);

return $result;

You already know that DEFAULT_LIMIT sets the number of records displayed per
page. As the second argument in the limit clause, the $offset variable indicates
which records will be returned from the query. If you are having problems under-
standing $offset, take a look at the explanation of the limit clause in Chapter 3.
A value for $offset will be passed through the navigational elements. (We™ll
examine this technique in detail when we discuss the next function.)
254 Part III: Simple Applications

To retrieve the date value in a readable way, this query makes use of MySQL™s
date functions. MySQL stores the date and time as a 14-digit number (YYYY:MM:
DD:HH:SS), but it™s nicer to return the date information in a way that™s easier for
humans to read. The MySQL date_format function retrieves the information in the
way we want to use it. This function and many other MySQL functions are dis-
cussed in Appendix J.

NAV() This function™s sole purpose is to create navigational elements.


. 45
( 132 .)