. 46
( 132 .)


function nav ($offset=0, $this_script=™™, $limit=DEFAULT_LIMIT)
$offset = (int)$offset;
$limit = (int)$limit;

// don™t run things from outside this directory
if ( empty($this_script) or
dirname(realpath(__FILE__)) !=
$this_script = $_SERVER[˜PHP_SELF™];

// get the total number of entries in the guest book -
// we need this to know if we can go forward from where we are

$result = safe_mysql_query(˜select count(*) from guestbook™);
$total_rows = mysql_result($result,0,0);

print “<p>\n”;
if ($offset > 0)
// if we™re not on the first record,
// we can always go backwards
$poffset = $offset - $limit < 0 ? 0 : $offset - $limit;
print <<<EOQ
<a href=”${this_script}?offset=${poffset}”>&lt;&lt;Previous
if ($offset+$limit < $total_rows)
// offset + limit gives us the maximum record number
// that we could have displayed on this page. if it™s
Chapter 8: Guestbook 2003, the (Semi-)Bulletproof Guestbook 255

// less than the total number of entries, that means
// there are more entries to see, and we can go forward
$noffset = $offset + $limit;
print <<<EOQ
<a href=”${this_script}?offset=${noffset}”>Next Entries&gt;&gt;</a>
print “</p>\n”;

When appropriate, this function will print out links that will enable the user to
view the next set of entries, the previous entries, or both. The scope is determined
by the $offset and $limit arguments.
The first time through $offset will have no value, and therefore no previous
entries link will exist (because $offset will not be greater than 0). But if more rows
remain to be displayed, a link will be created that creates a value for $offset to be
accessed if that link is followed.
Say it™s the first time we™re executing this function, so $offset has no value,
and the database contains 10 rows. When it reaches the last if block the script will
see that there are more rows to be displayed ($offset + $limit equals two, which
is less than 10), and so the following link will be printed:

<a href=”/book/guestbook2k/view.php?offset=2”>Next

Interesting code flow
Once you understand how the functions presented thus far work, you should have
no problem figuring out how Guestbook 2003 works. For the most part, very, very
little work is done in the pages called by the browser. These pages are pretty much
an assemblage of function calls.
We will break down one file in detail so you can get the feel of how this struc-
ture works. Most of the rest you should be able to figure out by flipping between
the files and the explanations of the functions. In the following sections we will
walk through the view.php file.

The first thing you need to do in every page is include the header.php file, which
enables access to all the functions we outlined previously. After that, you should
include standard header information by calling the guestbook_start_page()
function, passing in the title of the page. Here is the logical flow of the code:


256 Part III: Simple Applications

guestbook_start_page(˜View My Guest Book!!™);


<table border=”0”>


// $preserve is passed into the cleanup_text() function (declared in
// /book/functions/basic.php). setting it to an empty value will
// cause any HTML tags in an entry to be stripped out before
// being displayed.

$preserve = ˜™;

// select_entries() (declared in header.php) should return a mysql
// result set identifier

$result = select_entries($offset);

while ($row = mysql_fetch_array($result))

print “<tr><td colspan=2>&nbsp;</td></tr>\n”;

// release memory associated with this mysql result set






Chapter 8: Guestbook 2003, the (Semi-)Bulletproof Guestbook 257

This is it. You use the global $offset variable to run the query with the
select_entries() function, and then print the results by calling the print_
entry() function within a while loop. Navigational elements are determined by
the nav() function.

The most complex portion of this application involves the ability to delete entries
from the guestbook. This stands to reason because you don™t want your guestbook
being fooled with by anonymous users. So the first thing you need to do before
deleting an entry is authenticate users. When discussing the guestbook_authenti-
cate() function, we showed how an HTTP 401 header will bring up the browser™s
username-and-password dialog box. The values entered need to be checked against
the guestbook_admin database table. The guestbook_authenticate() function
takes care of this for you, which is why it™s called at the top of the edit.php file.





guestbook_start_page(˜Edit The Guest Book™);


We use the PHP output buffering functions, ob_start() and ob_end_flush(),
to make sure that no output gets sent to the browser before the HTTP 401 header.
Otherwise, a blank line or space outside the <?php ... ?> tags in the header.php
would prevent guestbook_authenticate() from running. If you know you won™t
be using PHP as an Apache module, then you know PHP won™t be trying to send
these headers, and you can remove this code. It shouldn™t make any visible differ-
ence if you leave it in, however.
Once a valid username and password have been entered, the remainder of the
edit.php file will be sent. But this time, in addition to all the other information, a
checkbox will be included so the user can decide which entries should be deleted.
The value of the checkbox will be the primary key of the guestbook table.

while ($row = mysql_fetch_array($result))
// call the normal function to display a guestbook entry
print_entry($row,$preserve,™name™,™entry date™,™location™,™email™
258 Part III: Simple Applications


// now add an extra row to allow the user to mark this entry
// for deletion
print <<<EOQ
<td valign=”top” align=”right”><b>Delete?</b></td>
<td valign=”top” align=”left”>
<input type=checkbox name=”entry_id[]”
Yes, delete entry #{$row[˜entry_id™]}
<tr><td colspan=2>&nbsp;</td></tr>

This form is then submitted to the confirm_delete.php file. Notice how we™re
passing an array here. The name of the form element is entry_id[], which means
that when this form is passed to PHP entry_id will become an array. The number
of values in the array depends on the number of boxes checked. HTTP will not send
the unchecked boxes at all.
The first time through the confirm_delete.php file, we will print out the
entries. This will make the person deleting these entries make sure he or she isn™t
doing something stupid.

foreach ((array)$entry_id as $value)
print <<<EOQ
<li>Delete entry #$value?
<input type=”hidden” name=”entry_id[]” value=”$value”>

If any of these entries are to be deleted, this page will submit to itself, with a dif-
ferent value (confirm delete) sent by means of the submit button. This will make the
following code run:

foreach ($entry_id as $value)
print “<li>Deleting entry #$value\n”;
safe_mysql_query(“delete from guestbook where entry_id =

We loop through the $entry_id array, deleting records for each member.
Chapter 8: Guestbook 2003, the (Semi-)Bulletproof Guestbook 259

A few more scripts and functions are available to you, but these don™t warrant
much discussion. Complete copies of all the files are included on the CD. We sug-
gest you look at them and the comments to get a feel for how they fit into the

The skills you learned here may not get you the big bucks as a programmer, but if
you understand everything that is being done here, you should be in pretty good
shape as you move forward in your PHP programming life.
In particular, you should see the priority that is put on creating reusable code.
Nearly everything in this example is in functions. This makes it much more likely
than it would otherwise be that the code we write will be usable in some future
application. Additionally, in this chapter you got to see some basic validation.
Validation is an important concept and one you will need to take very seriously
when your application accepts user input.
Chapter 9


— Learning functions for creating HTML tags

— Understanding data that use a relational structure

— Putting MySQL™s date functions to work

— Working with PHP™s error-handling functions

IF GUESTBOOKS are the most common type of application on the Web, surveys are
probably second in popularity. Many sites have some sort of widget that enables
you to choose you favorite color or sports hero, or whatever, to see what percent-
age of voters take what stance.
This application will be a bit more complex than the guestbook application you
saw in Chapter 8. The programming will get a bit trickier, and the administration of
the application will require more work. Unlike the guestbook, this application will
require some knowledge of database theory. Related tables, complete with the pri-


. 46
( 132 .)