Tải bản đầy đủ (.pdf) (86 trang)

Professional PHP Programming phần 8 pptx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.11 MB, 86 trang )

} // End of while

Finally we redirect the client to user_admin.php page.

header("Location:http://$HTTP HOST/$DOCROOT/user admin.php");
?>
Viewing the Transactions of a User
The script view_transactions.php displays the transactions of a user as shown in an earlier
screenshot, for example, when the administrator clicks on a user's link, such as hrawat, to view the
transactions of the user Harish Rawat.

<?php
require 'functions.php';

Verify that the administrator is already authenticated:

if(!authenticateUser($cookie user, $cookie passwd)){
header("Location:http://$HTTP HOST/$DOCROOT/admin.htm");
exit();
}

// Connect to the Database
if (!($link = mysql pconnect($DB SERVER, $DB LOGIN, $DB PASSWORD))){
DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql_errno(), mysql_error()));
exit() ;
}
?>

<HTML>
<HEAD>


<TITLE>Transactions of the User !! </TITLE>
</HEAD>

<BODY BGCOLOR="#F0F3D1">

<DIV ALIGN="left">
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" WIDTH="90%">
<TR>
<TD WIDTH="50%" ALIGN="right"><IMG SRC="wrox.gif" ALT="WroxWare" WIDTH="228"
HEIGHT="70">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</TD>
<TD WIDTH="50%"><IMG SRC="Shopping Mall.gif" ALT="Shopping Mall"
WIDTH="318" HEIGHT="87"></TD>
</TR>
</TABLE>
</DIV>

<DIV ALIGN="center"><CENTER>
<TABLE BORDER="0" CELLSPACING="1" WIDTH="100%" ALIGN="center">

























































<TR>
<TD WIDTH="25%" ALIGN="center"><A HREF="user admin.php">
<IMG SRC ="User records.gif" ALT="User Records" BORDER="0"></A>
</TD>
<TD WIDTH="25%" ALIGN="center"><A HREF="transaction admin.php">
<IMG SRC = "Transaction.gif" ALT="Today's Transactions" BORDER="0"></A>
</TD>
<TD WIDTH="25%" ALIGN="center"><A HREF="search user.htm">
<IMG SRC = "Search for user.gif" ALT="Search for user !" BORDER="0"></A>
</TD>
<TD WIDTH="25%" ALIGN="center"><A HREF="logout admin.php">
<IMG SRC = "Logout admin.gif" ALT="Logout !" BORDER="0"></A>
</TD>
</TR>
</TABLE>

</CENTER></DIV>
<BR>
<CENTER>
<FONT COLOR="#804000" FACE="Sans Serif"><SMALL><STRONG>
Records of user : <?php echo($userid); ?> </STRONG></SMALL></FONT><BR>


To get the details of the user $user-id from the user_profile table:

<?php
/* Read records from table transaction to read Account Status */
if (!($result = mysql db query($DB, "select * from user profile where
user id='$userid'" ))){

DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));
exit() ;
}

/* Read one record from the queried data */
if (($row = mysql fetch array($result))) {

?>

Display the current account balance of the user and free the memory associated with $result
variable:

<FONT COLOR="#804000" FACE="Sans Serif"><SMALL><STRONG>
<?php echo ("Current Account Balance : $");
echo ($row["account balance"]);

?>

</STRONG></SMALL></FONT>
<BR>
<?php
}

mysql_free_result($result) ; // free memory associated with $result

























































?>

<BR>
<TABLE BORDER="1" CELLSPACING="0" WIDTH="80%" CELLPADDING="2">
<TR>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL><STRONG>
Order No.</STRONG></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL><STRONG>
Item No.</STRONG></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL><STRONG>
Quantity</STRONG></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL><STRONG>
Date</STRONG></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL><STRONG>
Status</STRONG></SMALL></FONT></TD>
</TR>

<?php

Get all the transactions of the user $user-id from the transaction table. The variable
$result contains all the rows of the transaction table for which the value of the user_id column is
$userid.

