Tuesday, August 11, 2009

Building a Members Only Web Site

any Web sites require users to log in. Sometimes users can’t view any
Web pages without entering a password, while sometimes just part of the Web page requires a login. Here are some reasons why you might want to require a user login:

The information is secret. You don’t want anyone except a few autho­ rized people to see the information. Or perhaps only your own employ­ ees should see the information.

The information or service is for sale. The information or service that your Web site provides is your product, and you want to charge people for it. For instance, you might have a corner on some survey data that researchers are willing to pay for. For example, AAA Automobile Club offers some of its information for free, but you have to be a member to see its hotel ratings.

You can provide better service. If you know who your customers are or have some of their information, you can make their interaction with your Web site easier. For instance, if you have an account with Barnes and Noble.com or the Gap and log into their site, they use your stored ship­ ping address, and you don’t have to type it in again.

You can find out more about your customers. Marketing would like to know who is looking at your Web site. A list of customers with addresses and phone numbers and perhaps some likes and dislikes is a useful thing. If your Web site offers some attractive features, customers may be will­ ing to provide some information in order to access your site. For instance, a person might be willing to answer some questions in order to down­ load some free software or to play a great online game.

Typically, a login requires the user to enter a user ID and a password. Often, users can create their own accounts on the Web site, choosing their own user ID and password. Sometimes users can maintain their accounts — for exam­ ple, change their password or phone number — online.

In Chapter 11, you find out how to build an online catalog for your Pet Store Web site. Now, you want to add a section to your Web site that’s for Members Only. You plan to offer special discounts, a newsletter, a database of pet infor­ mation, and more in the Members Only section. You hope that customers will see the section as so valuable that they’ll be willing to provide their addresses and phone numbers to get a member account that lets them use the services in the restricted section. In this chapter, you build a login section for the Pet Store.

Designing the Application

The first step in design is to decide what the application should do. Its basic function is to gather customer information and store it in a database. It offers customers access to valuable information and services to motivate them to provide information for the database. Because state secrets or credit card numbers aren’t at risk, you should make it as easy as possible for customers to set up and access their accounts.

The application that provides access to the Members Only section of the Pet
Store should do the following:

Provide a means for customers to set up their own accounts with member IDs and passwords. This includes collecting the information from the customer that’s required to become a member.

Provide a page where customers type their member ID and password and then check whether they are valid. If so, the customer enters the Members Only section. If not, the customer can try another login.

Show the pages in the Members Only section to anyone who is logged in.

Refuse to show the pages in the Members Only section to anyone who is not logged in.

Keep track of member logins. You want to know who logs in and how often.

Building the Database

The database is the core and purpose of this application. It holds the cus­ tomer information that’s the goal of the Members Only section. It also holds the Member ID and password so that the user can log into the Members Only section.

The Members Only application database contains two tables:

Member table

Login table

The first step in building the login application is to build the database. It’s pretty much impossible to write programs without a working database to test the programs on. First design your database; then build it; then add some sample data for use while developing the programs.

Some changes have been made to the database design that I develop in Chapter 3 for the Members Only restricted section of the Pet Store Web site. Development and testing often result in changes. Perhaps you find that you didn’t take some factors into consideration in your design or that certain ele­ ments of your design don’t work with real-world data or are difficult to pro­ gram. It’s perfectly normal for the design to evolve while you work on your application. Just be sure to change your documentation when your design changes.

Building the Member table

In your design for the login application, the main table is the Member table.
It holds all the information entered by the customer, including the customer’s personal information (name, address, phone number, and so on) and the Member ID and password. The following SQL query creates the Member table:

CREATE TABLE Member (
loginName VARCHAR(20) NOT NULL, createDate DATE NOT NULL, password VARCHAR(255) NOT NULL, lastName VARCHAR(50),
firstName VARCHAR(40), street VARCHAR(50), city VARCHAR(50), state CHAR(2),
zip CHAR(10), email VARCHAR(50), phone CHAR(15),
fax CHAR(15), PRIMARY KEY(loginName) );

Each row represents a member. The columns are

loginName: A Member ID for the member to use when logging in. The customer chooses and types in the login name. The CREATE query defines the loginName in the following ways:

• CHAR(20): This data type defines the field as a character string that’s 20 characters long. The field will always take up 20 charac­ ters of storage, with padding if the actual string stored is less than
20 characters. If a string longer than 20 characters is stored, any characters after 20 are dropped.

• PRIMARY KEY(loginName): The primary key identifies the row and must be unique. MySQL will not allow two rows to be entered with the same loginName.

• NOT NULL: This definition means that this field can’t be empty. It must have a value. The primary key must always be set to NOT NULL.

createDate: The date when the row was added to the database — that is, the date when the customer created the account. The query defines createDate as

• DATE: This is a string that’s treated as a date. Dates are displayed in the format YYYY-MM-DD. They can be entered in that format or some similar formats, such as YY/M/D or YYYYMMDD.

• NOT NULL: This definition means that this field can’t be empty. It must have a value. Because the program, not the user, creates the date and stores it, it won’t ever be blank.

password: A password for the member to use when logging in. The cus­ tomer chooses and types in the password. The CREATE query defines the password in the following ways:

