The script code of transaction_admin.php looks like this: // Connect to the Database if !$link = mysql pconnect$DB SERVER, $DB LOGIN, $DB PASSWORD{ We then get the list of all the users
Trang 1} // 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))){
Trang 2<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>
<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
/* 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"]);
Trang 3Get 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
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>
<?php echo($row["order no"]);?></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL>
<?php echo($row["item no"]); ?></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL>
<?php echo($row["quantity"]); ?></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL>
<?php echo($row["date"]); ?></SMALL></FONT></TD>
<TD WIDTH="20%"><FONT COLOR="#804000" FACE="Sans Serif"><SMALL>
<?php echo($row["status"]); ?></SMALL></FONT></TD>
</TR>
<?php
} // End of while
Trang 4Transactions 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:
// Connect to the Database
if (!($link = mysql pconnect($DB SERVER, $DB LOGIN, $DB PASSWORD))){
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" ))){
Trang 5<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>
Trang 6<TABLE BORDER="0" CELLPADDING="3" CELLSPACING="0" WIDTH="95%">
<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' "))){
<TR>
<TD WIDTH="17%" ALIGN="center" HEIGHT="19"><FONT FACE="Sans Serif">
<?php // Code to show Status check box once
if ($new_count==0){
Trang 7<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>
Trang 8Shipping 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:
// Connect to the Database
if (!($link = mysql pconnect($DB SERVER, $DB LOGIN, $DB PASSWORD))){
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 "))){
Trang 9$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'
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"])
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'")){
Trang 10DisplayErrMsg(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:
// 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%'" ))){
Trang 11<IMG SRC="Shopping Mall.gif" ALT="Shopping Mall" WIDTH="318" HEIGHT="87"> </TD>
<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>
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>
Trang 12Create 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">
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
Trang 13Case 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 http://phorum.org/
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 ( http://www.phorum.org ) You can download the latest version of Phorum there, and also view demos of the application and receive support for it
Trang 14❑ 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
Trang 15embedded 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">
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
Trang 16</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:
Trang 17****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
Trang 18Next, 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
Trang 19Users 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:
Trang 20If 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:
Trang 21Getting 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
Trang 22Phorum 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:
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
Trang 23read 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:
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:
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
Trang 24This results in our file, names.txt , containing the following data:
Jones, Doe, Bob
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:
Trang 25$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:
Trang 26Cookie Description
phorum-new-$TableName The ID of the latest message read by the user
phorum-haveread-$TableName An array of the IDs of the messages read by the user in the
current session
phorum-collapse-$TableName Whether the conversation trees are to be collapsed
The Heart of it All: forums.inf
This file controls all the settings for Phorum As we said earlier, this file is generated on the fly All of the default settings, as well as the unique settings for each forum, are contained in this file There is one value that is passed around all pages in Phorum with the exception of the forum list page: the parameter
$num The value of this determines which forum is displayed on the other pages The code in
forums.inf assigns values from the array containing information for the forums to variables for use in the other files This is to ease both the writing and reading of code in the rest of the script A typical
forums.inf file might look like this:
$DB->open($dbName, $dbServer, $dbUser, $dbPass);
$q = new query($DB); //dummy query for generic operations
$forum page = 'index';
$list page = 'list';
$search page = 'search';
$read page = 'read';
$post page = 'post';
$violation page = 'violation';
$down page = 'down';
$default lang = 'english.lang';
$default_table_width = '100%';
Trang 27$default table header color = '#000080';
$default table header font color = '#FFFFFF';
$default table body color 1 = '#FFFFCC';
$default table body font color 1 = '#000000';
$default table body color 2 = '#CC0000';
$default table body font color 2 = '#000000';
$default nav color = '#FFFFFF';
$default nav font color = '#0000FF';
// forum information
// Phorum Admin forum
$forums['2']['id'] = 2;
$forums['2']['active'] = 1;
$forums['2']['name'] = 'Phorum Admin';
$forums['2']['table'] = 'dev Xfh8Uit admin';
$forums['2']['table header color'] = '#000080';
$forums['2']['table header font color'] = '#FFFFFF';
$forums['2']['table body color 1'] = '#FFFFCC';
$forums['2']['table body font color 1'] = '#000000';
$forums['2']['table body color 2'] = '#0000CC';
$forums['2']['table body font color 2'] = '#000000';
$forums['2']['nav color'] = '#FFFFFF';
$forums['2']['nav font color'] = '#0000FF';
// Phorum Features and Bugs forum
$forums['1']['id'] = 1;
$forums['1']['active'] = 1;
$forums['1']['name'] = 'Phorum Features and Bugs';
$forums['1']['table'] = 'dev Xfh8Uit phorum';
$forums['1']['table header color'] = '#000080';
$forums['1']['table header font color'] = '#FFFFFF';
$forums['1']['table body color 1'] = '#FFFFCC';
$forums['1']['table_body_font_color_1'] = '#000000';
Trang 28$forums['1']['table body color 2'] = '#0000CC';
$forums['1']['table body font color 2'] = '#000000';
$forums['1']['nav color'] = '#FFFFFF';
$forums['1']['nav font color'] = '#0000FF';
if (is array($forums) && $num>0) {
$ForumName = $forums["$num"]["name"];
$TableName = $forums["$num"]["table"];
$BodiesTable = $TableName." bodies";
$Mod = $forums["$num"]["mod"];
$ModPass = $forums["$num"]["mod pass"];
$EmailModerator = $forums["$num"]["email mod"];
$TableWidth = $forums["$num"]["table width"];
$TableHeaderColor = $forums["$num"]["table header color"];
$TableHeaderFontColor = $forums["$num"]["table header font color"];
$TableBodyColor1 = $forums["$num"]["table body color 1"];
$TableBodyFontColor1 = $forums["$num"]["table body font color 1"];
$TableBodyColor2 = $forums["$num"]["table body color 2"];
$TableBodyFontColor2 = $forums["$num"]["table body font color 2"];
$NavColor = $forums["$num"]["nav color"];
$NavFontColor = $forums["$num"]["nav font color"];
we print the table header:
<TABLE WIDTH="<?php echo $default table width; ?>" CELLSPACING="0"
CELLPADDING="2" BORDER="0">
<TR>
<TD WIDTH="100%" COLSPAN=2
<?php echo bgcolor($default table header color); ?>>
<FONT COLOR="<?PHP echo $default table header font color; ?>">
<?php echo $lAvailableForums;?>
Trang 29</FONT>
</TD>
</TR>
First we set a variable $empty with the value true We will check this variable later to give a message
if there are no active forums Next, we check to see whether the $forums array exists If so, we loop through each available forum:
Then we check for the date of the latest message:
$sSQL="SELECT max(datestamp) AS max date FROM $forum[table]"; $q->query($DB, $sSQL);
$last post date=$q->field("max date", 0);
if($last post date==0){
$last post date=date format("0000-00-00");
echo "<TR BGCOLOR=\"$default table body color 1\">
Trang 30COLOR=\"$default table body font color 1\">
echo "<DD><FONT SIZE=-1
COLOR=\"$default table body font color 1\">";
SELECT thread FROM $TableName WHERE thread > $cutoff_thread
ORDER BY thread DESC LIMIT 20
$TableName is the variable that holds the table name for this forum; $cutoff_thread is the thread number of the last message on the previous page If this is the front page, a query will have been run before this one to retrieve the maximum thread in the table
The high and low thread values from this list are then retrieved from the results and placed in the variables $max and $min Individual threads cannot be selected in the query as we are looking for threads which cover a given number of messages Also, by limiting the number of rows returned, we would risk failing to return all the messages in the last thread This is the reason for the next query We feed the maximum and minimum thread numbers into the following query:
SELECT id,parent,thread,subject,author,datestamp FROM $TableName
WHERE thread<=$max AND thread>=$min
ORDER BY thread DESC, id asc
This returns the fields that we will use for displaying the messages Depending on what type of display
Trang 31the administrator has set up, two different code modules will be used to display the messages If the messages are to be displayed in two-tiered format, threads.inc is included However, if they are to be shown in a multi-tiered format, multi-threads.inc is included Both do the same thing – they cycle through the messages and output the HTML to display the messages The only difference is that the multi-tiered format requires some preprocessing to order the threads correctly
But first we perform the cookie handling, checking for the last message read by the user and checking the user's preference for a collapsed or non-collapsed view:
Next, we check whether the database is PostgreSQL, and if so, we know we need to avoid using a LIMIT
clause in our SQL queries:
Trang 32Now we build the database queries which we looked at above The query will depend on whether we are
on the front page (in which case we will first need to get the maximum thread in the table), and whether the user has selected collapsed view (if not, we also need to get the count field from the database):
Trang 33$q->query($DB, "SET QUERY LIMIT TO '0'");
}
$rows = $thread list->numrows();
if($rows==0 && $action!=0){
Header("Location: $list page.$ext?num=$num$GetVars");
}
echo "<! $sSQL >\n";
$msg_list = new query($DB, $sSQL);
Next we build the <A> tag which the user can use to select a collapsed or non-collapsed view, and use this when we build the navigation bar links (at the top and bottom of the table):
if ($$phcollapse==0) {
$collapse link = "<A HREF=\"$list page.$ext?num=$num&collapse=1$GetVars\"> <FONT COLOR='$NavFontColor'>" $lCollapseThreads "</FONT></A>";
$nav = "<DIV CLASS=nav><FONT COLOR='$NavFontColor'>
<A HREF=\"$forum page.$ext?$GetVars\">
<FONT COLOR='$NavFontColor'>" $lForumList
"</FONT></A> |
<A HREF=\"$list page.$ext?num=$num$GetVars\">
<FONT COLOR='$NavFontColor'>" $lGoToTop
"</FONT></A> |
<A HREF=\"$post page.$ext?num=$num$GetVars\">
<FONT COLOR='$NavFontColor'>" $lStartTopic
$nav = "<DIV CLASS=nav><FONT COLOR='$NavFontColor'>
<A HREF=\"$list page.$ext?num=$num$GetVars\">
<FONT COLOR='$NavFontColor'>" $lGoToTop
"</FONT></A> |
<A HREF=\"$post page.$ext?num=$num$GetVars\">
<FONT COLOR='$NavFontColor'>" $lStartTopic
"</FONT></A> | $collapse_link |
Trang 34 <a href=\"$search page.$ext?num=$num$GetVars\">
<FONT COLOR='$NavFontColor'>" $lSearch
"</FONT></A> </FONT></DIV>";
}
?>
Finally, we build the table itself:
<TABLE WIDTH="<?php echo $TableWidth; ?>" CELLSPACING="0" CELLPADDING="3"
BORDER="0">
<TR>
<TD WIDTH="100%" ALIGN="RIGHT" VALIGN="BOTTOM" NOWRAP
<?php echo bgcolor($NavColor); ?>><?php echo $nav; ?></TD>
<FONT COLOR='<?php echo $NavFontColor; ?>'>
<A HREF="<?php echo "$list page.$ext"; ?>?num=<?php echo $num; ?> &thread=<?php echo $max; ?>&action=1&<?php echo $GetVars; ?>"> <FONT COLOR='<?php echo $NavFontColor; ?>'>
<?php echo $lNewerMessages;?></FONT>
</A> |
<A HREF="<?php echo "$list page.$ext"; ?>?num=<?php echo $num; ?> &thread=<?php echo $min; ?>&action=-1
&<?php echo $GetVars; ?>">
<FONT COLOR='<?php echo $NavFontColor; ?>'>
Trang 35SELECT * FROM $TableName WHERE thread=$thread ORDER BY id
This returns a list of the messages in thread number $thread With this the current message's author, subject, email address, host, and datestamp can be retrieved and the entire list can be displayed later by including one of the thread modules discussed in the message listing section
The other query necessary to get a complete message is:
SELECT * FROM $BodiesTable WHERE id=$id
This retrieves the body of the message that was requested The message bodies are stored in a separate table to help speed up the message listing queries
The values are all displayed in HTML and then the page is wrapped up with a form for replying to the current message This portion of the page is from form.inc , which contains the HTML that is used to create a new topic or reply to a topic It is also used on the post page
This page is very long, so we won't give the code for it, although you can look at it for yourself by downloading Phorum from http://www.phorum.org
Posting A Message
The default action of the post page is to display the form for the user to fill out This form is also shown
on the page where messages are read In both cases, the results are sent to the post page
If the post page is requested and sent information, we can begin the process of actually posting the message We first check that the required fields have been provided If not, we display the form and an error message If all the information is there, we can continue Next, the author, email address and the user's IP address are checked against a list of banned authors, email addresses and IP addresses These lists are kept in three files named bad_names.inc , bad_emails.inc and bad_hosts.inc If this
is a user that has been banned from the forum, an email is sent to the moderator and the user is sent to a page explaining that their post was disallowed and to contact the moderator for more information
The next step is to check for any words that the admin has chosen to censor out This is also kept in a file called censor.inc If a word that is in censor.inc shows up in the post, it is replaced with " @!#$ " The post is still allowed
The next few lines prepare the post for insertion into the database One step is to strip out any
unnecessary quotation marks that may cause problems for PHP or the database Once the data is ready, the insertion can begin
First, the next available ID is retrieved from the database sequence for this forum This id will be used as the ID for the message that is about to be inserted Then the message is inserted into the table that stores the message bodies It is inserted here first so that if any errors occur here, the message will not be in the main table As long as the message is not in the main table, it can not be reached via the Phorum
interface If it were done the other way round, disembodied messages could appear on the message list – messages without a message body in the message bodies table You can see what confusion that would cause
Next, the message is inserted into the message list table and the user is sent back to the message list If
Trang 36the posted message was a new message, the user will be sent back to the top of the message list If the message was a reply, the user is sent back to the message list at the point where the message they were replying to appears
First we get the host, and check whether it occurs on the banned list:
Next we check for bad names and email addresses:
require "./bad names.inc";
Trang 37Next we validate the email address and check that all the fields have been completed in the post form:
if(!eregi(".+@.+\\ +", $email) && $email!=$Password && $email!=$ModPass){ $email="";
$nav = "<DIV CLASS=nav><FONT COLOR=\"$NavFontColor\">
<A HREF=\"$forum page.$ext?$GetVars\">
<FONT COLOR=\"$NavFontColor\">" $lForumList "</FONT></A> |
<A HREF=\"$list page.$ext?num=$num$GetVars\">
<FONT COLOR=\"$NavFontColor\">" $lGoToTop "</FONT></A>
|
<A HREF=\"$search page.$ext?num=$num$GetVars\">
<FONT COLOR=\"$NavFontColor\">" $lSearch "</FONT></A>
</FONT></DIV>";
} else {
$nav = "<DIV CLASS=nav><FONT COLOR=\"$NavFontColor\">
<A HREF=\"$list page.$ext?num=$num$GetVars\">
<FONT COLOR=\"$NavFontColor\">" $lGoToTop "</FONT></A>
|
<A HREF=\"$search page.$ext?num=$num$GetVars\">
<FONT COLOR=\"$NavFontColor\">" $lSearch "</FONT></A>
if(file exists("censor $TableName.inc")){
include "./censor $TableName.inc";
} else {
include "./censor.inc";
Trang 38while (eregi("([^a-zA-Z])($sWord)([^a-zA-Z])", $author)) {
$author = eregi replace("([^a-zA-Z])($sWord)([^a-zA-Z])",
$email = eregi replace("([^a-zA-Z])($sWord)([^a-zA-Z])",
$body = eregi replace("([^a-zA-Z])($sWord)([^a-zA-Z])",
Trang 39they have different values to the user's input:
$author = str replace("'", "\\'", $author);
$email = str replace("'", "\\'", $email);
$subject = str replace("'", "\\'", $subject);
$body = str replace("'", "\\'", $body);
$datestamp = date("Y-m-d H:i:s");
$author = htmlspecialchars($author);
$email = htmlspecialchars($email);
$subject = htmlspecialchars($subject);
If the message was posted by the moderator, we place the author and subject in bold and the body in
<HTML> </HTML> tags Otherwise we strip off the <HTML> tags using the eregi_replace()
function, replacing them if the forum is set to allow HTML messages:
if (($email==$ModPass && $ModPass!="") || ($email==$Password &&
Trang 40VALUES ('$id', '$author', '$email', '$datestamp', '$subject',
'$host', '$thread', '$parent', '$email reply')";