/* Read records from table transaction to read user names */
if (!($result = mysql db query($DB, "select * from transaction where
user id='$userid'" ))){
DisplayErrMsg(sprintf("internal error %d:%s\n",

mysql errno(), mysql error()));
exit() ;
}

Display all the transactions of the user, and the details of each transaction:

/* Read one record at a time from the queried data */
while ($row = mysql fetch array($result))
{
?>

<TR>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL>
&nbsp;<?php echo($row["order no"]);?></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL>
&nbsp;<?php echo($row["item no"]); ?></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL>
&nbsp;<?php echo($row["quantity"]); ?></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL>
&nbsp;<?php echo($row["date"]); ?></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL>
&nbsp;<?php echo($row["status"]); ?></SMALL></FONT></TD>
</TR>

<?php
} // End of while

























































?>

</TABLE>
</CENTER>
</BODY>
</HTML>
Transactions of the Day
The script transaction_admin.php is executed on the web server when the administrator clicks
on the Transactions link from any of the pages of the application. This page displays all the

transactions of the day. The administrator can change the status of the transactions to Shipped, after
the items are shipped to the user.

The script code of transaction_admin.php looks like this:

<?php
require 'functions.php';

As usual, we begin by verifying that the administrator is already authenticated, but also store the
current date in a variable, $today:

if(!authenticateUser($cookie user, $cookie passwd)){
header("Location:http://$HTTP HOST/$DOCROOT/admin.htm");
exit();
}

// Connect to the Database
if (!($link = mysql pconnect($DB SERVER, $DB LOGIN, $DB PASSWORD))){
DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));
exit() ;
}

// Today’s Date
$today = date("Y-m-d");

We then get the list of all the users from user_profile table:

/* Read all records from table user profile to list all users */
if (!($result = mysql db query($DB, "select * from user profile" ))){

DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));
exit() ;
}

The script then creates an array $users containing the user-ids of all the users and then frees the
memory associated with $result variable:

// Initialize counter and create an array of all the users
$user count = 0;
while ($row = mysql_fetch_array($result))

























































{
$users[$user count] = $row["user id"];
$user count++;
}

mysql free result($result) ;
?>


The following draws the HTML page shown in an earlier screenshot.


<HTML>
<HEAD>
<TITLE>Transactions of the day !!</TITLE>
</HEAD>
<BODY BGCOLOR="#F0F3D1">

<DIV ALIGN="left">
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" WIDTH="90%">
<TR>
<TD WIDTH="50%" ALIGN="right">
<IMG SRC="wrox.gif" ALT="WroxWare" WIDTH="228" HEIGHT="70">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

</TD>
<TD WIDTH="50%">
<IMG SRC="Shopping Mall.gif" ALT="Shopping Mall" WIDTH="318" HEIGHT="87">
</TD>
</TR>
</TABLE>
</DIV>

<DIV ALIGN="center"><CENTER>
<TABLE BORDER="0" CELLSPACING="1" WIDTH="100%" ALIGN="center">
<TR>
<TD WIDTH="25%" ALIGN="center">
<A HREF="user admin.php">
<IMG SRC ="User records.gif" ALT="User Records" BORDER="0">
</TD>
<TD WIDTH="25%" ALIGN="center">
<IMG SRC = "Transaction.gif" ALT="Today's Transactions" BORDER="0">
</TD>
<TD WIDTH="25%" ALIGN="center"> <A HREF="search user.htm">
<IMG SRC = "Search for user.gif" ALT="Search for user !" BORDER="0">
</A>
</TD>
<TD WIDTH="25%" ALIGN="center">
<A HREF="logout admin.php">
<IMG SRC = "Logout admin.gif" ALT="Logout !" BORDER="0"></A>
</TD>
</TR>
</TABLE>

The script now creates a form for shipping with ship_order.php as the actioned script.


























































<TABLE BORDER="0" CELLPADDING="3" CELLSPACING="0" WIDTH="95%">
<TR>
<TD WIDTH="100%">&NBSP;<FORM METHOD="POST" ACTION="ship order.php">
<DIV ALIGN="center"><CENTER>

<TABLE BORDER="0" CELLSPACING="0" WIDTH="80%" HEIGHT="63">
<TR>
<TD WIDTH="17%" ALIGN="center" HEIGHT="36">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL><STRONG>
Check to Ship Order</STRONG></SMALL></FONT></TD>
<TD WIDTH="22%" ALIGN="center" HEIGHT="36">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL><STRONG>
User</STRONG></SMALL></FONT></TD>
<TD WIDTH="15%" ALIGN="center" HEIGHT="36">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL><STRONG>
Order No.</STRONG></SMALL></FONT></TD>
<TD WIDTH="14%" ALIGN="center" HEIGHT="36">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL><STRONG>
Item No.</STRONG></SMALL></FONT></TD>
<TD WIDTH="15%" ALIGN="center" HEIGHT="36">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL><STRONG>
Quantity</STRONG></SMALL></FONT></TD>
<TD WIDTH="17%" ALIGN="center" HEIGHT="36">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL><STRONG>
Status</STRONG></SMALL></FONT></TD>
</TR>

For all users, display their transactions of the day:

<?php
for ($i=0;$i<$user count;$i++) {
if (!($result = mysql db query($DB, "select * from transaction where
user id='$users[$i]' AND date='$today' "))){
DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));

exit() ;
}

$new count = 0;

while (($row = mysql fetch array($result)))
{
?>

Now we display checkboxes for each user, setting the name of the checkbox as the user_id of the
particular user. The administrator can select the checkbox to change the status of the transactions of a
user from Pending to Shipped.

<TR>
<TD WIDTH="17%" ALIGN="center" HEIGHT="19"><FONT FACE="Sans Serif">

<?php // Code to show Status check box once
if ($new_count==0){

























































?>

<INPUT TYPE="checkbox" NAME="<?php echo($users[$i]) ?>" VALUE="ON">

<?php } // End of if
?>

</FONT></TD>
<TD WIDTH="22%" ALIGN="center" HEIGHT="19">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL>

<?php // Code to ensure that name is displayed once
if ($new count==0)
{ echo ($users[$i]); }
?>

</SMALL></FONT></TD>


We also display the details of the item:

<TD WIDTH="15%" ALIGN="center" HEIGHT="19">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL>
<?php echo ($row["order no"]); ?></SMALL></FONT></TD>
<TD WIDTH="14%" ALIGN="center" HEIGHT="19">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL>
<?php echo ($row["item no"]); ?></SMALL></FONT></TD>
<TD WIDTH="15%" ALIGN="center" HEIGHT="19">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL>
<?php echo ($row["quantity"]); ?></SMALL></FONT></TD>
<TD WIDTH="17%" ALIGN="center" HEIGHT="19">
<FONT FACE="Sans Serif" COLOR="#804000"><SMALL>
<?php echo ($row["status"]); ?></SMALL></FONT></TD>
</TR>

<?php
$new count = 1;
} // End of while
} // End of for
?>

</TABLE>

Create a submit button with value Ship Order. The administrator clicks on this button, to change the
status of the transactions of the selected users, from Pending to Shipped.

<BR><INPUT TYPE="submit" NAME="ship order" VALUE=" Ship Order ">
</TABLE>

</CENTER></DIV>
</FORM>
</BODY>
</HTML>
























































Shipping the Order

The script ship_order.php is executed on the web server when the administrator clicks on the
Ship Order button in the transactions page. This script changes the status of the transactions of the
selected users from Pending to Shipped.

We begin by verifying that the administrator is already authenticated:

<?php
require 'functions.php';

if(!authenticateUser($cookie user, $cookie passwd)){
header("Location:http://$HTTP HOST/$DOCROOT/admin.htm");
exit();
}

// Connect to the Database
if (!($link = mysql pconnect($DB SERVER, $DB LOGIN, $DB PASSWORD))){
DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));
exit() ;
}

// Today’s Date
$today = date("Y-m-d");

Get the list of all the users:

// Get the list of all the users
if (!($result1 = mysql db query ($DB, "select * from user profile"))){
DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));

exit() ;
}

Get all the rows of the book_shop table that are stored in the variable $result3:

// Get all the information of the music and book shop
if (!($result3 = mysql db query ($DB, "select * from book shop "))){
DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));
exit() ;
}

Get all the rows of the music_shop table that are stored in the variable $result4:

if (!($result4 = mysql db query ($DB, "select * from music shop "))){
DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));
exit() ;
}
while (($row1 = mysql_fetch_array($result1))){

























































$user= $row1["user_id"] ;

For each user, verify that the status of the user’s transaction needs to be changed from Pending to
Shipped and get all of today’s transactions of the user. The form variable $$user will have a value
ON, if the administrator had selected the user’s transactions for shipping in the transactions page.

if(($$user) && ($$user == "ON")) {

// Get all the pending transactions of the user that needs to be shipped
if (!($result2 = mysql db query ($DB, "select * from transaction where
user id='$user' and date='$today'
and status='Pending'"))){
DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));
exit() ;

}

$amount=0 ;

Calculate the cost of all the items, purchased by the user:

while (($row2=mysql_fetch_array($result2))) {

Get the details of the item, which are then stored in the variable $row3:

mysql data seek($result3, 0) ;
mysql data seek($result4, 0) ;
while (($row3 = mysql fetch array($result3)) ||
($row3 = mysql fetch array($result4))){
if ($row3["item no"] == $row2["item no"])
break ;
}
if ($row3 == NULL){
DisplayErr("error in the tables\n") ;
exit() ;
}
$amount = $amount + $row3["price"] * $row2["quantity"];
}

Update the new balance and the account of the user:

// Update all the transactions of the user as shipped
if (!mysql db query($DB, "update transaction set status='Shipped'
where user id='$user' AND date='$today'")) {
DisplayErrMsg(sprintf("internal error %d:%s\n",

mysql errno(), mysql error()));
exit() ;
}

// Update the account of the user
if (!mysql db query($DB,"UPDATE user profile SET account balance =
account_balance-$amount where user_id='$user'")){

























































DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));
exit() ;
}

Free the memory associated with $result variable.

mysql free result($result2) ;
}
}

Finally, redirect the client browser to the transactions page.