• VARCHAR(255): This statement defines the field as a variable char­ acter string that can be up to 255 characters long. The field is stored in its actual length. You don’t expect the password to be 255 char­ acters long. In fact, you expect it to be pretty short. However, you intend to use the MySQL password function to encrypt it rather than store it in plain view. After it’s encrypted, the string will be longer, so you’re allowing room for the longer string.

• NOT NULL: This statement means that this field can’t be empty. It must have a value. You’re not going to allow an empty password in this application.

lastName: The customer’s last name, as typed by the customer. The
CREATE query defines the field as

• VARCHAR(50): This data type defines the field as a variable charac­
ter string that can be up to 50 characters long. The field is stored in its actual length.

firstName: The customer’s first name, as typed by the customer. The
CREATE query defines the field as

• VARCHAR(40): This data type defines the field as a variable charac­
ter string that can be up to 40 characters long. The field is stored in its actual length.

street: The customer’s street address, as typed by the customer. The
CREATE query defines the field as

• VARCHAR(50): This data type defines the field as a variable charac­
ter string that can be up to 50 characters long. The field is stored in its actual length.

city: The city in the customer’s address, as typed by the customer. The
CREATE query defines the field as

• VARCHAR(50): This data type defines the field as a variable charac­
ter string that can be up to 50 characters long. The field is stored in its actual length.

state: The state in the customer’s address. The string is the two-letter state code. The customer selects the data from a drop-down list contain­ ing all the states. The CREATE query defines the field as

• CHAR(2): This data type defines the field as a character string that’s two characters long. The field will always take up two char­ acters of storage, with padding if the actual string stored is less than two characters.

zip: The ZIP code that the customer types in. The CREATE query defines the field as

• CHAR(10): This data type defines the field as a character string that’s ten characters long. The field will always take up ten charac­ ters of storage, with padding if the actual string stored is less than ten characters. The field is long enough to hold a ZIP+4 code, such as 12345–1234.

email: The e-mail address that the customer types in. The CREATE query defines the field as

• VARCHAR(50): This data type defines the field as a variable charac­
ter string that can be up to 50 characters long. The field is stored in its actual length.

phone: The phone number that the customer types in. The CREATE
query defines the field as

• CHAR(15): This data type defines the field as a character string that’s 15 characters long. The field will always take up 15 charac­ ters of storage, with padding if the actual string stored is less than
15 characters.

fax: The fax number that the customer types in. The CREATE query defines the field as

• CHAR(15): This data type defines the field as a character string that’s 15 characters long. The field will always take up 15 charac­ ters of storage, with padding if the actual string stored is less than
15 characters.

Notice that some fields are CHAR and some are VARCHAR. CHAR fields are faster, whereas VARCHAR fields are more efficient in using disk space. Your decision will depend on whether disk space or speed is more important for your application in your environment.

In general, shorter fields should be CHAR because shorter fields don’t waste much space. For instance, if your CHAR is 5 characters, the most space that could possibly be wasted is 4 characters. However, if your CHAR is 200, you could waste 199 characters. Therefore, for short fields, use CHAR for speed with very little wasted space.

Building the Login table

The Login table keeps track of member logins by recording the date and time every time that a member logs in. Because each member has multiple logins, the login data requires its own table. The CREATE query that builds the Login table is

CREATE TABLE Login (
loginName VARCHAR(20) NOT NULL, loginTime DATETIME NOT NULL,
PRIMARY KEY(loginName,loginTime) );

The Login table has only two columns, as follows:

loginName: The Member ID that the customer uses to log in with. The loginName is the connection between the Member table (which I describe in the preceding section) and this table. Notice that the loginName column is defined the same in the Member table and in this table. This makes table joining possible and makes matching rows in the tables much easier. The CREATE query defines the loginName in the following ways:

• CHAR(20): This data type defines the field as a character string that’s 20 characters long. The field will always take up 20 charac­ ters of storage, with padding if the actual string stored is less than
20 characters. If a string longer than 20 characters is stored, any characters after 20 are dropped.

• PRIMARY KEY(loginName,loginTime): The primary key identifies the row and must be unique. For this table, two columns together are the primary key. MySQL will not allow two rows to be entered with the same loginName and loginDate.

• NOT NULL: This definition means that this field can’t be empty. It must have a value. The primary key must always be set to NOT NULL.

loginTime: The date and time when the member logged in. This field uses both the date and time because it needs to be unique. It’s very unlikely that two users would log in at the same second at the Pet Store Web site. However, in some very busy Web sites, two users might log in during the same second. At such a site, you might have to create a sequential login number to be the unique primary key for the site. The CREATE query defines the loginTime in the following ways:

• DATETIME: This is a string that’s treated as a date and time. The string is displayed in the format YYYY-MM-DD HH:MM:SS.

• PRIMARY KEY(loginName,loginTime): The primary key identifies the row and must be unique. For this table, two columns together are the primary key. MySQL will not allow two rows to be entered with the same loginName and loginDate.

• NOT NULL: This definition means that this field can’t be empty. It must have a value. The primary key must always be set to NOT NULL.

Adding data to the database

This database is intended to hold data entered by customers — not by you. It will be empty when the application is first made available to customers
until customers add data. However, to test the programs while you write them, you need to have at least a couple of members in the database. You need a couple of Member IDs and passwords to test the login program. You can add
a couple of fake members for testing purposes — by using an INSERT SQL query — and remove them when you’re ready to go live with your Members Only application.

Designing the Look and Feel