header("Location:http://$HTTP HOST/$DOCROOT/transaction admin.php");

?>
Search for Users
The script search_user.php is executed on the web server when the administrator enters a search
keyword and clicks on the Search button on the Search for Users page. The script
search_user.php is called with the form variable keyword, containing the search text.

We begin, as always, by verifying that the administrator is already authenticated:

<?php
require 'functions.php';

if(!authenticateUser($cookie user, $cookie passwd)){
header("Location:http://$HTTP HOST/$DOCROOT/admin.htm");

exit();
}

// Connect to the Database
if (!($link = mysql pconnect($DB SERVER, $DB LOGIN, $DB PASSWORD))){
DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));
exit() ;
}

Search in the user_profile table for rows whose user_id column is like $keyword.

// Read records from table user profile to list matching users

if (!($result = mysql db query($DB, "select * from user profile where
user id like '%$keyword%'" ))){
DisplayErrMsg(sprintf("internal error %d:%s\n",
mysql errno(), mysql error()));
exit() ;
}
?>


























































<HTML>
<HEAD>
<TITLE>User Search Results !! </TITLE>
</HEAD>

<BODY BGCOLOR="#F0F3D1">

<DIV ALIGN="left">
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" WIDTH="90%">
<TR>
<TD WIDTH="50%" ALIGN="right">
<IMG SRC="wrox.gif" ALT="WroxWare" WIDTH="228" HEIGHT="70">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD>

<TD WIDTH="50%">
<IMG SRC="Shopping Mall.gif" ALT="Shopping Mall" WIDTH="318" HEIGHT="87">
</TD>
</TR>
</TABLE>
</DIV>

<DIV ALIGN="center"><CENTER>
<TABLE BORDER="0" CELLSPACING="1" WIDTH="100%" ALIGN="center">
<TR>
<TD WIDTH="25%" ALIGN="center"><A HREF="user admin.php">
<IMG SRC ="User records.gif" ALT="User Records" BORDER="0"></A>
</TD>
<TD WIDTH="25%" ALIGN="center"><A HREF="transaction admin.php">
<IMG SRC = "Transaction.gif" ALT="Today's Transactions" BORDER="0"></A>
</TD>
<TD WIDTH="25%" ALIGN="center"><A HREF="search user.htm">
<IMG SRC = "Search for user.gif" ALT="Search for user !" BORDER="0"></A>
</TD>
<TD WIDTH="25%" ALIGN="center"><A HREF="logout admin.php">
<IMG SRC = "Logout admin.gif" ALT="Logout !" BORDER="0"></A>
</TD>
</TR>
</TABLE>
</CENTER></DIV>
<CENTER>

Generate a form with action as view_transactions.php:

<FORM METHOD="get" ACTION="view transactions.php">


<?php
while (($row = mysql fetch array($result)))
{
?>

Display the list of users matching the search criteria.

<P><FONT FACE="Sans Serif" COLOR="#804000"><SMALL><B>
View transaction details for </B></SMALL></FONT></P>


























































Create a submit button with the name as userid, and value as the user_id of the user. The
administrator clicks on this button to view the transactions of the user.

<INPUT TYPE="submit" VALUE="<?php echo($row["user id"])?>" NAME="userid">
<BR><BR>

<?php
} /* while ends */
?>

</FORM>

</CENTER>
</BODY>
</HTML>
Summary
In this chapter we wrote a complete real-life Shopping Cart application, using PHP in the middle tier.
This illustrates how PHP can be effectively used in the middle tier, to write web-based applications.
We have covered only the HTML files containing PHP code embedded in them, though I would
encourage readers to download the complete code, and look at all the files. This will give them a better
understanding of the application.

To keep the application simple, we chose only bare minimal features for the application. Some features
that you may like to add to this application could include:


❑ A page for users to change the information (including password) entered by them during
registration.
❑ Using session identifiers for maintaining the session context of users.
❑ Client-side validation of the data entered by the user in the HTML form.
❑ Store quantities of items available in the database.
❑ Pages to allow the administrator to add new items or modify the quantities of items in the
database.
❑ Add business workflow logic in the application. One example of such workflow could be:
❑ sending e-mail to the Shipping Department, after the user has purchased items.
❑ sending e-mail to the Finance Department to deduct the amount from the user’s credit
card account, after the status of item is changed from Pending to Shipped by the
administrator.




























































Case Study 2: Phorum Discussion
Software
Phorum is a bulletin board application written in PHP. It allows users to post and read messages on the
Web, storing these messages in a relational database. The messages are organized according to their
conversation threads, as with some popular Usenet news readers. It was developed as an Open Source
project. Information about Phorum can be found at

Phorum, like a lot of software applications, was born of necessity. The authors' web site needed an area
where users could exchange ideas. At that time, there were a lot of Perl-based solutions that used files to
store all their data. The problem with this approach was that the rest of the site was using PHP and, this
area would also need to use PHP if it was to integrate well with the rest of the site. In addition, having
thousands of files on the server could be a headache. There were horror stories of operating systems
having difficulties with too many files in a single directory. Therefore, Phorum also set itself the goal of
storing its data on a SQL database server. This would allow retrieval of the data in all sorts of formats
and in any order with only minimal changes. Fortunately, PHP's database support is very good.
Integration with the rest of the site and good database support made PHP an easy choice for this project.

The next hurdle was to develop the project quickly. Once the initial groundwork was laid, other users
were invited to try it out, and in return they could use the code. Amazingly, this increased the speed of

production exponentially. Within one month, Phorum became a product that, we believed, was superior to
any of the Perl solutions available.

Soon, the licensing for Phorum was changed from informal sharing of code to that of the GNU General
Public License (GPL). This meant that anyone could use Phorum for their website. Phorum grew slowly
in popularity until version 3 was released, and there was a large increase in the number of users. Now
there are sites running Phorum that receive over 500 new messages a day. Many of these new users are
people who, like the Phorum originators, are finding the Perl/file-based solutions inadequate for this
discussion area of this size.

Phorum is currently used on some very high profile sites. A list of these sites is available at the Phorum
web site (). You can download the latest version of Phorum there, and also
view demos of the application and receive support for it.
Why PHP?
The reasons why PHP is so good for Phorum are the same reasons that it is good for most web-based
applications:

❑ Support for many different databases


















































































































❑ Ability to embed PHP within HTML pages
❑ Exceptional error handling features
Database Support
PHP has support for a wide range of databases. At the last count, PHP natively supported over 15
databases. In addition it supports several varieties of ODBC, or Open DataBase Connectivity (iODBC and
OpenLink ODBC, as well as custom and unified ODBC libraries). This makes it a natural choice if we are
planning to perform any kind of database operations on our website. Compare this with, for example,
Microsoft's Active Server Pages (ASP), which relies entirely on ODBC and/or OLE DB for database
access. For those not familiar with ODBC, this is how it works from a programmer's prospective: a
programmer writes code in a given language (in our case, PHP). That code uses a standard set of
functions – the ODBC functions for that language. These functions internally use code (usually written by
the database manufacturer) that implements the standard set of ODBC functions specified in the ODBC
API. ODBC uses a Driver Manager to manage drivers for the available data sources; these drivers process
the function calls from the ODBC API and translate standard SQL syntax into the native SQL syntax of
the database. A graphical representation of this architecture would look like this:

***Insert figure (2025_03_06.cdr?)

With PHP supporting so many databases natively, we can avoid the whole of the ODBC level entirely.
When writing code for a database that PHP supports natively, we are in effect using the functions
supplied by the manufacturer for use with that database.


As you can see, there is a whole step removed from the equation there. By removing that extra level,
native support allows faster database access and is able to utilize additional features specifically related
to the actual database server being used. This is because ODBC has a standard interface, which must be
followed when working with the database. By supporting each database independently, PHP can
communicate with the database on its own terms. This allows us to use features in a database system that
may not be available through the ODBC API.

The downside of this is that applications written for a particular database cannot be easily ported to other
database systems. Phorum tackles this issue by implementing a loose abstraction layer around the most
popular database systems in use with PHP. The difference between this and ODBC is that in our
abstraction layer we have made concessions to allow the code to use the special features of each database
system. This abstraction layer will be discussed later.
PHP Embedded in HTML
The second advantage of PHP is the ability to embed the PHP code right inside HTML files. This makes
it easy to write both the PHP and the HTML. This ability to embed PHP code within an HTML page
means that we don't have to deal with long print statements to output HTML; in fact, we can
completely separate our PHP code from PHP. New web developers and those who just want scripts on
their sites may find Perl's pure programming style a bit confusing and intimidating. With PHP, large
blocks of HTML can be separated from the PHP and this is likely to be much more comprehensible to the
novice coder.

Suppose we had a program that would allow users to input color names and then print an HTML table
containing the hexadecimal values for those colors for use in HTML. This table will have two columns –
one each for the color name and the hex value – with a row for each color. The differences in an


















































































































embedded language like PHP and non-embedded languages (like regular Perl) could make all the
difference in the world to an inexperienced programmer. The HTML code could look like this:

<TABLE CELLSPACING="2" CELLPADDING="2" BORDER="0">
<TR>
<TD>Color</TD>
<TD>Hex Value</TD>
<TR>
<TR>
<TD>Red</TD>
<TD>#FF0000</TD>
</TR>
<TR>
<TD>Blue</TD>
<TD>#0000FF</TD>
</TR>

<TR>
<TD>Green</TD>
<TD>#00FF00</TD>
</TR>
</TABLE>

With Perl, we would first have to parse any information passed to the script from the web server. Next the
code would look something like this:

print "<TABLE cellspacing=\"2\" cellpadding=\"2\" border=\"0\">\n";
print " <TR>\n";
print " <TD>Color</TD>\n";
print " <TD>Hex Value</TD>\n";
print " </TR>\n";
print " <TR>\n";
print " <TD>$color name 1</TD>\n";
print " <TD>$color val 1</TD>\n";
print " </TR>\n";
print " <TR>\n";
print " <TD>$color name 2</TD>\n";
print " <TD>$color val 2</TD>\n";
print " </TR>\n";
print " <TR>\n";
print " <TD>$color name 3</TD>\n";
print " <TD>$color val 3</TD>\n";
print " </TR>\n";
print "</TABLE>\n";

Not very readable, is it? While we can improve it slightly (using a 'here document' to print out long
sections of pure HTML), we will still need print statements for the lines containing variables. The

ironic part is that very similar code would work also in PHP. However, the following code is much easier
on the eye:

<TABLE cellspacing="2" cellpadding="2" border="0">
<TR>
<TD>Color</TD>
<TD>Hex Value</TD>

















































































































</TR>
<TR>
<TD><?php echo $color name 1; ?></TD>

<TD><?php echo $color val 1; ?></TD>
</TR>
<TR>
<TD><?php echo $color name 2; ?></TD>
<TD><?php echo $color val 2; ?></TD>
</TR>
<TR>
<TD><?php echo $color name 3; ?></TD>
<TD><?php echo $color val 3; ?></TD>
</TR>
</TABLE>

As you can see, the PHP only comes into play when we need it. Moreover, with PHP, the variable values
are already available when the script starts: we don't need to parse the form input to abstract them.
Exceptional Error Handling
Most people agree that PHP has some of the most helpful error messages in web application building
today. PHP gives advanced error messages that help the developer find the problem quickly. A typical
PHP error message looks like this:

Fatal error: Call to unsupported or undefined function foo() in /usr/web/bar.php
on line 8

This error message tells us the name of the function called, the filename of the script in which the error
occurred (if it were in an included file, it would give the included file's name), and the line on which the
error occurred. In this case, a function called foo() was called on line 8 of /usr/web/bar.php and
that the function is not defined. With so much information, a developer can quickly and easily track down
the problem. PHP error messages and debugging are discussed in more detail in Chapter 19.
How Phorum Works
As we said at the start of the chapter, Phorum is an application that allows users of a web site to post
messages to a bulletin board and to read and search through earlier postings. Messages are stored in a

relational database (using wrapper functions to abstract database-specific functions, so that if the database
is upgraded, only this abstraction layer needs to be changed – we don't need to go through the entire code,
changing every single database function). The messages are organized according to conversation threads
– that is, when a user replies to a message, the reply is added to the same thread as the original posting.
We will first look at the graphical interface presented to the user, before looking at some of the code
behind the application.
Interface Overview
The Phorum user interface consists of five pages. The first page which users see is the forum list page.
This file is named index.php3 by default and lists the name and a description of each forum as well as
the total number of posts in the forum and the date of the most recent post:




















































































































****Insert 2963_24_01.bmp

From here, users can enter a forum by clicking on its name. This will take them to the message list page,
which by default is called list.php3. This page shows a list of the messages in this forum. This may
contain all the messages, or just the first messages of each conversation. The default setting for this is
decided by the administrator and can be different for each forum. Users can select a link that will allow
them to specify their own preference for this setting. The setting for each user is stored in a cookie, which
is accessed each time they return to the forum.






















































































































Next, a user may select to read a message by clicking on a topic of interest. This leads to a page where
individual messages may be read, named read.php3 by default. This page displays the subject, the
author's name (which is an email link if an email address was provided), and, of course, the body of their
message. Below this is the full list of all the messages in the conversation. The user can click on any of
these and be taken to that message. In all cases, the entire conversation thread is displayed, as opposed to
some other systems, which only show the messages from the current one down. Below the thread list is a
form that can be used to add a message to this thread. The message would appear in the list below the
message that is currently being viewed.






















































































































Users can start new threads by clicking on a link that takes them to a page which by default is named
post.php3. On this page is a form similar to the one on the read.php3 page. There are input boxes
where users can type their name and email address and the subject and body of the message they want to
post. There is also a checkbox which allows users to specify that any responses to their messages should
be sent to the specified email address:






















































































































If all is well, the user will be sent back to the message list after clicking on the Post button. If an error
occurred, the post form will be shown again with a message explaining the error. All of the information
entered will be preserved.

The final page which users can access is the search page (search.php3). This page illustrates another
advantage of using a database. Searches can be done quickly and can be quite complex. However, the
search facility built into Phorum is quite basic: it does not attempt to determine a rating or order
messages according to their relevance to the search criterion. A user enters some words and a list of the
subjects of messages containing those words is returned, sorted by date in reverse order. A user can read a
message by clicking on its subject. There is also a short summary of the message body listed on the page:





















































































































Getting Inside the Code
Now that we've seen what Phorum does, we can have a look under the hood, and see some of the code it
uses to achieve this. We will start by looking at the general design of the code, and move on to looking at
the code for specific pages.
Reusing Code
There are many more files in Phorum than the five pages we have already seen. One of the reasons for
this is that we make use of PHP's facility for reusing code. PHP makes it easy to use sections of code in
several different applications, or in a number of files within an application. We can include code or
HTML in a PHP document by using an include or require statement. The difference between the
two is important.

The include statement is conditional – the file is read each time the include statement is
encountered. That is, if the line with the include statement is not reached, the file will not be included.
However, if the same file is included twice, PHP will read and parse it twice. Required files are read in
and only parsed once. This means that if we have multiple require statements, PHP will only have to
read and parse the file once, which is faster. This is good if there are functions being declared in files: if a
file with functions in it is included twice an error will occur; if it is required, no error will occur. The
require and include statements are discussed in more detail in Chapter 6.



















































































































Phorum reuses two sections of the user interface code. One is the message list, which is used on the
message list page and again on the read page to show the entire thread list for that thread. This is done by
retrieving a set of rows from the database and then requiring the file threads.inc. The file
threads.inc contains all the HTML code for the table that displays the message list.

The other code that is reused in the interface is the form used to post messages. It is seen on the "new
message" page as well as the "read messages" page for replying to a thread. There are slight differences
in the presentation of the forms. For one, the subject is pre-filled on the "read" page. This allows the
users to skip that step in filling out the form. Also, there is a button on the "read" page that can be used to
quote the text from the message that is being read in the reply field. This is similar to the way many email
clients include text from messages to which the user is replying.

Reusing code in this way allowed us to cut down development time significantly on those pages. There is

other code that is required in all the files. The file common.inc is included in every page that Phorum
uses. This file contains a number of function definitions and also require statements for the other files
needed for Phorum to operate, such as the forum's information file, forums.inf, that stores all the
information about the different forums as well as the database connection information.
Database Abstraction Layer
After saying that one of the strong points of PHP is its ability to natively talk to many databases, it may
seem strange that Phorum would try to undo that by creating a layer of abstraction between itself and the
databases. However, a database abstraction layer allows us to write generic database-access code,
changing only the code in the abstraction layer if we upgrade to a more powerful database.

The database abstraction class was originally adopted from Muze (www.muze.nl). However, it has been
modified greatly; the code works as follows: there is a standard class interface that the abstraction layers
adhere to. There are two classes, a db class and a query class. The db class handles connections to the
database and other database-related transactions. The query class executes queries and returns results.
Exceptions are made in the Phorum code for some databases. The db class has a type property. We can
check this property to see whether an exception needs to be handled. For example, PostgreSQL version
6.4 and lower does not support the LIMIT clause in SQL statements, so a SQL statement has to be sent to
the database before a query is executed which limits the number of rows returned from the server:

if ($DB->type == "postgresql") {
$limit = "";
$q->query($DB, "SET QUERY_LIMIT TO '$nDisplay'");
} else {
$limit=" LIMIT $nDisplay";
}

This code checks to see whether the database server is PostgreSQL, and if so takes the appropriate
actions. Otherwise it defines the LIMIT clause to be used in the subsequent query. This gives Phorum the
flexibility to use any special features of the different databases where appropriate.
Generating PHP Code on the Fly

One of the nice things about using a parsed language instead of a compiled language is the ability to
generate code to be run later. Phorum stores its settings by generating a file called forums.inf. This
file contains PHP code that is itself created by Phorum's own PHP code; this approach makes it easier to

















































































































read the settings. The other option would be to write a file in our own custom format and then to write
code that can read that format. PHP already has a parser and it can generate variable values for us
automatically simply by including a file within the Phorum application.

Writing a file that contains PHP code is in essence quite simple. The following code will create a second
file called hello.php that writes 'Hello World' out to the browser:


<?php
$data ="<?php\n";
$data.=" echo \"Hello World\";\n";
$data.="?>";
$fp=fopen("hello.php", "w");
fputs($fp, $data);
fclose($fp);
?>

This creates a file that contains the code:

<?php
echo "Hello World";
?>

This file can then be run just as any PHP file that we have created ourselves. There are several things to
point out about the code above. The first is to note that that all of the text was placed into a variable and
then written to the file with one call to the function fputs(). This is because, while each call to
fputs() is guaranteed to be atomic (this means that all the data is written without interruption from
another process), separate calls to a file by a process are not guaranteed to happen consecutively. It might
not matter in the above code, but consider the following scenario.

Assume we have a PHP page that has a form where users can enter their first name in one field and their
last name in another. When that information is sent back to the server, it is written to a file. The code for
writing the data to the file might look like this:

$fp=fopen("names.txt ", "w");
fputs($fp, "$lastname, ");
fputs($fp, "$firstname\n");
fclose($fp);


If User Number 1 enters 'Bob' as his first name and 'Jones' as his last name, and User Number 2 enters
'Tom' as the first name and 'Doe' as last name, there is a chance the data could be mixed up. If the users
submit the pages at the same time, the requests may be processed as follows:

1. Request from User 1 received.
2. Request from User 2 request received.
3. User 1's last name written to file.
4. User 2's last name written to file.
5. User 1's first name written to file.
6. User 2's first name written to file.



















































































































This results in our file, names.txt, containing the following data:

Jones, Doe, Bob
Tom

However, if this following code is used, the file will be written as expected:

$fp=fopen("names.txt ", "w");
fputs($fp, "$lastname, $firstname\n");
fclose($fp);

The names.txt file will now contain the correct data:

Jones, Bob
Doe, Tom

This is a single call to fputs() and will be completed before another function can write to the file. The
values could also have been placed in a variable as in the example above.
Storing User Data in Cookies
Phorum uses several cookies to store users' information and preferences. This is handy because it allows
us to personalize our pages for each user.
New Messages
The first thing Phorum uses cookies for is determining whether to display a label on each message
indicating that it is a new message. Two cookies are involved here. The first is a cookie that holds the ID
of the latest message the user has read in this forum. The following code is used to set this cookie:

SetCookie("phorum-new-$TableName", $id, time() + 31536000);


The variable $TableName is used in the cookie to allow for storing settings for each forum. This
variable contains just what we would imagine – the name of the table in the database where the data for
this forum is stored.

The second is a session cookie – that is a cookie that dies when that browser is closed, that keeps up with
which messages are read during that visit to the forum. The IDs of the messages that the user has read
during that session are placed in an array. That array is then passed through the serialize() function
which prepares the variable to be stored without losing its structure or type. That value is then passed into
the SetCookie() call:

$haveread[$id] = 1;
SetCookie("phorum-haveread-$TableName", serialize($haveread), 0);

It should be noted here that to read the values in the $haveread array, the cookie must be passed
through the unserialize() function:

$haveread_cookie = "phorum-haveread-$TableName";


















































































































$haveread = unserialize($$haveread_cookie);

By using these two cookies together, Phorum allows users to see messages which have arrived since the
last time they visited the forum.
User Information
The next cookies store the name and email address which a user enters when posting a message. The next
time that user goes to post a message, the correct name and email address are already loaded in the
browser. These cookies are persistent, by default expiring after one year:

$name cookie = "phorum name $TableName";
$email cookie = "phorum email $TableName";

if((!IsSet($$name cookie)) || ($$name cookie != $author)) {
SetCookie("phorum name $TableName", $author, time() + 31536000);
// 365 days = 31536000 seconds
}
if((!IsSet($$email cookie)) || ($$email cookie != $email)) {
SetCookie("phorum email $TableName", $email, time() + 31536000);
}

The $author and $email variables are set in the form which users fill out when they post a message.
Before setting the cookie, Phorum checks that the cookie is not already set to the value supplied by the
user.


The last cookie Phorum uses is one that stores a user setting for Phorum. The default view of a message
list in Phorum is to show all messages in a conversation. That means that each message list will contain
the original message in the conversation and all subsequent replies. Users can select any one of these to
read from that list. However, to allow users to scan through the various conversations more quickly, there
is an option to collapse the conversation tree views so that only the original message is shown. Users may
do this by selecting one of two links (situated at the top and bottom of the message list). Once the
Collapse option has been selected, a cookie is set on the user's machine to save this setting. From that
point, on the user will see the collapsed view when visiting the site. Here is the code for setting this
value:

$phcollapse="phorum-collapse-$TableName";
if (IsSet($collapse)) {
$$phcollapse=$collapse;
SetCookie("phorum-collapse-$TableName", $collapse, time() + 31536000);
} elseif (!isset($$phcollapse)) {
$$phcollapse=$Collapsed;
}

The variable $collapase is passed in the URL specified in the link the user clicks to activate the
collapsed view. Phorum checks that this variable is set and if so sets the cookie as well as
$$phcollapse which is used later as the switch for using the collapsed view. If $collapse is not
set, $$phcollapse is set to $Collapsed which holds the default setting for this forum.

To summarize the cookies used in the Phorum application:



















































































































×