After you know what the application is going to do and what information you want to get from customers and store in the database, you can design the look and feel. The look and feel includes what the user sees and how the user interacts with the application. Your design should be attractive and

easy to use. You can create your design on paper, indicating what the user sees, perhaps with sketches or with written descriptions. You should also show the user interaction components, such as buttons or links, and describe their actions. Include each page of the application in the design.

The Pet Store Members Only application has three pages that are part of the login procedures. In addition, the application includes all the pages that are part of the Members Only section, such as the page that shows the special discounts and the pages that provide discussions of pet care. In this chapter, you only build the pages that are part of the login procedure. You don’t build the pages that are part of the Members Only section, but I do discuss what needs to be included in them to protect them from viewing by non-members.

The login application includes three pages, plus the group of pages that com­
prise the Members Only section, as follows:

Storefront page: The first page that a customer sees. It provides the name of the business and the purpose of the Web site. I introduce a storefront page in Chapter 11, and in this chapter, you modify it to pro­ vide access to the Members Only section.

Login page: Allows the customer to either log in or create a new member account. It shows a form for the customer to fill in to get a new account.

New Member Welcome page: Welcomes the new users by name, letting them know that their accounts have been created. Provides any informa­ tion that they need to know. Provides a button so that users can continue to the Members Only section or return to the main page.

Members Only section: A group of Web pages that contain the content of the Members Only section.

Storefront page

The storefront page is the introductory page for the Pet Store. Because most people know what a pet store is, the page doesn’t need to provide much expla­ nation. Figure 12-1 shows the storefront page. Two customer actions are avail­ able on this page: a link that the customer can click to see the Pet Catalog and a link to the Members Only section.

Login page

The login page allows the customer to log in or create a new member account. It includes the form that customers need to fill out to get a member account. Figure 12-2 shows the login page. This page has two different submit buttons: one to log in with an existing member account and one to create a new member account.

If a customer makes a mistake on the login page, either in the login section or the new member section, the form is displayed again with an error mes­ sage. For instance, suppose that a customer makes an error when typing his e-mail address: He forgot to type the .com at the end of the e-mail address. Figure 12-3 shows the screen that he sees after he submits the form with the mistake in it. Notice the error message printed right above the form.

When members successfully log in with a valid Member ID and password, they go to the first page of the Members Only section. When new members successfully submit a form with information that looks reasonable, they go
to a New Member Welcome page (see the next section). In addition, an e-mail message is sent to the new member with the following contents:

A new Member Account has been setup for you. Your new
Member ID and password are:

gsmith secret

We appreciate your interest in Pet Store at PetStore.com

If you have any questions or problems, send email to webmaster@petstore.com

This e-mail message contains the customer’s password. I think that it’s very helpful to both the customer and the business to provide customers with a hard copy of their password. Customers will forget their password. It seems to be one of the rules. An e-mail message with their password might help them
when they forget it, saving both them and you some trouble. Of course, e-mail messages aren’t necessarily secure, so sending passwords via e-mail isn’t a good idea for some accounts, such as an online bank account. But, for this Pet Store application, with only unauthorized discounts and pet care information at risk, sending the password via e-mail is a reasonable risk.

New Member Welcome page

The New Member Welcome page greets the customer and offers useful infor­ mation. The customer sees that the account has been installed and can then enter the Members Only section immediately. Figure 12-4 shows a welcome page.

Members Only section

One or more Web pages make up the contents of the Members Only section. Whatever the content is, the pages are no different than any other Web pages or PHP programs, except for some PHP statements in the beginning of each file that prevent non-members from viewing the pages.

Writing the Programs

After you know what the pages are going to look like and what they are going to do, you can write the programs. In general, you create a program for each page, although sometimes it makes sense to separate programs into more than one file or to combine programs on a page. (See Chapter 10 for details on how to organize applications.)

As I discuss in Chapter 10, keep the information needed to connect to the database in a separate file and include it in all the programs that need to access the database. Store the file in a secure location, with a misleading name. For this application, the following information is stored in a file named dogs.inc:

<?php
$user=”catalog”;
$host=”localhost”;
$password=””;
$database=”MemberDirectory”;
?>

The member login application has several basic tasks:

1. Show the storefront page. This provides a link to the login page.

2. Show a page where customers can fill in a Member ID and a password to log in.

3. Check the Member ID and the password that the customer types against the Member ID and password stored in the database. If the ID and password are okay, the customer enters the Members Only section. If the ID and/or password are not okay, the customer is returned to the login page.

4. Show a page where customers can fill in the information needed to obtain a member account.

5. Check the information the customer typed in for blank fields or incor­
rect formats. If bad information is found, show the form again so that the customer can correct the information.

6. When good information is entered, add the new member to the
database.

7. Show a welcoming page to the new member.

The tasks are performed in three programs:

PetShopFront.php: Shows the storefront page (task 1).

Login.php: Performs both the login and create new member account tasks (tasks 2–6).

New_member.php: Shows the page that welcomes the new member (task 7).

Writing PetShopFront

The storefront page doesn’t need any PHP statements. It simply displays a Web page with two links — one link to the Pet Catalog and one link to the Members Only section of the Web site. HTML (HyperText Markup Language) statements are sufficient to do this. Listing 12-1 shows the HTML file that describes the storefront page.

Listing 12-1: HTML File for the Storefront Page

<?php
/* Program: PetShopFrontMembers.php
* Desc: Displays opening page for Pet Store.
*/
?>
<html>
<head><title>Pet Store Front Page</title></head>
<body topmargin=”0” leftmargin=”0” marginheight=”0”
marginwidth=”0”>
<table width=”100%” height=”100%” border=”0”
cellspacing=”0” cellpadding=”0”>
<tr>
<td align=”center” valign=”top” height=”30” colspan=”2”>
<img src=”images/awning-top.gif” alt=”awning”>
</td>
</tr>
<tr>
<td align=”center” valign=”top” colspan=”2”>
<img src=”images/Name.gif” alt=”Pet Store”>
</td></tr>

(continued)

344 Part IV: Applications

Listing 12-1 (continued)

<tr>
<td width=”80%” align=”center”>
<p style=”margin-top: 40pt”>
<img src=”images/lizard-front.jpg” alt=”lizard picture”
height=”186” width=”280”>
<p><h2>Looking for a new friend?</h2>
<p>Check out our
<a href=”PetCatalog.php”>Pet Catalog.</a>
<br> We may have just what you’re looking for.
</td>
<td width=”20%” bgcolor=”black”>
<div style=”color: white; link: white”>
<p style=”text-align: center; font-size: 15pt”>
<b>Looking for <br>more?</b></p>
<ul>
<li>special deals?
<li>pet information?
<li>good conversation?
</ul>
<p style=”text-align: center”>Try the
<br><a href=”Login.php”
style=”color: white”>Members Only</a>
<br>section <br>of our store
<p style=”text-align: center”><b>It’s free!</b></p>
</td>
</tr>
</table>
</body></html>

Notice the link to the login PHP program. When the customer clicks the link, the login page appears.

Writing Login

The login page (refer to Figure 12-2) is produced by the program Login.php, as shown in Listing 12-2. The program uses a switch to create two sections: one for the login and one for creating a new account. The program creates a session that’s used in all the Members Only Web pages. The login form itself isn’t included in this program; it’s in a separate file called login_form.inc, which is called into this program, whenever the form is needed, by using include statements.

Listing 12-2: Login Program

<?php
/* Program: Login.php
* Desc: Login program for the Members Only section of the
* pet store. It provides two options: (1) login

* using an existing Login Name and (2) enter a new
* login name. Login Names and passwords are stored
* in a MySQL database.
*/
session_start(); # 9 include(“dogs.inc”); #10 switch (@$_GET[‘do’]) #11
{
case “login”: #13
$connection = mysql_connect($host, $user,$password) #14
or die (“Couldn’t connect to server.”);
$db = mysql_select_db($database, $connection)
or die (“Couldn’t select database.”); #17

$sql = “SELECT loginName FROM Member
WHERE loginName=’$_POST[fusername]’”; #20
$result = mysql_query($sql)
or die(“Couldn’t execute query.”); #22
$num = mysql_num_rows($result); #23
if ($num == 1) // login name was found #24
{
$sql = “SELECT loginName FROM Member
WHERE loginName=’$_POST[fusername]’
AND password=password(‘$_POST[fpassword]’)”;
$result2 = mysql_query($sql)
or die(“Couldn’t execute query 2.”); #30
$num2 = mysql_num_rows($result2);
if ($num2 > 0) // password is correct #32
{
$_SESSION[‘auth’]=”yes”; #34
$logname=$_POST[‘fusername’];
$_SESSION[‘logname’] = $logname; #36
$today = date(“Y-m-d h:m:s”); #37
$sql = “INSERT INTO Login (loginName,loginTime) VALUES (‘$logname’,’$today’)”;
mysql_query($sql) or die(“Can’t execute query.”);
header(“Location: Member_page.php”); #41
}
else // password is not correct #43
{
unset($do); #45
$message=”The Login Name, ‘$_POST[fusername]’ exists, but you have not entered the correct password! Please try
again.<br>”;
include(“login_form.inc”); #49
}
} #51
elseif ($num == 0) // login name not found #52
{

unset($do); #54
$message = “The Login Name you entered does not exist! Please try again.<br>”;
include(“login_form.inc”);
}
break; #59

case “new”: #61
foreach($_POST as $field => $value) #62
{
if ($field != “fax”) #64
{
if ($value == “”) #66
{
unset($_GET[‘do’]);
$message_new = “Required information is missing.
Please try again.”; include(“login_form.inc”); exit();
}
}
if (ereg(“(Name)”,$field)) #75
{
/*if (!ereg(“^[A-Za-z’ -]{1,50}$”,$value))
{
unset($_GET[‘do’]);
$message_new = “$field is not a valid name.
Please try again.”;
include(“login_form.inc”);
exit();
}*/
}
$$field = strip_tags(trim($value)); #86
} // end foreach
if (!ereg(“^[0-9]{5,5}(\-[0-9]{4,4})?$”,$zip)) #88
{
unset($_GET[‘do’]);
$message_new = “$zip is not a valid zip code.
Please try again.”;
include(“login_form.inc”);
exit();
}
if (!ereg(“^[0-9)(xX -]{7,20}$”,$phone)) #96
{
unset($_GET[‘do’]);
$message_new = “$phone is not a valid phone number.
Please try again.”;
include(“login_form.inc”);
exit();
}

if ($fax != “”) #104
{
if (!ereg(“^[0-9)(xX -]{7,20}$”,$fax))
{
unset($_GET[‘do’]);
$message_new = “$fax is not a valid phone number.
Please try again.”;
include(“login_form.inc”);
exit();
}
}
if (!ereg(“^.+@.+\\..+$”,$email)) #115
{
unset($_GET[‘do’]);
$message_new = “$email is not a valid email address.
Please try again.”;
include(“login_form.inc”);
exit();
} #122
/* check to see if login name already exists */
$connection = mysql_connect($host,$user,$password)
or die (“Couldn’t connect to server.”);
$db = mysql_select_db($database, $connection)
or die (“Couldn’t select database.”);
$sql = “SELECT loginName FROM Member
WHERE loginName=’$newname’”;
$result = mysql_query($sql)
or die(“Couldn’t execute query.”);
$num = mysql_numrows($result);
if ($num > 0) #133
{
unset($_GET[‘do’]);
$message_new = “$newname already used. Select another
Member ID.”; include(“login_form.inc”); exit();
}
else #141
{
$today = date(“Y-m-d”); #143
$sql = “INSERT INTO Member (loginName,createDate, password,firstName,lastName,street,city, state,zip,phone,fax,email) VALUES
(‘$newname’,’$today’,password(‘$newpass’),
‘$firstName’, ‘$lastName’,’$street’,’$city’,
‘$state’,’$zip’,’$phone’,’$fax’,’$email’)”;
mysql_query($sql); #150
$_SESSION[‘auth’]=”yes”; #151
$_SESSION[‘logname’] = $newname; #152

(continued)

Listing 12-2 (continued)

/* send email to new member */ #153
$emess = “A new Member Account has been setup. “;
$emess.= “Your new Member ID and password are: “;
$emess.= “\n\n\t$newname\n\t$newpass\n\n”;
$emess.= “We appreciate your interest in Pet Store”;
$emess.= “ at PetStore.com. \n\n”;
$emess.= “If you have any questions or problems,”;
$emess.= “ email webmaster@petstore.com”;
$ehead=”From: member-desk@petstore.com\r\n”; #161
$subj = “Your new Member Account from Pet Store”;
$mailsend=mail(“$email”,”$subj”,”$emess”,”$ehead”);
header(“Location: New_member.php”); #164
}
break; #166

default: #168
include(“login_form.inc”);
}
?>

Some of the lines in Listing 12-2 have line numbers at the ends of the lines. The following list refers to the line numbers in the listing to discuss the pro­ gram and how it works:

9 Starts a session. The session has to be started at the beginning of the program, even though the user hasn’t logged in yet.

10 Reads in the file that sets the variables needed to connect to the data­ base. The program is called dogs.inc, which is a misleading name that seems more secure than calling it mypasswords.inc.

11 Starts a switch statement. The switch statement contains three sections, based on the value that was passed for do, obtained from the built-in array $_GET. The first section runs when the value pair passed for do is login; the second section runs when the value passed for do is new; and the third section is the default that runs if no value was passed for do. The third section just creates the login page and only runs when the cus­ tomer first links to the login page.

13 Starts the case block for the login section — the section that runs when the customer logs in. The login section of the form sends do=login in the URL, which causes this section of the switch statement to run.

14 Lines 14–17 connect to MySQL and select the database.

19 Lines 19–22 look in the database table Member for a row with the login name typed by the customer.

23 Checks to see whether a row was found with a loginName field contain­ ing the Member ID typed by the customer. $num will equal 0 or 1, depend­ ing on whether the row was found.

24 Starts an if block that executes if the Member ID was found. That means that the user submitted a Member ID that is in the database. This block then checks to see whether the password submitted by the user is cor­ rect for the given Member ID. This block is documented in more detail in the following list:

26 Lines 26–28 create a query that looks for a row with both the Member ID and the password submitted by the customer. Notice that the password submitted in the form ($fpassword) is encrypted by using the MySQL function, password(). Passwords in the data­ base are encrypted, so the password that you’re trying to match must also be encrypted, or it won’t match.

29 Lines 29–31 execute the query and check whether a match was found. $num2 equals 1 or 0, depending on whether a row with both the Member ID and the password is found.

32 Starts an if block that executes if the password is correct. This
is a successful login. Lines 32–41 are executed, performing the fol­ lowing tasks: 1) The two session variables, auth and logname, are stored in the SESSION array. 2) $today is created with today’s date and time in the correct format expected by the database table. 3) A row for the login is entered into the Login table. 4) The first page of the Members Only section is sent to the member.

43 Starts an else block that executes if the password is not correct.
This is an unsuccessful login. Lines 45–49 are executed, perform­ ing the following tasks: 1) Unset the form variable $do. This pre­ vents any confusion later. 2) Set the appropriate error message into $message. 3) Show the login page again. The login page will show the error message.

Notice that the loop starting on line 43 lets the user know when they have a real login name but the wrong password. If the security of your data is very important, you may want to write this loop dif­ ferently. Providing that information may be helpful to someone who is trying to break in. The cracker now only needs to find the pass­ word. For more security, just have one condition that gives the same error message whenever either the login name or the pass­ word is incorrect. In this example, I prefer to provide the informa­ tion because it is helpful to the legitimate member (who may not remember whether he or she installed an account at all), and I’m not protecting any vital information.

51 Ends the block that executes when the Member ID is found in the database.

52 Starts an if block that executes when the Member ID is not found in the database. This could actually be an else, instead of an elseif, but I think that it’s clearer to humans with the if condition in the statement. This block unsets the form variable $do, creates the appropriate error message and also shows the login page again, which includes the error message.

59 Ends the case block that executes when the customer submits a Member ID and password to log in. The login block extends from line 13 to this line.

61 Starts the case block that executes when the customer fills out the form to get a new member account. The form sends do=new in the URL, caus­ ing the program to jump to this section of the switch statement.

62 Starts a foreach loop that loops through every field in the new member form. The loop checks for empty required fields and checks the first and last name for acceptable characters. The statements in the loop are docu­ mented in more detail in the following list:

64 Checks whether the field is the fax field. The fax field is not required. The fax field isn’t checked to see whether it is blank because it’s okay for it to be blank.

66 Checks whether the field is blank. If it is, the if block performs the following tasks: 1) Unsets $do. 2) Creates an error message that explains the problem. 3) Shows the login form again, includ­ ing the error message. 4) Stops the program and waits for the user to submit the form again with the field filled in.

75 Checks whether the field is the last name or first name field. If so, it checks the field format for allowed characters. If any characters that are not allowed are found, it performs the following tasks:
1) Unsets $do. 2) Creates an error message that explains the prob­
lem. 3) Shows the login form again, including the error message.
4) Stops the program and waits for the user to submit the form again with the correct format.

86 Trims extra spaces from all the field values after they are checked in line 66 to be sure that they aren’t blank. Removes any HTML tags that are in any of the fields. Creates a variable for each of
the fields in the following way. Suppose in the first loop of the foreach loop, the variable is $ _POST [loginName] = gsmith. The foreach loop sets $key=”loginName” and $value=”gsmith”. Therefore, the statement in line 86 is equivalent to
$loginName=strip_tags(trim(“gsmith”)). The
$$key is $loginName because $key=loginName.

87 Ends the foreach loop.

88 Lines 88–122 are a series of if blocks that check the fields for the cor­ rect format. If any of the fields checked doesn’t have the correct format, the block performs the following tasks: 1) Unsets $do. 2) Creates an error message that explains the problem. 3) Shows the login form again, includ­ ing the error message. 4) Stops the program and waits for the user to submit the form again with the correct format.

124 Lines 124–132 check whether the Member ID submitted by the customer is already a loginName in the database table Member. The loginName must be unique. $num equals 0 or 1, depending on whether the loginName is found in the database.

133 Starts an if block that executes if the loginName is already in the data­ base. The new member cannot be added if the Member ID is not unique. The block performs the following tasks: 1) Unsets $do. 2) Creates an error message that explains the problem. 3) Shows the login form again, including the error message. 4) Stops the program and waits for the user to submit the form again with a different Member ID.

141 Starts an else block if the loginName is not already in the database.
This is a successful application for a member account. The block inserts a new row in the Member table for the new member account and sends
an e-mail message to the customer about the new account. The state­
ments in the block are documented in more detail in the following list:

143 Sets $today to today’s date in the correct format for the
createDate field in the Member table.

144 Creates an INSERT query to add the new member row. Notice that the password is encrypted as password(‘$newpass’) when it is entered. This is a security method so that no one who looks in the database can see the password. If you’re totally sure that no one will see the database that shouldn’t, encryption isn’t really necessary.

150 Executes the INSERT query.

151 In lines 151 and 152, the two session variables, $auth and $logname, are stored in the SESSION array.

154 Lines 154–163 send an e-mail to the new member, verifying the Member ID and password. Notice that the e-mail message is cre­ ated in the variable $emess over several lines. It begins in line 154 and is added to (by using .=) on each line until it finishes on line
160. This is to make it easier for humans to read — not because PHP needs this. Unlike HTML content that ignores extra spaces and line ends, extra spaces and other things have an effect on an
e-mail message. For instance, if I created one long message — with extra spaces to indent it so that I could read it — those spaces would show up in the e-mail. So, I set the message on several lines that I can indent for readability in the program. Line 163 uses the PHP function mail to send the e-mail message. The mail function is documented in Chapter 14.

164 Sends the customer to the New Member page.

165 Ends the else block for a successful new member account application.

166 Ends the case block for the New Member section of the login page.

168 Starts the case block for the default condition. If $do is not set to either “login” or “new”, the program skips to this block. Because both the forms on the login page set $do, this block only executes the first time this program runs — when the user links to it from the storefront page and has not yet submitted either form. This section has only one state­ ment: a statement that displays the login page.

This program shows the login page in many places. This is done with include statements that call the file login_form.inc. This file includes the HTML that produces the login page. The program Login.php does not produce any output at all. All the output is produced by login_form.inc. This type of application organization is discussed in Chapter 10. This is a good example
of the use of include files. Just imagine this program, which is long enough, if the statements in login_form.inc, shown in Listing 12-3, were included in the Login program at each place where login_form is included. Whew, that would be a mess that only a computer could understand.

Listing 12-3: File That Creates the Login Page

<?php
/* File: login_form.inc
* Desc: Displays login page. Page displays two forms--one
* form for entering an existing login name and
* password and another form for the information
* needed to apply for a new account.
*/
include(“functions12.inc”); # 8
?>
<html>
<head><title>Members Only Login</title></head>
<body topmargin=”0” leftmargin=”0” marginheight=”0”
marginwidth=”0”>
<table border=”0” cellpadding=”5” cellspacing=”0”>
<tr><td colspan=”3” bgcolor=”gray” align=”center”>
<font color=”white” size=”+10”>
<b>Members Only Section</b></font></td></tr>

<tr>

<td width=”33%” valign=”top”>
<font size=”+1”><b>Are you a member?</b></font>
<p>
<!-- form for customer login -->
<form action=”Login.php?do=login” method=”POST”>
<table border=”0”>

<?php #25
if (isset($message))
echo “<tr><td colspan=’2’>$message </td></tr>”;
?>

<tr><td align=right><b>Username</b></td>
<td><input type=”text” name=”fusername”
size=”20” maxsize=”20”>
</td></tr>
<tr><td width=”120” align=”right”><b>Password</b>
</td>
<td><input type=”password” name=”fpassword”
size=”20” maxsize=”20”></td></tr>
<tr><td align=”center” colspan=”2”>
<br><input type=”submit” name=”log”
value=”Enter”>

<?php

?>

</td></tr>
</table>
</form>
</td>
<td width=”1” bgcolor=”gray”></td>
<td width=”67%”>
<p><font size=”+1”><b>Not a member yet?</b></font> Get discounts, a newsletter, advance notice of new pets, much more. Fill in the information below and join. It’s easy and free! </b>
<!-- form for new member to fill in -->
<form action=”Login.php?do=new” method=”POST”>
<p>
<table border=”0” width=”100%”>

if (isset($message_new)) #55
echo “<tr><td colspan=’2’><b>$message_new</b>
</td></tr>”;

<tr><td align=”right”><b>Member ID</b></td>
<td><input type=”text” name=”newname” value=”<?php echo @$newname ?>” size=”20” maxlength=”20”></td></tr>
<tr><td align=”right”><b>Password</b></td>
<td><input type=”password” name=”newpass” value=”<?php echo @$newpass ?>” size=”10” maxlength=”8”></td></tr>
<tr><td align=”right”><b>First Name</b></td>
<td><input type=”text” name=”firstName” value=”<?php echo @$firstName ?>” size=”40” maxlength=”40”></td></tr>
<tr><td align=”right”><b>Last Name</b></td>
<td><input type=”text” name=”lastName” value=”<?php echo @$lastName ?>” size=”40” maxlength=”40”></td></tr>
<tr><td align=”right”><b>Street</b></td>
<td><input type=”text” name=”street” value=”<?php echo @$street ?>” size=”55” maxlength=”50”></td></tr>

(continued)

Listing 12-3 (continued)

<tr><td align=”right”><b>City</b></td>
<td><input type=”text” name=”city” value=”<?php echo @$city ?>” size=”40” maxlength=”40”></td></tr>
<tr><td align=”right”><b>State</b></td>
<td><select name=”state”>

<?php

$stateName=getStateName(); #86
$stateCode=getStateCode(); #87
for ($n=1;$n<=50;$n++)
{

$state=$stateName[$n];
$scode=$stateCode[$n];
echo “<option value=’$scode’”;
if ($scode== “AL”)
echo “ selected”;
echo “>$state\n”;
}
?>
</select>
&nbsp;&nbsp;&nbsp;&nbsp;<b>Zip</b>
<input type=”text” name=”zip” value=”<?php echo @$zip ?>” size=”10” maxsize=”10”>
</td></tr>
<tr><td align=right><b>Phone</b></td>
<td><input type=”test” name=”phone” value=”<?php echo @$phone ?>” size=”15” maxlength=”20”>
&nbsp;&nbsp;&nbsp;<b>Fax</b>
<input type=”text” name=”fax” value=”<?php echo @$fax ?>” size=”15” maxlength=”20”></td></tr>
<tr><td align=right><b>Email Address</b></td>
<td><input type=”test” name=”email” value=”<?php echo @$email ?>” size=”55” maxlength=”67”></td></tr>
<tr><td>&nbsp;</td>
<td align=”center”>
<input type=”submit”
value=”Become a Member”></td>
</tr>
</table>
</form>
</td>
</tr>
<tr><td colspan=”3” bgcolor=”gray”>&nbsp;</td></tr>
</table>

<div align=”center”><font size=”-1”>
All comments and suggestions are appreciated. Please
send comments to <a href=”mailto:webmaster@petstore.com”>
webmaster@petstore.com</A> </font></div>
</body></html>

Notice the following points about login_form:

Most of the statements are HTML, with a few small PHP sections here and there.

The two forms that start on lines 23 and 51 set action to the same pro­
gram, but add a different string to the URL — do=login or do=new.

The error messages are shown on the login page by using small PHP sec­ tions. Each form has its section, and the message has different names for the two forms: $message and $message_new. On line 26, the variable
$message is tested. If it has a value, the message is shown but is not if
it has no value. If there was no error in the form, the message was never set, and no message is displayed. A similar statement on line 55 shows error messages for the new member form.

A selection drop-down list (started on line 84) is provided for the cus­ tomer to select the state, guarding against typing errors by the customer. Notice that lines 86 and 87 call functions. These functions are not PHP functions; they’re my functions. The functions are included in the pro­ gram on line 8. The functions make arrays from a list of state names and a list of two-letter state codes. By using functions, you don’t need the two lists of 50 states in the program. The functions can be used repeat­ edly for many programs. The function12.inc file contains the two functions as follows:

<?php
function getStateCode()
{
$stateCode = array(1=> “AL” , “AK” ,
“AZ” ,
...
“WY” );
return $stateCode;
}

function getStateName()
{
$stateName = array(1=> “Alabama”, “Alaska”,
“Arizona”,
...

“Wyoming” );
return $stateName;
}
?>

A for loop then creates 50 options for the select list, using the two state arrays.

After running Login.php, if the user is successful with a login, the first page of the Members Only section is displayed. If the user is successful in obtain­ ing a new user account, the New_member.php program is run.

Writing New_member

The New Member Welcome page greets new members by name and provides information about their accounts. Members then have the choice of entering the Members Only section or returning to the main page. Listing 12-4 shows the program that displays the page that new members see.

Listing 12-4: Program That Welcomes New Members

<?php
/* Program: New_member.php
* Desc: Displays the new member welcome page. Greets member by name and gives user choice to enter
* restricted section or go back to main page.
*/
session_start(); # 7

if (@$_SESSION[‘auth’] != “yes”) # 9
{
header(“Location: Login.php”);
exit();
}
include(“dogs.inc”); #14
$connection = mysql_connect($host,$user,$password)
or die (“Couldn’t connect to server.”); #16
$db = mysql_select_db($database, $connection)
or die (“Couldn’t select database.”); #18
$sql = “SELECT firstName,lastName FROM Member
WHERE loginName=’{$_SESSION[‘logname’]}’”;
$result = mysql_query($sql)
or die(“Couldn’t execute query 1.”);
$row = mysql_fetch_array($result,MYSQL_ASSOC);
extract($row);

echo “<html>
<head><title>New Member Welcome</title></head>
<body>
<h2 align=’center’ style=’margin-top: .7in’>
Welcome $firstName $lastName</h2>\n”; #29
?>
<p>Your new Member Account lets you enter the Members Only section of our web site. You’ll find special discounts and bargains, a huge database of animal facts and stories, advice from experts, advance notification of new pets for sale,
a message board where you can talk to other Members, and much more.
<p>Your new Member ID and password were emailed to you. Store them carefully for future use.<br>
<div align=”center”>
<p style=”margin-top: .5in”><b>Glad you could join us!</b>
<form action=”Member_page.php” method=”POST”>
<input type=”submit”
value=”Enter the Members Only Section”>
</form>
<form action=”PetShopFrontMembers.php” method=”POST”>
<input type=”submit” value=”Go to Pet Store Main Page”>
</form>
</div>
</body></html>

Notice the following points about New_member.php:

A session is started on line 7. This makes the session variables stored in
Login.php available to this program.

The program checks, beginning on line 9, whether the customer is logged in. $auth is set to yes in Login.php when the customer successfully logs in or creates a new account and stored in the $_SESSION array. If
$auth doesn’t equal yes, the customer isn’t logged in. If a customer tries to run the New_member.php program without running the Login. php program first, $_SESSION[auth] won’t equal yes, and the user will be sent to the login page.

The program gets the customer’s first and last name from the database, beginning with the database connection statement on line 15. In line
19/20, the query is created by using $_SESSION[logname] to search for the member’s information. The session variable logname that contains the Member ID was set in the login program.

The PHP section ends on line 30. The remainder of the program is HTML.

The program uses two different forms to provide two different submit buttons. The form statements on lines 41 and 45 start different programs.

The customer controls what happens next. If the customer clicks the button to return to the main page, the PetShopFront.php programs runs. If the cus­ tomer clicks the Members Only Section submit button, the first page of the Members Only section is shown.

Writing the Members Only section

The Web pages in the Members Only section are no different than any other Web pages. You just want to restrict them to members who are logged in. To do this, you start a session and check whether they’re logged in at the top of every page. The statements for the top of each program are

session_start();
if (@$_SESSION[‘auth’] != “yes”)
{
header(“Location: Login.php”);
exit();
}

When session_start executes, PHP checks for an existing session. If one exists, it sets up the session variables. One of the session variables is $auth. When the user logs in, $_SESSION[auth] is set to yes. If $_SESSION[auth] doesn’t equal yes, the user is not logged in, and the program takes the user
to the login page.

Planning for Growth

The original plan for an application usually includes every wonderful thing that the user might want it to do. Realistically, it’s usually important to make the application available to the users as quickly as possible. Consequently, applications usually go public with a subset of the planned functionality. More functionality is added later. That’s why it’s important to write your application with future growth in mind.

Looking at the login application in this chapter, I’m sure you can see many things that could be added to it. Here are some possibilities:

E-mail a forgotten password. Users often forget their passwords. Many login applications have a link that users can click to have their passwords e-mailed to them.

Change the password. Members might want to change their password.
The application could offer a form for password changes.

Update information. Members might move or change their phone number or e-mail address. The application could provide a way for members to change their own information.

Create a member list. You might want to output a nicely formatted list of all the members in the database. This probably isn’t something you want to make available to other members but just for yourself. In some situations, however, you might want to make the list available to all members.

You can easily add any of these abilities to the application. For instance, you can add a button to the login form that reads Forgot my password that e-mails the password to the e-mail address in the database. The button can run the login program with a section for e-mailing the password or run a different program that e-mails the password. In the same manner, you can add buttons for changing the password or updating the customer information. You don’t need to wait until an application has all its bells and whistles to let your cus­ tomers use it. You can write it one step at a time.

1 comments:

kyle said...

I have no idea how to dow this