ServerCore helps you write programs that run on Web servers. As explained in Foundations, that’s where much of the business value of the Web is. It’s not surprising that companies want to hire people with server-side skills. Even humans, as well as us dogs.
ServerCore covers the programming language PHP. Other languages are used on the server-side, like PERL, Ruby, Java, and C#. The first chapter explains why PHP was chosen. The second chapter is short, talking about how PHP works, and (free!) tools for writing PHP.
The rest of the book talks about things you can do with PHP, like getting form data, saving it into a database, creating reports, and making a log in system for security.
ServerCore will not make you a professional PHP programmer. But that’s not the goal.
ServerCore covers just the most important bits of PHP. It shows how PHP is used to solve real, though simplified, problems. When you finish ServerCore, you will know enough to:
So, let’s get started!
Learning anything takes time, and time is valuable. Why spend your time at this Web site, when you could be playing in the sun?
This chapter answers three questions:
You should know that:
CoreDogs will not turn you into a professional PHP programmer.
That takes years of learning and practical experience.
CoreDog’s goal is simpler:
Help you learn the core of PHP as it is used in the real world.
CoreDog’s PHP lessons can help you:
Let’s start by looking at what PHP does, and why it can help to know it. Then we’ll talk about the CoreDogs Way.
Should you spend your time learning PHP at CoreDogs? This lesson looks at part of that question: why learn PHP at all?
By the end of this lesson, you should:
It’s easiest to understand with an example.
Humans have a site called Facebook. Maybe you’re a human yourself (I won’t hold it against you), and have a Facebook account.
Suppose we want to create Snoutbook, like Facebook but for dogs. One feature will be like the Facebook wall. The wall is a place where people can put text, photos, and other stuff. Here’s part of my wall (please don’t tell the Facebook people that I’m really a dog):

Figure 1. My wall
Snoutbook will have the same thing, but we’ll call it the tree.
When Ivan posts to a new entry to his tree, other dogs get to see it:

Figure 2. Ivan’s tree
Here’s how it will work:

Figure 3. Snoutbook
Mazie types http://snoutbook.com into her browser. The Web server is the software that sends back the HTML for that page. The best known Web server is Apache. You can use it for free. We’ll talk more about that in the next chapter.
Web servers aren’t good at storing lots of data. But database servers are good at that. So the Web server sends Ivan’s post to the database server. When Mazie wants to look at Ivan’s tree, the Web server gets the data from the database server, and formats it.
There are many different database servers. One of the best known is MySQL. It’s free, too.
But wait, wait! There’s a problem. Web servers don’t know how to talk to database servers! Arghhhhh! What to do?
Enter PHP.
PHP code can be embedded directly into HTML files, just like JavaScript.

Figure 4. tree.php
Unlike JavaScript, the PHP statements are run on the Web server. They can do servery things, like access databases.
The files usually have the extension .php instead of .html. So if Mazie wanted to look at Ivan’s tree, her browser would ask for tree.php rather than tree.html. But the browser would still get HTML code.
Here’s what happens when Mazie looks at Ivan’s tree.

Figure 5. Snoutbook’s architecture
The browser asks the server for tree.php (1). The Web server – Apache, say – loads the file tree.php from disk into memory (2). The Web server sees that the file’s extension is .php, and sends the file to the PHP interpreter (3).
The PHP interpreter is a program, running on the same computer as Apache. It knows how to follow instructions written in the PHP language. These PHP instructions are written by a person – human, dog, or some other intelligent being.
So the PHP interpreter runs the PHP code in tree.php (4). This code asks the database server for Ivan’s tree postings. The database server returns the data. The PHP code wraps the data in HTML tags, like <p> and <div>.
When it has finished running all the code, the PHP interpreter sends the result back to the Web server (5). The result will be a bunch of HTML, created by the PHP code in tree.php. The server then sends the data to the browser (6).
The browser just gets HTML. It doesn’t know, or care, that it was generated by a program.
So that’s what PHP does. PHP code runs on a server, and generates HTML (usually – it can output other types of data as well).
So why is this important?
When Mazie tells her browser to get tree.php, she sees something different each time. What she sees depends on what is in the database. If Ivan has added something, the database will have changed, and tree.php will show different content.
Server-side programming technology makes the Web what it is today. It’s the ability to store data from one person and show the data to someone else that’s important.
This technology is behind YouTube, Amazon, Twitter, Facebook, and, well, just about every big site you’ve heard of. It makes the Web what it is today.
Server-side programming is also behind search engines like Google, Yahoo, and Bing. They grab Web pages and store them. When you do a search, a program runs on one of their servers, and access the stored data.
These sites don’t use PHP, necessarily. People use many languages for Web programming, including PERL, ASP.NET, C, C++, C#, Java, Ruby, and Python. More on this later.
Let’s talk about how learning PHP can help you.
You won’t know how the Web works unless you know something about server-side processing. There’ll be a big gap in your knowledge. You won’t know how most of the Web pages you see every day are created.
Much of the business value of the Web depends on the server side. Want to sell products online? Your product data will be in a database. Web pages describing products will be generated by programs written in PHP (or some other language).
If you set up a Web shop, you probably won’t write your own PHP shopping cart. You’d use one somebody else has written. But if you understand how it all works, you can manage the software more effectively. Which brings us to…
You have thousands of PHP applications to choose from. Many of the most popular Web applications in the world are written in PHP: WordPress, Drupal, Joomla, and many others.
If you learn a little PHP, you’ll find it easier to install and manage these applications. The documentation and forum discussions will make more sense to you.
You’ll also be able to…
You can use PHP to extend existing applications. You can change them to match your exact requirements.
CoreDogs is like this. It’s a Drupal site. Drupal is a very powerful content management system. But it doesn’t do everything I wanted.
For example, CoreDogs embeds exercises directly in pages. It shows the exercises you have completed in your portfolio. Suppose you want to share some of your solutions with, say, your cousin Jim. You can choose the exercises you want him to see, and send him a URL that lists them.
Drupal doesn’t have these features. But it does let you add your own features. This was one of the reasons I chose Drupal. To add your own “modules” (as Drupal extensions are called), you need to be able to write PHP.
You can write simple PHP programs for many tasks. For example, you can build a contact page. Or a page that helps users recommend a site to a friend. Even a simple chat feature is easy to write.
CoreDogs won’t turn you into a professional PHP programmer. But you’ll find that there are many basic day-to-day things you’ll be able to do with PHP.
If you work for a big company, you can bet that PHP or another server-side language is used on many of the firm’s sites. If you haven’t run into one yet, you will.
You might be asked to work on one of those sites, as a document creator, graphic designer, project manager, or in some other role. You might work with PHP programmers. You’ll be able to work with them more effectively if you understand the tools they’re using.
If you run a small business Web site, you might hire and supervise PHP contractors. You’ll be able to do that better if you know a little PHP yourself. You’ll also be able to explain to them what you want, and understand their answers to your questions.
There are many different Web jobs. Designer, artist, writer, community manager, usability tester, marketer, server admininstrator,... Oh, and programmer.
Learning about PHP will help you decide how much server-side programming you want in your career.
There are good reasons why it’s worth learning server-side programming. But…
There are two reasons it’s best to start with PHP.
PHP is likely the most widely used server-side programming language (see this article, for example). It’s used on millions of servers world wide.
HotScripts is a collection of Web programs and related stuff. They list scripts (another word for programs) by language. What language has the most scripts? What percentage of their scripts are in PHP?
(Log in to enter your solution to this exercise.)
PHP is very popular in the open source world, where applications like WordPress and Drupal live. One reason is that PHP is itself open source.
This means that your PHP skills will have value to many different people (including you!).
Open up the control panel on your shared hosting account. If you don’t have an account, get one.
You probably have something like Fantastico, that lets you install software on your account. Here’s what part of my Fantastico page looks like:

Figure 1. Fantastico
These are some of the applications Fantastico will install for me.
Pick ten or so applications at random. Find out what server-side programming language they use. List the number that use each different language.
(Log in to enter your solution to this exercise.)
Make no mistake: programming is hard. For most people, anyway.
What people have trouble with is not the programming languages, but the way of thinking about program design. This only comes with practice, practice, and more practice.
But still, some languages are easier to learn than others. Java is one of the more difficult. It’s a good language, but it’s very structured, and hard for beginners to get started with.
Two of the easiest languages to learn are JavaScript and PHP. Even a few lines of JavaScript and PHP can do useful things. You can start writing programs quickly, without having to learn of lot of stuff first.
PHP programs run on Web servers. They let Web pages store data on database servers. Server-side programs are crucial on today’s Web.
Learning PHP can help you:
PHP is widely used. It’s easy to learn, for a programming language.
Let’s talk about the CoreDogs Way of learning PHP. You should decide whether it’s right for you.
You know roughly what PHP does, and why it is worth learning. But why learn with CoreDogs?
This lesson explains:
Learning how to create Web sites is worthwhile.
But learning takes time. Time you could spend doing other things.
CoreDogs helps you spend your learning time well.
Why do all these things? Because of a core value of the CoreDogs project:
CoreDogs should use ideas from the science of learning.
There’s a lot of good research on how people learn. The last two decades have seen some amazing work. People into learning research will recognize terms like constructivism, scaffolding, phenomenography, and social learning.
CoreDogs uses ideas from this research. That means that CoreDogs is unlike other books or Web sites on PHP. They tend to be written by people with strong technical skills, but who haven’t studied learning.
CoreDogs has less technical material about PHP than other sources. What is here is just the important core. And the lessons are designed to help reduce your learning time.
Learning how to create Web sites is worthwhile. You can make a career out of it. You can give a boost to another career. You can serve the greater good.
Learning takes time. CoreDogs helps you spend your learning time well. It focuses on the real world. It flattens the learning curve. CoreDogs leaves a lot out, focusing only on the core of PHP. CoreDogs encourages practice, practice, practice.
CoreDogs uses ideas from the science of learning. It has less technical material about PHP than other sources. What is here is just the important core. The lessons help reduce your learning time.
To finish up this chapter, let’s talk about how you can use CoreDogs most effectively.
You know roughly what PHP does, and why it is worth learning. You’ve read about the CoreDogs Way. What if you like what you see, and decide to go further?
By the end of this lesson, you should know how to get the most from your CoreDogs learning time.
CoreDogs breaks up concepts into small chunks. Some ideas are scattered across chapters. For example, PHP’s if statement is explored a little here, a little there, and a little somewhere else.
This makes learning easier, since you don’t have to learn complicated new things all at once. But it also means that if you jump around the chapters, you might get confused.
It’s best to read the chapters in sequence. There are forward and backward links at the top and bottom of each lesson.
I can’t stress this enough. PHP is about doing, not memorizing. You won’t learn unless you do the exercises.
Exercises are in two places. Some are mixed in with the lessons, usually following an example. They tend to be “near transfer” tasks. That is, tasks that are similar to the examples in the lessons.
There are also exercises at the end of each chapter. They are more “far transfer” tasks. They ask you to apply what you have learned to new situations.
Ideally, you should do both types of exercises.
Working with other people can help a great deal. You’ll be less frustrated when you can bounce ideas off other people. And it’s motivating to see other people working on the same stuff.
Some CoreDoggers have natural groups; they might be using CoreDogs in a college course, for example. Other CoreDoggers are not so lucky.
You can use Facebook to find other people to work with. You can use other sites, of course, but CoreDogs is Facebook-oriented.
If you work in a company, you might be able to find coworkers who want to learn about Web sites. Or maybe there are people in your neighborhood association. Ask around.
You’ll learn a lot with CoreDogs. And it will be useful stuff.
From time to time, think back over what you have learned. Take a look at your exercise portfolio; click Your account and the portfolio tab. This is a visible record of your accomplishments.
Share some of your exercise solutions with others. For example, you might want to show your friend Jenny what you know how to do. You can choose the solutions you want to share, and email people a URL that will show those solutions. See the portfolio tab on the Your account page for details.
OK, this is more for me than for you. You might find mistakes, think that something should be explained better, have an idea for a good example,... Let me know!
There’s a feedback area to the right, below the notes and quick links.
It often takes me a while to fix errors. CoreDogs is evolving, with a lot of work still to be done. But I do appreciate your taking the time to send feedback.
This lesson explains how to make the most of CoreDogs. You should read in sequence, do the exercises, work with a group, take notes, and take pride in your learning. Please send feedback as well.
Let’s get going with PHP. We’ll start by reviewing what PHP does, and then look at tools that will make your PHP work easier.
This short chapter is about how you’ll do PHP work. First, it reviews what PHP does. Then it recommends some tools to make your PHP work easier.
By the end of this lesson, you should:
Recall that Snoutbook is Facebook for dogs. It has the tree, equivalent to Facebook’s wall. When Ivan posts to his tree, everyone can see his new entry.

Figure 1. Ivan’s tree
If Mazie wants to look at Ivan’s tree, she points her browser at tree.php. tree.php has PHP code embedded into HTML.

Figure 2. tree.php
Here’s what happens when Mazie looks at Ivan’s tree.

Figure 3. Snoutbook’s architecture
The browser asks the server for tree.php (1). The Web server – Apache, say – loads the file tree.php from disk into memory (2). The Web server sees that the file’s extension is .php, and sends the file to the PHP interpreter (3).
The PHP interpreter is a program, running on the same computer as Apache. It knows how to follow instructions written in the PHP language. These PHP instructions are written by a person – human, dog, or some other intelligent being.
So the PHP interpreter runs the PHP code in tree.php (4). This code asks the database server for Ivan’s tree postings. The database server returns the data. The PHP code wraps the data in HTML tags, like <p> and <div>.
When it has finished running all the code, the PHP interpreter sends the result back to the Web server (5). The result will be a bunch of HTML, created by the PHP code in tree.php. The server then sends the data to the browser (6).
The browser just gets HTML. It doesn’t know, or care, that it was generated by a program.
So that’s what PHP does. PHP code runs on a server, and generates HTML (usually – it can output other types of data as well).
PHP code can work with a database server. But it can do other things as well.
When you can write PHP code, you can do, well, all sorts of things.
PHP programs run on Web servers. They can work with database servers. PHP can also send email, read and write files, and interact with many kinds of other servers.
Let’s look at the tools you’ll need to make your PHP work easier.
You know what PHP programs do. Now let’s look at the tools you’ll need to write them.
By the end of this lesson, you should:
There are two general ways to end up with runnable PHP code on a server:
You can do the former, but I don’t recommend it. If you like the Unix command line, you can log in to your Web host and use pico or some other character-oriented editor. Or you can use an editor with FTP support.
There are two problems with this. First, other people might try to access your Web site as you’re working on it. They’ll see a broken site. Second, unless you’re careful with backups, it’s easy to damage a file, and not be able to get it back to a working state.
I recommend the second approach: Get your PHP working on your own computer, then upload the files to your hosting account. That means installing a Web server on your own computer. It’s easier than you think.
You’ll need at least three types of tools:
You’ll need software to write PHP code. The same tool should help you write HTML, CSS, and JavaScript as well.
There are two main types of tools you can use: text editors and integrated development environments. There of lots of different options. I’ll just recommend one of each type.
Notepad++ is a free Windows text editor. It has lots of nice features, including syntax highlighting and code completion. You can install it on a portable drive (like a USB stick), so you can carry it around and use it on any PC that is available.
TextMate is often recommended for the Mac, but it’s not free. Aptana and TextWrangler are free options.
An integrated development environment (IDE) is a more complete solution. The one I recommend at the moment is the PHP bundle of Netbeans. The editor is better than Notepad++, and it has things like an integrated debugger. It’s free. You can run Netbeans from a portable drive (like a USB stick), if you want.
You can start with Notepad++ if you like. But you should switch to Netbeans eventuually. It will make you more productive. The best thing: it will find some typing mistakes for you! Yay!
You can do file transfer from within both Notepad++ and Netbeans, but I prefer using a separate program. I generally get the PHP right, and then upload it as a separate step.
The program I use is WinSCP. It supports FTP, SFTP, and SCP. Easy to use, and free.
You can use your own computer as a development server. Just install Web server software, and off you go.
Let’s do that now. We’ll be looking at a Windows installation, but other platforms are similar.
Download XAMPP from ApacheFriends. There are Windows, Mac, and Linux versions. It’s easiest to grab the installer, since it does all the work for you.
Run the installer, and install XAMPP at c:\ (for Windows). It will install the Apache Web server, MySQL, and some other stuff.
XAMPP comes with a control panel you can use to start and stop Apache. Run it, and start Apache. It will look something like this:

Figure 1. XAMPP control panel
Now you can use your browser to access Apache on your computer. But what is the URL?
Some IP addresses and domain names are reserved for special uses. The IP address 127.0.0.1 and the domain name localhost always map to the computer you are using. So start your browser, and type http://localhost into the address bar. You should see a welcome page from XAMPP.
W00f!
Now you have a development server. You can create pages, and upload them to your hosting account.
You should be able to put files on your disk, and access them through your localhost. But you need to put them in the right place.
If you installed XAMPP at C:\xampp, the root of your Web – your DocumentRoot – will be C:\xampp\htdocs. To make a file available through your development server, all you have to do is copy it to somewhere under C:\xampp\htdocs.
Let’s try it. Create a directory under C:\xampp\htdocs. Called it testthing. Now create a text file with a joke in it, and save it in that directory, under the name joke.txt.
Here’s a screen shot:

Figure 2. Joke file
Windows Explorer is in the background (you can see the path and file name). I just used Notepad to make the file. You can see the file’s contents.
Let’s see if it worked. Go to your browser, and enter the URL:
http://localhost/testthing/joke.txt

Figure 3. Joke in the browser
If you did everything right, then – wee hoo!
You can write PHP code directly on your Web hosting account’s server. But it’s better to install a development server on your own computer, and do your programming there.
Notepad++ is a good text editor for PHP work. Netbeans is a good IDE.
WinSCP is good for file transfer.
Download XAMPP from ApacheFriends. It’s an easy way to install a development server.
Time to dig into PHP. Let’s start with one of the most important uses of PHP, one that gives you a big productivity win: creating dynamic Web templates.
Our first PHP task is an easy one, but one of the most important uses of PHP.
Most Web pages have regions, like this:

Figure 1. Web page regions
Different parts of a page are put into the regions:

Figure 2. Using the regions
All of the pages on a site usually have the same structure. Some regions have the same content. For example, every page might have the same thing in the bottom region. CoreDogs is like this. No matter what page you look at, the bottom is the same.
Well, almost. The message on the right of the footer is chosen randomly for each page.
Suppose you have a Web site with, say, 200 pages. Every page has this in the bottom region:

Figure 3. Original footer
Your client (or employer, school, sister, whoever) wants to change it to this, on every page:

Figure 4. New footer
If each page is a separate HTML, that means you have to change 200 files. Ack! What a pain.
But if you use PHP to set up a template for the site, you can change all 200 pages by just changing one file. You read that right – change the entire site by editing one file!
Talk about a productivity win.
If you use just one thing from this book, make it this one: creating active Web templates with PHP. That’s what you’ll learn in this chapter. And it’s one of the easiest things you can do with PHP.
By the end of this chapter, you should:
One thing to keep in mind: this can get confusing! Not so much because the PHP for Web templates is complex. It’s not. It’s simple, in fact.
The problem is that there are so many pieces to a Web site. HTML file, CSS files, JavaScript files, link tags, image tags, and other things. They all need special treatment.
For this chapter to make sense, you’ll need to know how Web sites are put together. I’ll assume that you’ve worked through the ClientCore book, and more-or-less understand it. You should know about images, links, nav bars, CSS files, JavaScript files, a little jQuery, and page layouts.
Let’s get started!
By the end of this lesson, you should:
print statement.Let’s take a look at a simple PHP program. It will show the time on the server. This is quite likely to be in a different time zone from the one you’re in, but that’s OK.
To begin, click here. A new window (or tab) opens, and a page shows with the time on the server.
Now look at the page’s URL. It will look something like this:
http://coredogs.com/content_media/lessons/servercore/web-site-templates/server-time.php
Look at the extension of the file. Usually, we see .html. This one is .php. What’s the difference?
When a Web server gets a request for a URL, it looks at the extension to figure out what to do. Here is the process for an HTML file.

Figure 1. Get an HTML file
The browser sends a request for x.html to the server (1). The server looks on its hard disk, and reads the file (2). It sends the data it read back to the browser (3).
You can read about the details on the page Static Web pages.
But when the URL ends in .php, the server does something else.

Figure 2. Get a PHP file
The browser sends a request to the server, this time for x.php. The server reads the file from its disk drive (2). The server notices the file has the extension .php, so it sends the data it read to the PHP interpreter (3).
The PHP interpreter is a program that runs on the server, just like any other program. It can follow instructions written in the PHP programming language.
The PHP interpreter follows the instructions in the data it got from the server (4). Most of the time, PHP code is embedded inside HTML code.
The PHP interpreter sends its results back to the server (5). The server sends the data back to the browser (6).
All of this happened when you clicked on a link to http://coredogs.com/content_media/lessons/servercore/web-site-templates/server-time.php. The browser asked the Web server at coredogs.com for the the data at server-time.php (1 in Figure 2). The server read the file server-time.php from its disk drive (2), and passed the file’s contents to the PHP interpreter (3). The interpreter ran the code (4), and gave the results back to the Web server (5). The server then sent the data back to your browser (6).
That’s a lot of stuff happening, just because you clicked a link!
Have a look at the HTML code that the browser got from this page. (In Firefox, hit Control-U. In other browsers, right-click and select View source or Show source, or whatever it says.)
You’ll see something like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Server time</title>
</head>
<body>
<p>The time at the server is
04:10:22 PM </p>
</body>
</html>
Figure 3. The HTML
It looks just like regular HTML, although the formatting around line 8 is a little strange. There is no PHP or anything here, just plain HTML.
The PHP in the file server-time.php generates normal HTML. The browser can’t tell the difference between a page manually typed in by a person, and one that’s generated. That’s the beauty of PHP; the browser just gets HTML, a language it already understands.
Now click the server time link again. You’ll get a different time, assuming that a few seconds have elapsed. Have a look at the HTML source of the new page. It’s almost the same as it was. Just the time is a little different.
In fact, each time your browser asks for server-time.php, it will get plain HTML, but it will get different HTML (if the requests are at least a second apart). The URL is always the same:
http://coredogs.com/content_media/lessons/servercore/web-site-templates/server-time.php
But the PHP in the file server-time.php creates different HTML each time.
server-time.phpHere’s what’s in the file server-time.php on the server’s hard disk:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Server time</title>
</head>
<body>
<p>The time at the server is
<?php
print date('h:i:s A');
?>
</p>
</body>
</html>
Figure 4. server-time.php
Lines 1 to 8 and 12 to 14 are regular HTML. The PHP interpreter just passes them back to the server unchanged. The magic happens in lines 9 to 11.
Let’s make sure we understand what I wanted the page to show. I wanted the server to send back HTML that looked like this:
<p>The time at the server is (time goes here)</p>
When you program in PHP, you are programming indirectly. You write PHP code that creates HTML, or text that gets
embedded in HTML. The HTML then gets returned to the browser for rendering. So remember:
You write PHP that writes HTML.
Line 9 is the PHP opening tag: <?php. It tells the PHP interpreter that some PHP is about to appear. The interpreter runs the code until it hits the close tag in line 11: ?>.
Line 10 is:
print date('h:i:s A');
print is a PHP statement that says “Output what follows.” date() is a PHP function that gets the current date and time, and returns it. The stuff in the parentheses (()) is a format, telling date how to show the date and time. In this case, the hours, then a colon (:), then the minutes, then another colon, then the seconds, then a space, and then an AM/PM indicator.
The statement ends with a semicolon (;). All statements in PHP end this way. If you leave off the semicolon, the PHP interpreter will get confused, and complain.
The result? The print statement outputs the current time.
Upload the server time program on to your own server. One way is to:
coredogs/servercore/web-templates/server-time, and save the file there. coredogs is this site, servercore is the book on CoreDogs, web-templates is the chapter in the book, and server-time is the exercise in the chapter. It seems like a lot of typing, but it’s very important to keep your files organized.Name the file whatever you want. If you name it index.php, it will be the default file in that directory. You can read more about default files.
If you installed XAMPP on your computer, and you put the file somewhere under your htdocs directory, you can access it through your browser. For example, suppose you saved the file as:
C:\xampp\htdocs\coredogs\servercore\web-templates\server-time\server-time.php
This is a file path for a Windows machine. If you use a Mac or Linux box, your path will have a slightly different format.
Remember that C:\xampp\htdocs\ is the root of the Web server on your computer.
Make sure your Apache server is running (use the XAMPP control panel to start it). In your browser, type the URL:
http://localhost/coredogs/servercore/web-templates/server-time/server-time.php
http://localhost/ maps to C:\xampp\htdocs\. The rest of the path maps to your PHP file.
Once the program works, upload the file to your hosting account, and point your browser there. (If you didn’t install XAMPP on your computer, you will need to upload the file before you can test it.) I recommend that you use the same file structure on your hosting account that you used on your own computer.
Now, change the page. Add an <h1> that says something like:
Renata’s first PHP program
Of course, replace Renata with your own name.
Upload the new version. Enter the URL of your first PHP program below.
(Log in to enter your solution to this exercise.)
Write a PHP page that will show the IP addresses of your browser, and the server the page is running on.
You can get the IP addresses with:
$_SERVER['REMOTE_ADDR']
$_SERVER['SERVER_ADDR']
Hint: base this on the server time example.
You can run my solution.
Upload your solution to your server. Put the result below.
(Log in to enter your solution to this exercise.)
.php extension, it sends the file to the PHP interpreter.Outputting stuff will help you create dynamic Web templates. The next thing you need to know is how to insert one file inside another.
Onward! To glory!
You saw how the print statement puts data into the HTML. Let’s see how you can put entire files into the HTML.
By the end of this lesson, you should:
PHP has several statements that insert files: include, require, and require_once. We’ll just talk about one of them: require.
Suppose we create a file that contains HTML to show a footer for every page on a Web site. Here it is:
<div id="footer"> Copyright © 2010 | All rights reserved </div>
Figure 1. Footer code.
This is a complete HTML page, just a fragment of HTML. It wouldn’t work by itself. Let’s call the file footer.inc. The .inc reminds us that the file is meant to be included in another file.
Here’s the PHP page include-footer.php:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Including a footer</title>
</head>
<body>
<h1>Hi there!</h1>
<p>I am a page. Aren't I wonderful?</p>
<?php
require 'footer.inc';
?>
</body>
</html>
Figure 2. Including a footer
The require statement reads the file it’s given, and inserts it. Any PHP in the included file is executed. If the PHP interpreter can’t find the file, it shows an error message.
You can try it. Make sure you have a look at the HTML source. As far as the browser knows, the content of footer.inc was typed directly into include-footer.php.
The code in Figure 2 is in a file called include-footer.php. It has the line:
require 'footer.inc';
There is no directory given, just a file name. Oo where will the PHP interpreter look for footer.inc?
The answer: in the same directory as include-footer.php. So if include-footer.php is in /my/directory/of/doom/, then the PHP interpreter will look for /my/directory/of/doom/footer.inc.
When you’re using dynamic Web templates for real, this won’t work very well. Suppose you were creating a site with this structure:

Figure 3. Site structure.
The file at /index.php is the home page of the site. The file at /articles/index.php shows a list of articles. There are other things like blog entries, products, and so on, but we don’t need to consider them here.
Suppose you put this line into both /index.php and /articles/index.php:
require 'footer.inc';
You would need one footer.inc for /index.php and another for /articles/index.php:

Figure 4. Duplicate footer files.
But what’s the point of that? If we wanted to change the footer text across the entire site, we’d have to change all of the footer.inc files.
What we really want is for both /index.php and /articles/index.php to refer to the same footer.inc.

Figure 5. Just one footer file.
footer.inc is in a directory called library. PHP developers often move shared files to a directory like this, and call it library, lib, includes, common, or something like that.
But we have to change the require statements to make this work. Here is the require statement from /index.php:
require 'library/footer.inc';
Here is the one for /articles/index.php:
require '../library/footer.inc';
The .. means “go up a level.”
To brush up on how to use paths to navigate between directories, see the discussion of absolute, relative, and root relative links.
Let’s see how this would work in an entire Web site.
Let’s create a simple site called The Dog Site. It has a home page and two sections: dog profiles, and articles.

Figure 6. Site overview.
Each of the two sections of the site – dog profiles and articles – has a main page. Each one is linked from the home page.
Here is the home page:

Figure 7. Home page.
Here is the main page for the dog profile section:

Figure 8. Dog profile main page.
Here is the main page for the articles section:

Figure 9. Articles main page.
You can try the site.
Let’s create the directory tree for the site, that is, the directories on the server that all the files will go into. We’ll make a directory for each section of the site: dog profiles and articles.
The header is the same on every page, so let’s pull that out and put it in its own file. Here’s the code:
<!-- Header file, to be included into every page. --> <h1>The Dog Site</h1> <h2>Bringing you happiness since last week</h2> <hr>
Figure 10. HTML for the header.
Let’s put the code into the file header.inc, and put the file into a library directory.
Here is the directory tree so far.

Figure 11. Initial tree.
The home page is index.php. Recall that if a browser sends a URL to a Web server, and the URL does not have a file name, then the browser uses a default name. In ClientCore, we used index.html as the default. index.php can be the default as well.
So if a browser asks for:
http://dogthing.com/play/
the server will run:
http://dogthing.com/play/index.php
Let’s see what the tree looks like when all the files are added:

Figure 12. Complete tree.
All of the PHP files are going to have a require statement, to insert header.inc. But since the PHP files are in different directories, they will use different paths to navigate to header.inc. Here are the paths for some of the files.
| File | Path in require |
|---|---|
| /index.php | library/header.inc |
| /articles/index.php | ../library/header.inc |
Figure 13. Paths in some of the files.
Complete figure 13. Give the path in the require statement for every PHP file.
You can check your solution by downloading a zip file of the site. Expand the zip file to see the PHP code.
(Log in to enter your solution to this exercise.)
Download and expand this zip file. It will create a directory tree like this:

Figure 1. Directory tree
Open up wombat.php. Add require statements to create a page that looks like this:

Figure 2. Output
Use only relative file paths.
Upload the entire tree to your server. Put the URL below.
You can see my solution, but try it yourself first.
(Log in to enter your solution to this exercise.)
In this lesson, you learned:
require statement.But there’s a problem. It’s easy to break links to images and other resources. Let’s fix that in the next lesson.
You’ve seen how to use the require statement to insert one file into another. But do it the wrong way, and your site won’t work. Let’s look at the problem and a solution.
By the end of this lesson, you should:
Let’s look again at the dog site. Remember that it has two sections: articles and dog profiles. Here’s the directory tree:

Figure 1. Directory tree
The file header.inc is inserted in all of the other PHP files. For example, here’s how it is inserted into playing-with-dogs.php:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Playing with dogs | The Dog Site</title>
</head>
<body>
<?php
require '../library/header.inc';
?>
<h2>Article: Playing with dogs.</h2>
<p>Pretend there is some content here.</p>
<p><a href="index.php">Back to the article list</a></p>
</body>
</html>
Figure 2. Code for playing-with-dogs.php
Line 9 inserts a header file that is the same across the entire site. The path says “go up to the parent directory, then down into the library directory, then insert the file header.inc.”
Here’s the header:
<!-- Header file, to be included into every page. --> <h1>The Dog Site</h1> <h2>Bringing you happiness since last week</h2> <hr>
Figure 3. HTML for header.inc
You can try the site. You can also download a zip file with all of the PHP and other files.
The client wants to add the following logo to the header of every page:

Figure 4. Logo
“No problem,” we say. We put the image file (logo.png) in the library directory.

Figure 5. Logo in the library directory
We change header.inc to:
<!-- Header file, to be included into every page. --> <h1><img src="logo.png" alt="Logo">The Dog Site</h1> <h2>Bringing you happiness since last week</h2> <hr>
Figure 6. First try at the new header.inc
But this doesn’t work. Look at the home page. You’ll see something like:

Figure 7. Broken logo
Exactly what you see depends on how your browser handles broken image links.
So what’s the problem? Tell your browser to show you the HTML of the page (Control+U, or right click), and you’ll see:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Welcome | The Dog Site</title>
</head>
<body>
<!-- Header file, to be included into every page. -->
<h1>The Dog Site</h1>
<h2><img alt="Logo" src="logo.png">Bringing you happiness since last week</h2>
<hr> <p>Welcome to the dog site.</p>
<p>See some <a href="dog-profiles/index.php">dogs</a>.</p>
<p>Read an <a href="articles/index.php">article</a>.</p>
</body>
</html>
Figure 8. HTML of index.php with broken logo
Again, the layout is a little strange around line 9, because the HTML here is inserted by PHP.
Line 10 contains the logo HTML. It’s exactly what we put into header.inc. So what’s the problem?
It has to do with the directory that logo.png is in. The file is in the library directory (See Figure 5). But the browser gets this code:
<img alt="Logo" src="logo.png">
There is no path, just logo.png. So the browser tries to load the image from the same directory that index.php is in.
index.php is in the root directory of the site (/). So the browser tries to load /logo.png.
Oops.
Every page on the site has the same problem. For example, /articles/why-dogs-are-great.php also has the line:
<img alt="Logo" src="logo.png">
inserted by the PHP require statement. The browser tries to load logo.php from the same directory as why-dogs-are-great.php. So it tries to load /articles/logo.png.
Argh!
How to fix it?
One choice is to copy logo.png to every directory, but that’s a really bad idea. If we wanted to change the logo for the site, we’d have to find and change every copy of logo.png.
A better choice (but not the best) is to change the inserted code to:
<!-- Header file, to be included into every page. --> <h1><img src="/library/logo.png" alt="Logo">The Dog Site</h1> <h2>Bringing you happiness since last week</h2> <hr>
Figure 9. Root relative path in header.inc
The src attribute on line 2 has changed. It has the full path to the logo. This is a root relative URL. It says “Go to the root of the Web site, then go down into the library directory, then find logo.png.”
This works! But…
Root relative URLs cause their own problems. They reduce the portability of a site, that is, your ability to move all of the files in the site around.
Remember that part of CoreDogs is not just learning the tech, but learning to think like a Weber. That is, to understand how people who create Web sites do their work.
On just about every project, you want to have separate a test version of the site. You tell your client “go look at the test version to see what I’m doing.” Then you can talk about changes.
Let’s say you put a test version of the site on http://mytestsite.com/dogsite/. You also have test versions of other sites, like http://mytestsite.com/catsite/ and http://mytestsite.com/hipposite/. This is common Weber practice; keep test versions on different domains from the product site.
But there’s a problem. You’ve moved the files into a directory called dogsite. The root relative path of the logo file is now /dogsite/library/logo.png. Your library file (header.inc) has /library/logo.png. So you have to change it to /dogsite/library/logo.png. Argh!!
But every time you copy the test version of the site to the production version, you have to remember to change back every root relative path. Double argh!!
And you have to do this for every root relative path! Not just the logo, but CSS files, JavaScript files, ...
Triple argh!!!
There are ways around this, too, but the fact is that root relative URLs are a pain.
But there’s a good solution. One that works well for template-based, dynamic Web sites.
What we’re going to do is change the HTML code that header.inc inserts. Instead of just src="logo.png", it will insert src="library/logo.png", or src="../library/logo.png", or src="../../../library/logo.png", or whatever each page needs.
We’ll do this by creating a variable on each page, and having header.inc use that variable.
If you remember back to JavaScript, you learned that a variable is a piece of computer memory that’s given a name. For example, here’s some JavaScript that takes whatever the user typed into a form field, puts it into the variable user_name, and tests whether the variable is empty.
user_name = $("#user").val();
if ( user_name == "" ) {
alert("Sorry, you must enter a user name.");
}
Figure 10. JavaScript variable
PHP has variables, too. They act much like JavaScript variables. One difference is that their names all begin with $ (a dollar sign).
Our solution to the logo problem is in two steps:
header.inc.Let’s have a look. Here’s the directory tree again.

Figure 5 (again). Logo in the library directory
See the file playing-with-dogs.php in the articles directory? Here is the new version:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Playing with dogs | The Dog Site</title>
</head>
<body>
<?php
$path_to_root = '..';
require $path_to_root . '/library/header.inc';
?>
<h2>Article: Playing with dogs.</h2>
<p>Pretend there is some content here.</p>
<p><a href="index.php">Back to the article list</a></p>
</body>
</html>
Figure 11. Code for playing-with-dogs.php
Line 9 creates a variable called $path_to_root. Remember that variables begin with $ in PHP. The string “..” is put into the variable. You can put any characters you like into a variable. You can put numbers, too, and other things.
Notice that the statement ends with a semicolon (;). All statements must have a semicolon at the end. JavaScript is forgiving about this. PHP is not.
Line 10 is:
require $path_to_root . '/library/header.inc';
The period (.) is PHP’s string concatenation operator. It sticks two strings together. So 'Big' . 'ger' would yield 'Bigger'.
JavaScript uses plus (+) to mean the same thing, but if you remember, that causes problems, because + in JavaScript also means “add two numbers together.” PHP doesn’t have that problem. PHP has two operators (. and +) that mean two different things.
Let’s look again:
$path_to_root = '..'; require $path_to_root . '/library/header.inc';
Part of Figure 10 (again). Code for playing-with-dogs.php
.. is the path from playing-with-dogs.php to the Web root. /library/header.inc is the path from the Web root to the header file. Put them together, and you get ../library/header.inc, which is the path from playing-with-dogs.php to the header file.
So far, so w00f. As long as $path_to_root is set correctly for every page, the header file will be found correctly.
Note that this variable is declared in files that other files (like the header) are inserted into. These are the files with require statements, not the files that are inserted by the require statements.
What about the files that are inserted? They use the variable that was declared. Let’s look at the new header.inc.
<!-- Header file, to be included into every page. --> <h1><img src="<?php print $path_to_root; ?>/library/logo.png" alt="Logo">The Dog Site</h1> <h2>Bringing you happiness since last week</h2> <hr>
Figure 12. Code for header.inc
Remember that the print statement outputs stuff into the HTML stream. It can output anything, anywhere. Including file paths!
If $path_to_root has ‘..’ in it, then line 2 will produce:
<h1><img src="../library/logo.png"…
As before, $path_to_root has the path from the file that the code in Figure 12 is inserted into, to the Web root. /library/logo.png is the path from the Web root to the logo file. Put them together, and you have the path from the page the header code is inserted into, to the logo.
You can try this version of the site. Look at the HTML for a few pages, and check out the paths to the logo. By the way, you know that “..” means “go up one level to the parent.” “.” means “stay where you are.” You’ll see that on the home page.
You can download a zip file of the site if you want.
This approach works for all paths, whether they’re for images, links, CSS files, JavaScript files, or anything else. To see that, suppose we wanted to add a contact page to the site, with a link from the header of every page:

Figure 13. Link to contact page in header
The contact page is at /contact.php, that is, in the Web root, along with the site’s home page.

Figure 14. Contact page in directory tree
We can add a contact link to every page on the site by only changing header.inc! W00f!
Here is what the new header.inc would look like.
<!-- Header file, to be included into every page. --> <h1><img src="<?php print $path_to_root; ?>/library/logo.png" alt="Logo">The Dog Site</h1> <h2>Bringing you happiness since last week</h2> <p><a href="<?php print $path_to_root; ?>/contact.php">Contact us</a></p> <hr>
Figure 15. New code for header.inc
Line 4 does the trick. $path_to_root contains the path from the page the code is inserted into to the Web root. The contact page is right there in the Web root; that’s just /contact.php.
You see how we could add the contact link by changing only one file? It wouldn’t matter how many pages were in the site. 10? 100? 10,000? Change just one file, and everything gets changed.
This is a Big Win for someone who works on Web sites, especially someone who does it professionally. Webers don’t think just about the end result, that is, the site that users will see. They also think about the work processes that create that site.
Webers think about productivity. The more productive they are, the better value they give employers and clients.
Modify your solution to the first Wombat exercise. Add a variable to wombat.php that has the path to the Web root. Use the variable in all the require statements.
Upload the file to your server. Put the URL below.
You can see my solution, but try it yourself first.
(Log in to enter your solution to this exercise.)
Using a variable like $path_to_root works with all kinds of paths in the HTML. We’ve fixed paths to images (the logo) and links (the contact page) in this lesson.
But there are often paths in JavaScript code. To images, for example. We need to fix them as well. How?
In the previous lesson, we saw how to use a PHP variable to keep links and images working in a dynamic templating system. We got the Big Win of being able to reuse things like page headers on all pages of a site.
Let’s look at reusing JavaScript files. The goal is the same. Have one JavaScript file that’s included across all pages. If we want to change the JavaScript, change that one file, and every page is changed.
By the end of this lesson, you should:
Here’s a page with an image and a link.

Figure 1. Sample page
Try it, but ignore the Another link for the moment. (We’ll get back to it.)
Move the mouse over the image, and it changes. Move the mouse out, and it changes back. There are two different images. They are swapped in and out when the mouse moves over an <img> tag (more on that later).
Here’s the directory tree for the site.

Figure 2. Tree
There are two pages. One is /index.html, the one we’ve been looking at. There’s another page, /another/index.html. We’ll get to that later.
The images are in the library directory. The JavaScript that handles the image changing is in the file js-example.js. It’s in the library directory as well.
Why are the files in a separate library directory? Because they’re going to be reused across the site.
Here’s the HTML. No PHP yet.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="library/js-example.js"></script>
<title>JavaScript Example - Broken</title>
</head>
<body>
<h1>JavaScript Example - Broken</h1>
<h2>Home</h2>
<p><img id="message" src="library/welcome.png" alt="Welcome"></p>
<p><a href="another/index.html">Another</a></p>
</body>
</html>
Figure 3. HTML
Look at line 12. There’s the <img> tag that shows the current image. It has an id of message.
The JavaScript that changes the image is in the file /library/js-example.js, referenced in line 6.
(Line 5 is a reference to the jQuery library on a Google server. It uses an absolute URL, and is not affected by the templating system.)
Here’s the contents of /library/js-example.js. This is the JavaScript code that does the image switching.
$(document).ready(function() {
$('#message').hover(
function(){
$('#message').attr('src', 'library/go_away.png');
},
function(){
$('#message').attr('src', 'library/welcome.png');
}
);
});
Figure 4. JavaScript
When the mouse goes over the image, the <img> tag shows go_away.png (line 4). When the mouse leaves the image, it’s set back to welcome.png (line 7).
So far, so good. Now try the page again. But this time, follow the Another link. This loads the page /another/index.php. As you can see in Figure 2, it’s in a subdirectory.
The image shows up. But when you move the mouse over the image, go_away.png doesn’t appear. What’s the deal?
It’s the same problem we had on the previous lesson. It’s the file paths. Remember, you’re looking at the file:
/another/index.html
This JavaScript executes:
...
attr('src', 'library/go_away.png')
library/go_away.png is a relative path, so the browser looks for the file relative to the current page. It tries to load:
/another/library/go_away.png
But the file is not here.
Argh! Yet again!
You can download all the files from this example.
Let’s look at a fix that uses just JavaScript, not PHP. It uses a similar approach, though: Set a variable with the path to the root.
Let’s add a few lines to /index.html. This is the first page we saw, the one that worked. Here is the new head section of the page.
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="library/js-example.js"></script>
<script type="text/javascript">
var path_to_root = ".";
</script>
<title>JavaScript Example - Fixed with JavaScript</title>
</head>
Figure 5. JavaScript fix
Lines 7 to 9 are new. Line 8 creates a JavaScript variable called path_to_root. It’s set with – guess what? – the path from /index.html (the file the code is in) to the root of the Web site. /index.html is in the root directory. “.” means “stay right here.”
We change /another/index.php as well, the page that didn’t work. Here’s what it changes to:
<script type="text/javascript"> var path_to_root = ".."; </script>
Figure 6. JavaScript fix for another file
The “..” says, “To get to the root, go up to the parent.”
Let’s say we had the file hippo/llama/lion/dog.html. It’s code would be:
<script type="text/javascript"> var path_to_root = "../../../"; </script>
Figure 7. A long path
dog.html is in the subdirectory lion. Line 8 says, “To get to the root, go up a level, go up a level, and go up a level.”
So, on each page, we create a JavaScript variable with the path from that page to the root of the Web page.
Now, what do we do with that variable? Let’s change /library/js-example.js. Remember that’s the file with the JavaScript code that handles the image switching. Here’s the new code:
$(document).ready(function() {
$('#message').hover(
function(){
$('#message').attr('src', path_to_root + '/library/go_away.png');
},
function(){
$('#message').attr('src', path_to_root + '/library/welcome.png');
}
);
});
Figure 8. New JavaScript
path_to_root has the path from the page to the root. /library/go_away.png is the path from the root to the new image file. So:
path_to_root + '/library/go_away.png'
is the path from the page to the new image file.
You can try it. You can also download all the files. Note that everything works just fine.
W00f!
We have a JavaScript line like this, to get the JavaScript code in the library files to work right:
var path_to_root = "..";
We did the same thing in the last lesson, to get the file paths in the library HTML files (like the header and footer) to work right. But we used PHP:
$path_to_root = '..';
We could combine them, and get rid of the duplication. We’d end up with something like this for the head section of a page:
<head>
<?php $path_to_root = '..'; ?>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="../library/js-example.js"></script>
<script type="text/javascript">
var path_to_root = "<?php print $path_to_root; ?>";
</script>
<title>JavaScript Example - Fixed with JavaScript</title>
</head>
Figure 9. PHP and JavaScript
Line 4 sets a PHP variable with the root path. Line 9 puts that into a JavaScript variable. With this approach, we only need to remember to set the PHP variable. The JavaScript variable will be set correctly.
/library/js-example.js doesn’t change. The variable it needs – path_to_root – is still set correctly.
For this to work, you need to rename the HTML files, giving them a .php extension. Remember that PHP statements are only executed in files that have the extension .php. Here is the new directory tree:

Figure 10. New tree
You can try this approach. You can also download the files.
Notice what you are doing here. Look at this again.
<script type="text/javascript"> var path_to_root = "<?php print $path_to_root; ?>"; </script>
Part of Figure 9 (again). PHP and JavaScript
This puts something like this in the code sent to the browser:
<script type="text/javascript">
var path_to_root = "..";
</script>
That we are doing is using PHP code to write JavaScript code!
This is so weird. But it’s done all the time. Browsers know how to run JavaScript, just as they know how to display HTML. We take advantage of this. We write PHP that writes JavaScript, just as we write PHP that writes HTML. The browser then does its thing with what we send it.
This is a good example of how computers work in layers. The PHP layer creates stuff for the HTML/JavaScript layer.

Figure 11. Layers
Each layer has languages it understands. The layer underneath it creates stuff in those languages.
Try this site with a broken logo animation. Move the mouse over the logo image. It works on the first page, but not the others.
Download a zip file of the site. Fix it, using PHP and JavaScript variables.
You can check my solution and download a zip file of it.
But try it yourself first!
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
Let’s see where we are.
require statement.Let’s put this all together, and create a complete dynamic templating system for a Web site.
You know how to use require statements. You know how to use variables to fix broken paths.
Now let’s put it all together. Let’s make a site that has a complete PHP templating system.
By the end of this lesson, you should:
Let’s remake the dog site we looked at a couple of lessons ago. Here is its overall structure:

Figure 1. Site structure
There are two sections in the site: dog profiles and articles. The profiles section has profiles of several dogs. The articles section has articles on dog issues.
Here is the home page of the new site.

Figure 2. Home page
Note that the buttons on the nav bar match the site structure in Figure 1.
The dogs link on the home page, and the dogs button in the nav bar, link to the main page of the dogs section of the site. Here it is:

Figure 3. Main page of the dogs section
Each link shows a page about a dog. Here’s a sample:

Figure 4. Dog profile
Each dog profile has a photo of the dog.
The other section of the site in Figure 1 is the articles section. Here is its main page:

Figure 5. Main page of the articles section
The page has links to each article. Here’s one of them.

Figure 6. An article
There is one other page: the contact form. A link to it is in the footer of every page. Here it is:

Figure 7. Contact page
You can try the site yourself. You can also download the site.
Each page has the same layout:

Figure 8. Page layout
There are four regions on the page: top, left, center, and bottom. There is no right region in this layout.
The top region has a header. It contains two images (the logo and header image), and some text (a site subtitle). The content of the header – the images and text – is the same on every page.
The left region has a nav bar that is the same on every page. It has three images. The images switch when the mouse hovers over them:

Figure 9. Mouse hover effect
The bottom region has a footer that is the same on every page. It contains some text, and a link to the contact page.
The center region has the main content for the page. It is different for each page.
Let’s open up the HTML for a page, and see how it is organized.

Figure 10. HTML organization
Inside the body tag are the four regions. Each has each own div. The colors match the ones in Figure 8.
Figure 10 uses code adapted from the liquid layout in the page layout lesson. The right region was removed, and widths, margins, and padding adjusted.
There’s some more HTML at the start and end of the page. The code at the start gives meta data like title, loads CSS and JavaScript files, and includes any page-specific CSS and JavaScript. The code at the end of the page just closes the body and html tags, although it might do more on other sites.
The HTML for a page is not stored in one file. A key point to remember is that the HTML in Figure 10 is assembled by PHP code.
The HTML is broken up into pieces. Most of the pieces are shared by all pages. The header, for example, is the same across the site (apart from adjusting file paths). The only thing that is completely different for each page is the content.
That’s what gives us the Big Win. There will be one file containing the HTML for the header, no matter how many pages are on the site. 100 pages? One header file. 10,000 pages? One header file.
Change that one file, and every page on the site changes. All 100. Or 10,000. A big productivity win.
Where are we? You’ve seen:
Let’s see what the site’s directory tree looks like.
Here it is:

Figure 11. Site directory tree
The root directory of the site just has two files: index.php and contact.php. All of the other files are in directories.
There is a directory for each major part of the site in Figure 1.
Why create a directory for each part of the site? This is another example of how Webers think about productivity. Suppose a user sends this email:

Figure 12. Email about spelling error
We want to be able to fix that error quickly and easily. Notice how easy it is to find the file with the error. All the articles are in the articles directory, so go there. The names of the files match the titles of the articles, so we open the file playing-with-dogs.php.
On the other hand, suppose we’d put all of the files in one directory, and named them things like file17.php. It would be harder to the file with the spelling error.
So the organization in Figure 11 gives us another productivity win. W00f!
OK, back to the files. Here is the tree again.

Figure 11 (again). Site directory tree
All of the shared files are in library. The file dogsite.css is a site-wide CSS file, with the colors, fonts, page layout style rules, etc. dogsite.js has the JavaScript code for the nav bar image switching.
The .inc files contain the HTML pieces for each page. There’s one file for each chunk in Figure 10:
start_page.inc)header.inc)left_nav.inc)footer.inc)end_page.inc). We’ll look at the code for each one in a moment.
The rest of the files in library are images for the header and the nav buttons.
Now let’s open up /index.php (the home page), and look at the PHP. Here is the entire code:
<?php //Path from this page to the site root. $path_to_root = '.'; //Title of this page. $page_title = 'Welcome'; require $path_to_root . '/library/start_page.inc'; require $path_to_root . '/library/header.inc'; require $path_to_root . '/library/left_nav.inc'; ?> <div id="center_region"> <h1>Welcome</h1> <p>See some <a href="dog-profiles/index.php">dogs</a>.</p> <p>Read an <a href="articles/index.php">article</a>.</p> </div> <?php require $path_to_root . '/library/footer.inc'; require $path_to_root . '/library/end_page.inc'; ?>
Figure 13. PHP for /index.php (the home page)
Line 2 is a comment. Comments begin with //.
Line 3 sets the variable $path_to_root. It contains the path from this file to the site root. /index.php is already at the root, so the variable is set to '.', which means “the current directory.”
Line 5 sets the variable $page_title to, well, the page title. This is used in the title tag, as we’ll see.
Lines 6 to 9 insert chunks of HTML. start_page.inc (line 6) goes down to the body tag. header.inc (line 7) is the page header. left_nav.inc (line 8) is the nav bar.
Line 9 ends the PHP code. Regular HTML follows.
Lines 10 to 14 are the main content of the page, in the center region. This is what varies the most across the pages of the site.
Line 15 starts PHP mode again. Line 16 inserts footer.inc, with the HTML for the page footer. Line 17 inserts end_page.inc, with the HTML to close the body and html tags.
Let’s have a look at /dog-profiles/renata.php, the page showing Renata’s profile. Here’s what it looks like in a browser:

Figure 4 (again). Dog profile
Here’s the code.
<?php //Path from this page to the site root. $path_to_root = '..'; //Title of this page. $page_title = 'Renata'; require $path_to_root . '/library/start_page.inc'; require $path_to_root . '/library/header.inc'; require $path_to_root . '/library/left_nav.inc'; ?> <div id="center_region"> <h1>Dog profile: Renata</h1> <p>Here is Renata.</p> <p><img src="renata.jpg" alt="Renata"></p> <p><a href="index.php">Back to the dog list</a></p> </div> <?php require $path_to_root . '/library/footer.inc'; require $path_to_root . '/library/end_page.inc'; ?>
Figure 14. PHP for /dog-profile/renata.php
Most of it is the same as the code for /index.php, in Figure 13. Let’s look at the differences.
Line 3 has the path to the root. /dog-profile/renata.php is in a subdirectory, so its path is “..”.
Line 5 has the page title. It is different, of course.
The content for the center region starts at line 10. That’s completely different from page to page, of course.
And that’s it. Those are the only differences.
Notice how easy it is to create a new page. Copy one of the existing pages, change the root path and title, and add the unique content. The header, footer, and nav bar are all set up.
This is another productivity gain. Creating new pages just got a lot simpler.
Let’s look inside the included files.
Here’s start_page.inc, the HTML up to the body tag (see Figure 10).
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><?php print $page_title; ?> | The Dog Site</title>
<link rel="stylesheet" type="text/css" href="<?php print $path_to_root; ?>/library/dogsite.css">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var path_to_root = '<?php print $path_to_root; ?>';
</script>
<script type="text/javascript" src="<?php print $path_to_root; ?>/library/dogsite.js"></script>
</head>
<body>
Figure 15. start_page.inc
The page title is added in line 5. The root path is used in lines 6, 9, and 11. The rest is just plain HTML.
Here’s the code for header.inc.
<div id="top_region">
<div id="logo_images">
<img src="<?php print $path_to_root; ?>/library/logo.png" alt="Logo">
<img alt="The Dog Site" src="<?php print $path_to_root; ?>/library/header.png">
</div>
<div id="subtitle">Bringing you happiness since last week</div>
</div>
Figure 16. header.inc
The root path is used on lines 3 and 4, to get the right paths to the image files.
Here is left_nav.inc.
<div id="left_region">
<ul class="vertical_menu">
<li>
<a href="<?php print $path_to_root; ?>/index.php">
<img id="home_button" src="<?php print $path_to_root; ?>/library/home_up.png" alt="Home">
</a>
</li>
<li>
<a href="<?php print $path_to_root; ?>/dog-profiles/index.php">
<img id="dogs_button" src="<?php print $path_to_root; ?>/library/dogs_up.png" alt="Dogs">
</a>
</li>
<li>
<a href="<?php print $path_to_root; ?>/articles/index.php">
<img id="articles_button" src="<?php print $path_to_root; ?>/library/articles_up.png" alt="Articles">
</a>
</li>
</ul>
</div>
Figure 17. left_nav.inc
Each link and image needs to be adjusted for the root path.
Here’s footer.inc.
<div id="bottom_region"> <p>© 2010 Nobody at all | <a href="<?php print $path_to_root; ?>/contact.php">Contact</a></p> </div>
Figure 18. footer.inc
The path to the contact page needs the root path. Nothing else changes.
Hey, wait a minute.
What’s up?
Well, I’m just thinking. This seems awfully complex. Is it worth the effort?
Yes, I agree. I’m not sure I’ll like PHP.
Good point. Let’s talk about that a bit.
Have a look at the directory tree again.

Figure 11 (again). Site directory tree
There are lots of pieces here. Look at the library directory. There are fifteen files in there! Ack!
But let’s look more closely. Six of the files are button images. Another two are images for the header region. Nothing to do with PHP there, just regular images.
The files dogsite.css and dogsite.js have nothing to do with PHP either. They’re just regular things we saw in ClientCore.
What about the five .inc files? They are mostly plain HTML. There is only one PHP statement in them: print. As in:
<?php print $path_to_root; ?>
In fact, there is only one other type of PHP statement in the entire site. That’s require, as in:
require $path_to_root . '/library/start_page.inc';
The complexity comes not from the PHP. It comes from the underlying complexity of HTML, CSS, and JavaScript itself.
Web sites are complex. There is no doubt about it. That’s why people pay other people to create them.
The complexity comes not from the fact that any one thing is complex. But even in a small site, there are dozens of things to get right. Each of those small things combines to make a complex whole.
Don’t feel bad if this seems messy to you. It is messy. There’s a lot going on. But it’s your mastery of this that will make you a valuable employee, consultant, or whatever.
And one more thing about complexity. We added the PHP not to make the Web site look better. We added it to make it easier to change. Not only do we have to complexity of HTML, CSS, JavaScript, jQuery, and PHP. We have the complexity of work processes as well.
Ack! It’s enough to drive you to catnip.
There are some big productivity wins here. Let’s review.
We want a new page, like a new article. We just copy /index.html (or another of the PHP files), change the root path and the title, and add the new content. We just work on what is unique on that new page. We don’t even have to think about the header, nav bar, etc.
Remember the email we looked at earlier, the one that told us about the spelling error? We could find the file to change very easily, because:
This has nothing to do with PHP. It’s just another example of how Webers think. Organize now to reduce problems later.
We can change the entire site by changing one file. For example, let’s say we wanted to add a new section to the site, for products. We add a new button to left_nav.inc, and every page in the site has the new button!
This is a Big Win. Your employer or client will be glad they hired you, because you give good value for money.
In this lesson, you:
Time to do some more exercises.
Create a Web site about old things, like this one. Use the same method we used in the complete template system.
Download the images from the site.
You can download a zip file of my solution, but try it yourself first.
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
One of the most important uses of PHP is processing form data. This chapter shows you how to do it.
The next three chapters are about forms. This chapter starts off looking at the HTML for forms. Then it shows you how to do simple things with form data in PHP.
The next chapter looks at a couple of ways of saving form data: sending it by email, and saving it to a file.
The final form chapter talks about validation, that is, how you check data that users put into forms. We also look at how to make forms look good.
Let’s start by reviewing HTML form tags.
By the end of this lesson, you should:
get and post.<form>, <input>, and <button> tags.Remember HTTP? That’s the protocol that Web browsers and servers use to talk to each other.
There are various types of HTTP messages that browsers and servers send to each other. A common one is GET. This is usually how a browser asks a server for a page.
For example, suppose a user is looking at this page at http://dogsite.com/articles/index.php:

Figure 1. Articles list
Here’s part of the HTML for the page:
<h1>Articles</h1> <ul> <li><a href="why-dogs-are-great.php">Why dogs are great</a></li> <li><a href="playing-with-dogs.php">Playing with dogs</a></li> </ul>
Figure 2. HTML for articles list
The user clicks the link “Playing with dogs.” The browser contacts the server at dogsite.com, and sends:
GET /articles/playing-with-dogs.php HTTP/1.1
The server sends back the HTML for playing-with-dogs.php to the browser.
When the browser sends a message like GET to the server, the browser sends other data as well, besides the URL. For example, it tells the server what type of browser it is.
GET /articles/playing-with-dogs.php HTTP/1.1
Host: dogsite.com
User-Agent: Mozilla/5.0
Host and User-Agent are headers. Each one is on a different line.
There are many headers types besides Host and User-Agent. They specify the date and time the request was sent, languages the browser would prefer, and other stuff. A typical GET might have a half dozen headers.
Here’s the important bit for this chapter: the total HTTP request – the URL and the headers – can include data the user typed into form fields. This gives a way for browsers to send data to servers (usually it’s the other way around).
When the user submits a form in a Web browser, the form data travels along with a URL.

Figure 3. Form data attached to URL
So, the form data is sent along with a URL. But which URL? What page does the browser send the data to?
The browser sends form data to a page that is specially written to handle form data. The page knows how to extract the data, and do something with it, like save it to a database. That’s one of the things you do in PHP: write pages that can handle form data.
More on that later. Right now, we’re just looking at the form.
Let’s create a form like this:

Figure 4. Simple form
There are two text fields, and a button. Clicking the button submits the form. This means that the browser sends the form’s data to the server.
Here’s the HTML:
<h1>Simple Form</h1>
<form action="process-simple-form-get.php" method="get">
<p>First name:
<input name="first_name" type="text" size="20">
</p>
<p>Surname:
<input name="surname" type="text" size="20">
</p>
<p>
<button type="submit">Save</button>
</p>
</form>
Figure 5. Simple form HTML
The form is wrapped in a <form> tag. This one has two attributes: action and method. There are other attributes we could use, but we don’t care about them yet.
The action says where the data should be sent. Remember that form data gets attached to a URL. This is the URL the data gets attached to.
The method attribute says how the form data will be attached. There are two ways: get and post.
get methodget tacks the form data on to the URL itself. Suppose the user filled in the form like this:

Figure 6. Simple form with data
The browser would create this URL:
/process-simple-form.php?first_name=Buffy&surname=Summers
Why this URL? Let’s look at the HTML again.
<h1>Simple Form</h1>
<form action="process-simple-form-get.php" method="get">
<p>First name:
<input name="first_name" type="text" size="20">
</p>
<p>Surname:
<input name="surname" type="text" size="20">
</p>
<p>
<button type="submit">Save</button>
</p>
</form>
Figure 5 (again). Simple form HTML
The main part of the URL is in the form’s action property: process-simple-form.php. The browser adds a ? on the end, to indicate the start of the form data.
Then it adds the data in form fields. There’s the name of a field, an equals sign (=), and the field data. An ampersand (&) separates the fields.
So we end up with:

Figure 7. Get action
Try it. Notice that you can see the data in the address field of your browser.

Figure 8. Address bar for get
post actionInstead of the get action:
<form action=... method="get">
you can use post:
<form action=... method="post">
It works almost the same as get, except that the form data is passed through in an HTTP header, rather than concatenated to the URL. So the message from the browser to the server might look like this:
POST /articles/playing-with-dogs.php HTTP/1.1 Host: dogsite.com User-Agent: Mozilla/5.0 Content-Length: 32 Content-Type: application/x-www-form-urlencoded first_name=Buffy&surname=Summers
Figure 9. post message example
The form data itself has the same pattern; field name then value, field name then value, etc.
The advantage of the post method is that the data doesn’t show up in the browser’s address bar:

Figure 10. Address bar for post
Only the URL shows. Headers do not.
You can try it. You won’t see the form data in the address bar of your browser.
Most Webers use method="post" because the form data doesn’t show up in the browser’s address bar. It’s a little more secure, and less annoying.
Create a form like this:

Figure 1. Address form
Use the get method. Set the action to the page itself. For example, if you called the file ralph-the-wonder-form.html, use action="ralph-the-wonder-form.html".
When the form is filled in, it should look something like this:

Figure 2. Address form with data
Have a look at how the browser attaches data to the URL.
You can see my solution, but try it yourself first.
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
Let’s do an experiment. Open the get version of the form. Try typing some special characters into the form. A special character is anything that is not a letter or a digit. For example:

Figure 11. Special characters in a form
Click the button, and look at the URL on the form processing page. You’ll see something like this:
process-simple-form-get.php?first_name=Renata+is+cute!&surname=Yes%2C+I+agree.
Some of the characters have been changed. The spaces became plus signs (+). The comma (,) became %2C.
This is called “URL encoding.” Browsers automatically URL encode data before sending it in a get or a post.
Why do browsers do this? URLs use a small character set. A “character set” is a limited group of characters to choose from. For example, when computers were first developed, most used the old US-ASCII character set. It has just the letters A to Z, the digits, punctuation(,.!; etc.) and a few other things. If you want to send characters with accents (like é) or currency symbols (like ¥), you were out of luck.
Other character sets were developed. Perhaps the most common international character set these days is UTF-8. It can represent thousands of characters; Cyrillic, Kanji, you name it, it’s probably in UTF-8.
The trouble is, URLs still use US-ASCII. It’s efficient, and is universally supported. So how do you send special characters in a URL?
The answer: URL encoding. It converts some special characters to a code. For example, it changes commas (,) into %2C. It also changes spaces into plus signs.
Browsers are not consistent in their URL encoding. Here’s how different browsers encode the data in Figure 11:
Firefox
first_name=Renata+is+cute!&surname=Yes%2C+I+agree.
Internet Explorer
first_name=Renata+is+cute%21&surname=Yes%2C+I+agree.
Chrome
first_name=Renata+is+cute!&surname=Yes,+I+agree.
Safari
first_name=Renata+is+cute%21&surname=Yes%2C+I+agree.
Only IE and Safari are the same.
Usually, you won’t need to worry about URL encoding. It happens automatically. But sometimes you’ll need to do it yourself in PHP. More on that later.
Open the page you created for the address form exercise. Type special characters into the fields. See how they are encoded in the URL generated by the form.
Make some notes below on how different characters (like %, $, +, and #) are encoded.
(Log in to enter your solution to this exercise.)
Let’s go back and look at the HTML for the text input fields.
<h1>Simple Form</h1>
<form action="process-simple-form-get.php" method="get">
<p>First name:
<input name="first_name" type="text" size="20">
</p>
<p>Surname:
<input name="surname" type="text" size="20">
</p>
<p>
<button type="submit">Save</button>
</p>
</form>
Figure 5 (again). Simple form HTML
Line 4 sets up the first name field. <input> says it’s an input field. There are several types of input fields. Let’s stick with type="text" for now.
size sets the width of the field in number of characters. It does not limit the number of characters the user can type; size just affects the width of the field on the screen.
name is an important one. It’s the name given to the value when the data is sent to the server. You can see it in action on the last line of Figure 9.
Nothing happens until the user hits the button. The HTML for our button is:
<button type="submit">Save</button>
Notice the type attribute. Earlier, we’ve seen this as type="button". When type="submit", the browser sends form data to the server when the button is pressed.
In this lesson, you learned:
get attaches form data to a URL. post puts it in a separate HTTP header.<form> are action and method. <input> are type, name, and size.<button> is type.Let’s see how you can write PHP to handle form data.
We just looked at the HTML tags <form>, <input>, and <button>, and saw how browsers send form data to Web servers. Let’s see how you can retrieve that data using PHP.
By the end of this lesson, you should:
post and get forms.Here’s some HTML from the previous lesson.
<h1>Simple Form</h1>
<form action="process-simple-form-post.php" method="post">
<p>First name:
<input name="first_name" type="text" size="20">
</p>
<p>Surname:
<input name="surname" type="text" size="20">
</p>
<p>
<button type="submit">Save</button>
</p>
</form>
Figure 1. Simple form HTML
When the user clicks the button, the browser gets the data from the form, and sends it to the page given in the form’s action attribute. That’s process-simple-form-post.php.
You can try it.
Here’s process-simple-form-post.php.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<?php
$first_name = $_POST['first_name'];
$surname = $_POST['surname'];
?>
<p>Your name is <?php print $first_name . ' ' . $surname; ?>.</p>
</body>
</html>
Figure 2. process-simple-form-post.php
Line 9 fetches the data for one of the fields and puts it into a variable. Here is the statement again:

Figure 3. Fetching from $_POST
$_POST is an array holding all of the form data. You don’t need to know much about arrays yet; just follow the pattern above.
In the [''], you give the name of the HTML input field. This was specified in the HTML:
<input name="first_name" type="text" size="20">
There is a variable name in Figure 3. That’s where the form data will go.
You can name the PHP variable anything you like. Common practice is to give it the same name as the HTML form field. This makes it easier to match the HTML code with the PHP code.
Line 10 fetches the other form field’s data. It works the same way.
Line 12 in Figure 2 outputs the values. As before, print just inserts stuff into the PHP program’s output stream. Let’s look at this:
$first_name . ' ' . $surname
It’s the string concatenation operator at work again. The line tells PHP to:
Take the contents of
$first_name, stick a space on the end of that, and stick the contents of$surnameon the end of that.
The space (’ ‘ in the statement) is a string constant. The characters between the quotes are used exactly as-is, with no changes. For example:
$first_name . ' (Killer) ' . $surname
If $first_name was “Bugs” and $surname was “Bunny”, you would get:
Bugs (Killer) Bunny
The spaces inside the quotes are important. If we left them out:
$first_name . '(Killer)' . $surname
We’d get:
Bugs(Killer)Bunny
The spaces outside the quotes don’t matter to the PHP interpreter. We could have:
$first_name.' (Killer) '.$surname
It would still work. Some people think the extra spaces make the line more readable.
Here’s part of line 12 again:
print $first_name . ' ' . $surname;
We could have written it like this:
print "$first_name $surname";
This version has double quotes. What’s the difference?
'), it returns exactly what is between the quotes, with no changes."), it replaces variable names with their contents, and then returns the result.JavaScript doesn’t do this. Only PHP.
Here are some more examples.
<?php $a = 'Willow'; $b = 'Rosenberg'; print "<p>$a $b</p>"; //Prints <p>Willow Rosenberg</p> print '<p>$a $b</p>'; //Prints <p>$a $b</p> print '<p>'. $a . ' ' . $b . '</p>'; //Prints <p>Willow Rosenberg</p> ?>
Figure 4. Single versus double quotes
You can try it.
Here’s one of those statements again:
$first_name = $_POST['first_name'];
That only works for forms that use method="post":
<form action="..." method="post">
If you used get:
<form action="..." method="get">
then you fetch the form data in PHP like this:
$first_name = $_GET['first_name'];
If your PHP code isn’t fetching form data, check that you are using the right method.
In a previous exercise, you created a form like this:

Figure 1. Address form
Add a PHP page that processes this data, producing something like:

Figure 2. Address output
You can try my solution. You can also download the files.
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
post method.get method.Let’s see how you deal with an annoying thing PHP does to quotes in form data.
You know how to get form data into a PHP program. Let’s talk about an annoying thing that PHP does to text strings.
In this lesson, you learn:
stripslashes() function to fix the problem.Look at the name form again. Here it is, with some data:

Figure 1. Input
When the user clicks the button, the form data is sent to this page:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<?php
$first_name = $_POST['first_name'];
$surname = $_POST['surname'];
?>
<p>Your name is <?php print $first_name . ' ' . $surname; ?>.</p>
</body>
</html>
Figure 2. Process form data
Here is the output:

Figure 3. Output
No problem.
But suppose the user has a name with a quote (’) in it, like this:

Figure 4. Input with quote
This is the result:

Figure 5. Output with quote
There’s a backslash in the output (\). You can try it.
Huh? Where did that backslash come from?
PHP has a bunch of settings that change how it works. For example, you can set the maximum time a PHP program is allowed to run.
One of the settings is “magic quotes.” It’s either on or off. When it’s on, PHP looks at all the text data coming in from forms. If it finds a quote, it will put a backslash in front of it. So it converts De'ev to De\'ev.
If magic quotes is off, no conversion happens. De'ev stays De'ev.
Here’s the output again:

Figure 4 (again). Output with quote
This came from a PHP program running on a server. The server had magic quotes on. Hence the backslash.
I turned magic quotes off, and ran the program again. I got:

Figure 5. No backslash
Er, why did the PHP people do this magic quotes thing?
It helps with security in some situations. But it causes problems, as well. For De’ev, and others.
Many people think that magic quotes was a bad idea. It didn’t solve many security problems. And anyway, there are better ways to handle those security issues it did solve.
Magic quotes will be dropped in the next major version of PHP.
There are two ways to get rid of the extra backslashes. One way is to turn the magic quotes setting off in PHP. But to do that, you need access to a special settings file. You may or may not have access, depending on your hosting account.
The second way is to call PHP’s stripslashes() function. It does what its name says. You give it a string, and it goes through and removes the backslashes.
Here’s the code again, with a slight change:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<?php
$first_name = $_POST['first_name'];
$first_name = stripslashes($first_name);
$surname = $_POST['surname'];
$surname = stripslashes($surname);
?>
<p>Your name is <?php print $first_name . ' ' . $surname; ?>.</p>
</body>
</html>
Figure 6. Using stripslashes()
Lines 10 strips out the slashes from $first_name, and puts the result back into $first_name. Line 12 does the same for $surname.
You could also write it like this:
<?php $first_name = stripslashes($_POST['first_name']); $surname = stripslashes($_POST['surname']); ?> <p>Your name is <?php print $first_name . ' ' . $surname; ?>.</p>
Figure 7. Using stripslashes() again
It works the same. Line 9 takes the value of the form field first_name, strips out the backslashes, and puts the result into $first_name.
You could also do this:
<?php $first_name = $_POST['first_name']; $surname = $_POST['surname']; ?> <p>Your name is <?php print stripslashes($first_name) . ' ' . stripslashes($surname); ?>.</p>
Figure 8. Using stripslashes() again again
This version moves the calls to stripslashes() to the output.
Write an HTML page with a form that has one field, like this:

Figure 1. Input
When the button is clicked, the data is sent to a PHP page that shows whatever data the user typed. If there is a quote (’) in the data, no slashes appear in the output. Like this:

Figure 2. Output
You can try my solution to see how it works. You can download my solution, but do the exercise yourself before you look at the files.
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
In this lesson, you learned:
stripslashes() function to fix the problem.Let’s see how you can do computations on form data.
You know how to create forms, and get user input into PHP programs. Now let’s do something with the data.
By the end of this lesson, you should:
Suppose we start a company that sells two dog toys, frisbees and giant chew ropes. Frisbees are the great flying disks from Wham-o. Many dogs love chasing frisbees!
We want to sell the toys over the Web. Let’s create a form like this:

Figure 1. Order form
The user enters the number of each item s/he wants to order:

Figure 2. Order form with data
The user clicks the button, and a confirmation page appears.

Figure 3. Order confirmation
We’ll fix the number formats later.
Here’s the HTML for the order form.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Simple Order Form</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>Simple Order Form</h1>
<form action="process-order.php" method="post">
<p>
Frisbees:
<input type="text" name="frisbees" size="3">
at $8.95 each
</p>
<p>
Giant chew ropes:
<input type="text" name="giant_chew_ropes" size="3">
at $12.95 each
</p>
<p>
<button type="submit">Order</button>
</p>
</form>
</body>
</html>
Figure 4. Order form HTML
Nothing new here. Just two text input fields, as before. One field is for the number of frisbees the user wants. The other is for the number of giant chew ropes.
Here’s the code for process-order.php, the file listed in the action attribute of the form.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Order Processed</title>
</head>
<body>
<h1>Order Processed</h1>
<?php
//Get the input.
$frisbees = $_POST['frisbees'];
$giant_chew_ropes = $_POST['giant_chew_ropes'];
//Compute totals.
$frisbees_total = $frisbees * 8.95;
$giant_chew_ropes_total = $giant_chew_ropes * 12.95;
$order_total = $frisbees_total + $giant_chew_ropes_total;
//Output totals.
?>
<p>Thank you for your order.</p>
<table cellpadding="5" cellspacing="0" border="1">
<tr>
<th>Product</th>
<th>Number ordered</th>
<th>Unit price</th>
<th>Total price</th>
</tr>
<tr>
<td>Frisbees</td>
<td><?php print $frisbees; ?></td>
<td>$8.95</td>
<td>$<?php print $frisbees_total; ?></td>
</tr>
<tr>
<td>Giant chew ropes</td>
<td><?php print $giant_chew_ropes; ?></td>
<td>$12.95</td>
<td>$<?php print $giant_chew_ropes_total; ?></td>
</tr>
<tr>
<td colspan="3">Total</td>
<td>$<?php print $order_total; ?></td>
</tr>
</table>
</body>
</html>
Figure 5. Order processing code
Lines 11 and 12 fetch the data from the form, and put them into PHP variables:
$frisbees = $_POST['frisbees'];
$giant_chew_ropes = $_POST['giant_chew_ropes'];
Line 14 works out the total cost of the frisbees:
$frisbees_total = $frisbees * 8.95;
It’s the number of frisbees ordered (in $frisbees) times the price of one frisbee (8.95). The asterisk (*) means “multiply.” The result is put into $frisbees_total.
Line 15 works out the total cost of the giant chew ropes:
$giant_chew_ropes_total = $giant_chew_ropes * 12.95;
It’s the number of giant chew ropes ordered (in $giant_chew_ropes) times the price of one giant chew rope ($12.95). The result is put into $giant_chew_ropes_total.
Line 16 works out the total cost of the order:
$order_total = $frisbees_total + $giant_chew_ropes_total;
It’s the total cost of the frisbees (in $frisbees_total) plus the total cost of the giant chew ropes (in $giant_chew_ropes_total). The order total is put into the variable $order_total.
The results are output in a <table>. You learned about tables in the lesson A Web page with tables.
Write a program that computes the area of a circle. Create a form like this:

Figure 1. Form
The user enters a number, and clicks the button:

Figure 2. Form with data
The user then sees:

Figure 3. Output
The formula is:
area = 3.14159 x radius x radius
You can try my solution, and download the files. Of course, try it yourself first.
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
We’ve seen strange number formats, like this:

Figure 3 (again). Order confirmation
The prices are messed up. For example, 17.9 should be 17.90.
This is easy to fix with the number_format() function. You tell it the number you want to format and how many decimals you want, and it will return a formatted value. For example:
$x = 17.9;
print number_format($x, 2);
This will output 17.90.
We can change the order processing program like this:
<tr> <td>Frisbees</td> <td><?php print $frisbees; ?></td> <td>$8.95</td> <td>$<?php print number_format($frisbees_total, 2); ?></td> </tr> <tr> <td>Giant chew ropes</td> <td><?php print $giant_chew_ropes; ?></td> <td>$12.95</td> <td>$<?php print number_format($giant_chew_ropes_total, 2); ?></td> </tr> <tr> <td colspan="3">Total</td> <td>$<?php print number_format($order_total, 2); ?></td> </tr>
Figure 6. Formatting numbers
You can try it.
Write a program that computes the volume of a cylinder. Create a form like this:

Figure 1. Form
When the user clicks the button, the result appears:

Figure 2. Output
Make sure that the volume is shown to three decimal places.
You can try my solution. You can download the files. Try it yourself first, of course.
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
Let’s add shipping to our form.

Figure 7. Order form with shipping
The form uses radio buttons for the shipping method. The user can select only one. You can try it.
Here’s the output:

Figure 8. Order confirmation with shipping
Here’s the new HTML.
<div>
<p>Shipping method</p>
<p>
<input type="radio" name="shipping_method" value="standard">
Standard ($5)
</p>
<p>
<input type="radio" name="shipping_method" value="overnight">
Overnight ($10)
</p>
</div>
Figure 9. HTML for shipping method
type="radio" tells the browser you want to use a radio button.
Only one of the radio buttons can be selected. When one is clicked, the other is turned off.
There can be more than one group of radio buttons on a form. Like this:

Figure 10. Radio button groups
The user can select one button in each group:

Figure 11. Selected buttons
If the user clicks on a radio button, the others are turned off. But which others? Only the ones in the same group.

Figure 12. Selecting different buttons
How does the browser know which radio buttons belong together? Here’s the HTML:
<p>Favorite animal</p> <blockquote> <input type="radio" name="animal" value="Dog">Dog<br> <input type="radio" name="animal" value="Cat">Cat<br> <input type="radio" name="animal" value="Horse">Horse<br> <input type="radio" name="animal" value="Maggot">Maggot<br> </blockquote> <p>Favorite color</p> <blockquote> <input type="radio" name="color" value="Green">Green<br> <input type="radio" name="color" value="Blue">Blue<br> <input type="radio" name="color" value="Purple">Purple<br> </blockquote>
Figure 13. Radio button HTML
The name attribute of the radio buttons defines the groups. Radio buttons what belong together have the same name.
Let’s go back to the shipping method for the dog toys. Here is the HTML:
<div>
<p>Shipping method</p>
<p>
<input type="radio" name="shipping_method" value="standard">
Standard ($5)
</p>
<p>
<input type="radio" name="shipping_method" value="overnight">
Overnight ($10)
</p>
</div>
Figure 9 (again). HTML for shipping method
Both radio buttons have the same name: shipping_method (lines 24 and 28). That’s how the browser knows they belong together.
The PHP page will get one piece of form data called shipping_method. It’s value will be either standard or overnight. Which one the PHP gets will depend on what the user chose.
Here’s the PHP page.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Order Processed</title>
</head>
<body>
<h1>Order Processed</h1>
<?php
//Get the input.
$frisbees = $_POST['frisbees'];
$giant_chew_ropes = $_POST['giant_chew_ropes'];
$shipping_method = $_POST['shipping_method'];
//Compute product totals.
$frisbees_total = $frisbees * 8.95;
$giant_chew_ropes_total = $giant_chew_ropes * 12.95;
//Compute shipping cost.
if ( $shipping_method == 'standard' ) {
$shipping_cost = 5;
}
else {
$shipping_cost = 10;
}
$order_total = $frisbees_total
+ $giant_chew_ropes_total
+ $shipping_cost;
//Output totals.
?>
<p>Thank you for your order.</p>
<table cellpadding="5" cellspacing="0" border="1">
<tr>
<th>Product</th>
<th>Number ordered</th>
<th>Unit price</th>
<th>Total price</th>
</tr>
<tr>
<td>Frisbees</td>
<td><?php print $frisbees; ?></td>
<td>$8.95</td>
<td>$<?php print number_format($frisbees_total, 2); ?></td>
</tr>
<tr>
<td>Giant chew ropes</td>
<td><?php print $giant_chew_ropes; ?></td>
<td>$12.95</td>
<td>$<?php print number_format($giant_chew_ropes_total, 2); ?></td>
</tr>
<tr>
<td colspan="3">Shipping</td>
<td>$<?php print number_format($shipping_cost, 2); ?></td>
</tr>
<tr>
<td colspan="3">Total</td>
<td>$<?php print number_format($order_total, 2); ?></td>
</tr>
</table>
</body>
</html>
Figure 14. Code for PHP file
Line 13 get the shipping_method. Lines 18 to 23 work out the shipping cost.
PHP’s if statement is much like JavaScript’s if statement, that we covered earlier. The general form is:
if (expression) {
Do this if expression is true;
}
else {
Do this if expression is false;
}
In line 18,
$shipping_method == 'standard'
is true if $shipping_method has exactly “standard” in it. Not “STANDARD” or “Standard” or “ standard “ – just “standard”.
Note the ==. Don’t use just =. = means “take the thing on the right an put it in the variable on the left.” Like lines 11, 12, 13, 15, 16, etc. If you want to compare, use ==.
You can use other things besides ==. For example:
if ( age > 21 ) {
if ( age <= 65 ) {
if ( color != 'blue') {
> is greater than. <= is less than or equal to. != means “is not equal to.”
You learned:
Time for some exercises.
Create an order form for model ships. The form should look like this:

Figure 1. Order form
After clicking the button, you see:

Figure 2. Output
You can try my solution. You can also download the files. Try it yourself first, of course.
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
You learned how to create forms, and use PHP to process form data. This lessons looks at how to email it.
By the end of this lesson, you will learn:
<textarea> tag.mail function to send an email message.In an earlier chapter, we created a template-based site with PHP. It included a contact form that looked like this:

Figure 1. Incomplete contact page
Now we can finish it. This is what we want:

Figure 2. Complete contact page
Here’s the code for the form.
<?php
//Path from this page to the site root.
$path_to_root = '.';
//Title of this page.
$page_title = 'Contact';
require $path_to_root . '/library/start_page.inc';
require $path_to_root . '/library/header.inc';
require $path_to_root . '/library/left_nav.inc';
?>
<div id="center_region">
<h1>Contact</h1>
<p>Please complete the form and click Send.</p>
<form action="send-contact-message.php" method="post">
<p>
Subject<br>
<input type="text" name="subject" size="40">
</p>
<p>
Comment<br>
<textarea name="message" rows="5" cols="40"></textarea>
</p>
<p>
<button type="submit">Send</button>
</p>
</form>
<p><a href="index.php">Home</a></p>
</div>
<?php
require $path_to_root . '/library/footer.inc';
require $path_to_root . '/library/end_page.inc';
?>
Figure 3. Code for contact form
Lines 1 to 9 and 28 to 31 are the templating system at work. The form itself is the HTML from lines 13 to 35.
There’s a new form element on line 20: <textarea>. It creates the multi-line field in Figure 2. The rows attribute specifies the number of rows the field has. The cols attribute specifies the number of columns.
If we want the field to have contents when the page loads, we could add it inside the tag. For instance:
<textarea>(Type your message here)</textarea>
would show this when the page loads:

Figure 4. <textarea> with content
send-contact-message.php (named in the action property of the form) will send the message. Here’s the code:
<h1>Contact</h1> <?php //Get message data. $subject = $_POST['subject']; $message = $_POST['message']; //Email it. mail( 'email address', //Where to send "Contact form - $subject", //Email subject $message //Message body ); ?> <p>Thank you! Your message has been sent.</p>
Figure 5. send-contact-message.php
I’ve omitted the PHP that adds the header, nav bar, etc.
Lines 4 and 5 get the subject and the message. Nothing new there.
Lines 7 to 11 sends the email using the email() function. We give the function three arguments:
You should replace the email address with your own, if you want to test the program.
A couple of things to notice. First, I split the function call over a few lines, to make it easier to read. I added comments at the end of each line, explaining the arguments.
Line 9 adds the text “Contact form – “ in front of the subject. This will help the recipient know where the message came from.
The mail() function will work only if your server is set up correctly. PHP has to know what program on the server to use to send email.
Most shared hosting servers will be set up correctly. However, your test server on your local machine (e.g., your XAMPP server) might have some trouble. That’s OK; it’s just for testing anyway.
You can try it.
I’ve had trouble sending email to Gmail using the basic mail() function. Here’s the code I used in an application that worked:
$to_address = 'email address'; $subject = 'Card order'; $message = "Stuff stuff, thing thing"; $from = 'another email address'; $headers = "MIME-Version: 1.0\n"; $headers .= "Content-type: text/plain; charset=iso-8859-2\n"; $headers .= "From: $from\n" . "Reply-To: $from\n" . "X-Mailer: PHP/" . phpversion() . "\n"; mail($to_address, $subject, $message, $headers);
Figure 6. Sending email to Gmail
You may need to use this approach for some other services as well.
In this lesson, you learned:
<textarea> tag.mail function to send an email message.Let’s see how you can store form data to a file.
You just learned how to send form data be email. But sometimes you want that data to be immediately available on your server. Let’s see one way to do that.
By the end of this lesson, you should:
Let’s say you want people to be able to add comments to a Web page. Here’s what the user sees when there are no comments:

Figure 1. No comments
There are two pieces to the comment area:

Figure 2. Comment area pieces
First, there’s the current comments. There are none in this case.
Second, there’s a form for adding new comments. This will always be the same, no matter how many comments there are.
Users add comments by typing in the text field and clicking the button:

Figure 3. Adding a comment
When they click Save, the page reloads, and shows the new comment in the comment area.

Figure 4. Comment showing
We need a place to keep comments that people enter, so we can read them back and show them on Renata’s page.
For this example, we’ll store them in a file. When there’s a new comment, we’ll append it to the file. Append means “add to the end.”
When Renata’s page – renata.php – loads, we’ll show the comments. We’ll put some PHP on the page that will read the comments in and insert them into the HTML.
This will work most easily if the comments file already contains HTML tags. But people who type comments don’t type HTML. They just type text:

Figure 3 (again). Adding a comment
So we’ll add some HTML tags around what they type.
Whenever you save data that people type, you have to worry about security. You don’t want hackers typing comments that, for example, contain JavaScript. We’ll address that later.
Let’s draw a picture of what happens behind the scenes. It will show us the flow of control, that is, what happens first, what happens next, and so on.

Figure 5. Comment control flow
The browser asks the server for renata.php (1). PHP code in renata.php reads the comments file, and inserts the comments into the HTML (2). renata.php also contains HTML to show the form (the <form> tag and friends).
Once the browser has shown the page, it waits for the user to do something. Suppose the user types a comment, and clicks the Save button (3). The browser looks at the action property of the form: action="save-renata-comment.php". So it asks the server for that page, and sends the form data along with the request (4).
The server loads save-renata-comment.php. Code in that file takes the new comment, wraps it in a <p> tag, and appends it to the comments file. Then it redirects the browser. That is, it tells the browser to load another page. Which one? renata.php (6).
So the browser loads renata.php (1). Code in renata.php reads the comments file (2), which now has the new comment on the end. So the new comment appears.
Phew! That’s a lot of work! But it’s a lot of work for the computers, not for us. We tell them what to do, and they do it.
Let’s have a look at the pieces.
Here’s what the comments file looks like after a comment has been added:
<p>You are so cute!</p><hr>
Figure 6. Comments file with one comment
We need to read that in to the right place in renata.php (2 in Figure 5). Here’s the code for the entire file. (I’ve wrapped it in the template code we saw earlier.)
<?php
//Path from this page to the site root.
$path_to_root = '..';
//Title of this page.
$page_title = 'Renata';
require $path_to_root . '/library/start_page.inc';
require $path_to_root . '/library/header.inc';
require $path_to_root . '/library/left_nav.inc';
?>
<div id="center_region">
<h1>Dog profile: Renata</h1>
<p>Here is Renata.</p>
<p><img src="renata.jpg" alt="Renata"></p>
<h2>Comments</h2>
<?php
if( file_exists('renata_comments.txt') ) {
readfile('renata_comments.txt');
}
else {
print '<p>There are no comments yet.</p>';
}
?>
<h2>Add a comment</h2>
<form action="save-renata-comment.php" method="post">
<p>
<textarea rows="3" cols="30" name="comment"></textarea>
</p>
<p>
<button type="submit">Save</button>
</p>
</form>
<p><a href="index.php">Back to the dog list</a></p>
</div>
<?php
require $path_to_root . '/library/footer.inc';
require $path_to_root . '/library/end_page.inc';
?>
Figure 7. renata.php
Everything up to line 9 is templating code. The real stuff starts on line 10.
Lines 10 to 14 are simple HTML. That’s the main content of the page. The comment section follows.
There may be comments, or there may not be. If there are comments, we want to show them. Otherwise, we want to tell the user that there are no comments yet.
We’re going to store comments in the file renata_comments.txt. The file will be created when the first comment is added. If there are no comments, the file won’t even exist.
Here’s line 16:
if( file_exists('renata_comments.txt') ) {
file_exists() is a function that returns true if the file is found. If it is, line 17 runs:
readfile('renata_comments.txt');
This reads the file, and inserts it into the HTML output stream.
If the file does not exist, then file_exists('renata_comments.txt') is false, and line 20 runs:
print '<p>There are no comments yet.</p>';
This outputs the text in the quotes. Remember that we need to include the HTML tags.
The rest of the page uses stuff we’ve seen before.
The user types a comment and clicks the same button. The browser takes the URL in the form’s action attribute – save-renata-comment.php – and goes there, sending the contents of the comment field (4 in Figure 5).
Here’s save-renata-comment.php:
<?php
//Get the user's comment.
$comment = $_POST['comment'];
//Append it to the comments file.
$f = fopen('renata_comments.txt', 'a');
fwrite($f, "<p>$comment</p><hr>");
fclose($f);
//Jump back to Renata's page.
header('location:renata.php');
?>
Figure 8. save-renata-comment.php
Here’s what happens when the user clicks the Send button.
![]() Browser |
![]() Web server |
![]() PHP interpreter |
Gimme save-renata-comment.php.And here's some form data: comment=You+are+so+cute!
|
||
(Fetches save-renata-comment.php from disk.)Oh! This is a PHP file. Hey, PHP interpreter! Run this. |
||
|
Get the form data. Save data to the file. Hey, server, here's the result. No HTML, but I have an HTTP header for you: Location: renata.php
|
||
Hey, browser. Here's the contents of
save-renata-comment.php.
Just an HTTP header:Location: renata.php
|
||
|
OK. Hey, send me renata.php.
|
||
(Fetches renata.php from disk.)Oh! This is a PHP file. Hey, PHP interpreter! Run this. |
||
|
HTML, send it through. Here's some PHP. Read the file, put it in the HTML. More HTML. Hey, server, here's the result. |
||
Hey, browser. Here's the contents of
renata.php.
|
||
|
OK. (Render for user.) |
Figure 9. After the Save button is pressed
Let’s have a look at the code again.
<?php
//Get the user's comment.
$comment = $_POST['comment'];
//Append it to the comments file.
$f = fopen('renata_comments.txt', 'a');
fwrite($f, "<p>$comment</p><hr>");
fclose($f);
//Jump back to Renata's page.
header('location:renata.php');
?>
Figure 8 (again). save-renata-comment.php
Line 3 gets the user’s comment, and puts it into the variable $comment.
Line 5 is new. We want to write to the comments file. But before we do that, we need to open it. That’s what the fopen() function does.
We give fopen() two arguments:
The 'a' means the file is opened in append mode. That means that anything we write to the file will be added to the end.
Take another look at the line:
$f = fopen('renata_comments.txt', 'a');
What is $f for? fopen() return a file handle. This is a special object that refers to the file. You don’t need to know what it is, exactly. Just use it.
The next line is:
fwrite($f, "<p>$comment</p><hr>");
This writes data to $f. Because the program opened the file in append mode, the data is written to the end of the file.
Remember that it’s easier if the file contains HTML. But the user types just plain text into the comment field. So we wrap the comment in a <p> tag. We also add a horizontal rule (<hr>) to separate comments from each other.
Finally, the program closes the file in line 9. PHP will close files for you automatically if you forget to do it.
One more thing to notice about the code.
<?php
//Get the user's comment.
$comment = $_POST['comment'];
//Append it to the comments file.
$f = fopen('renata_comments.txt', 'a');
fwrite($f, "<p>$comment</p><hr>");
fclose($f);
//Jump back to Renata's page.
header('location:renata.php');
?>
Figure 8 (again). save-renata-comment.php
This program does not send any HTML to the browser. The program adds the new comment, then tells the browser to jump to another page.
PHP files don’t have to generate an <html> tag. Most do, but this one doesn’t. In future chapters, you’ll see other programs that have the same pattern:
$_POST.Is save-renata-comment.php really a Web “page?” Well, it’s a PHP file that has a URL. But it doesn’t show anything to the user. save-renata-comment.php does its work, then generates an HTTP header that says to the browser, “Sorry, you need to go to another place. Here is the URL.”
I don’t care whether you call it a page or not. The important thing to understand is that:
Some PHP programs don’t create HTML.
There’s a problem with the code, though. Evil people could cause some problems.
Actually, what they would do is write a program that repeatedly loaded our page and shoved a lot of data into the comments field. Do that a 100,000 times, and the disk could get full.
Let’s make the following changes.
Here’s the new version of save-renata-comment.php.
<?php
//Make sure the file exists.
touch('renata_comments.txt');
//Check the length of the comments file.
if ( filesize('renata_comments.txt') < 50000 ) {
//Get the user's comment.
// Limit it to 2,000 characters.
$comment = substr($_POST['comment'], 0, 2000);
//Convert special characters to HTML entities.
$comment = htmlentities($comment);
//Append comment to the comments file.
$f = fopen('renata_comments.txt', 'a');
fwrite($f, "<p>$comment</p><hr>");
fclose($f);
}
//Jump back to Renata's page.
header('location:renata.php');
?>
Figure 10. Secure version of save-renata-comment.php
Look at line 5:
if ( filesize('renata_comments.txt') < 50000 ) {
The filesize() function returns the number of bytes in the file. If this number is less than 50,000, the statements inside the braces ({}) will run. The braces extend from lines 5 to 15, and include the code that adds data to the comments file.
You can have as many statements as you want in the braces. You can have other if statements, if you want. We’ll see that later.
There is one problem with the filesize() function. It will cause an error if the file does not exist. That’s what line 3 does. The touch() function creates the file if it doesn’t exist.
Have a look at line 8:
$comment = substr($_POST['comment'], 0, 2000);
$_POST['comment'] returns the contents of the comment form field, as before. But rather than just putting that in $comment, something is done with it first.
substr() is a function that returns part of a string. It takes three arguments. The first is the string to process. The next two are numbers. One is the start position of the string. The other is the number of characters to return.
Here are some examples.
$x = 'Lion King';
print substr($x, 0, 3);
The first character is at position 0, not 1. So this says “Start at character 0, and return three characters. The result is:
Lio
How about this one?
print substr('Renata is cute', 2, 4);
This says “Start at character 2 and return 4 characters”. Character 2 is the third character (the first one is character 0). So the result is:
nata
One more.
$r = 'Rupert Giles';
$s = substr($r, 10, 8);
print $s;
This says “Start at the 11th character, and return 8 characters.” The 11th character is the “e” in “Giles.” The result will be:
es
substr() cannot return 8 characters, because there aren’t that many. So it returns what it can.
Back to the code. The line is:
$comment = substr($_POST['comment'], 0, 2000);
This says “Start at character 0 (the first character) and return the first 2,000 characters.” If there are, say, 23 characters, then $comment will contain all 23. If there are 4,321 characters, $comment will contain 2,000 characters.
The last thing we need to do is make sure that any JavaScript that the user types into the comment field will not run when the page is shown. There are various ways to do this; we’ll use the simplest.
For JavaScript to run, it has to have a <script> tag around it, like this:
<script>
alert("Evil JavaScript");
</script>
As you know, the < and the > have special meaning to the browser. They mean “Here is some HTML.” If you want to show a less than sign in your content, you need to replace < with <. This is an HTML entity. It displays a less than sign, that isn’t interpreted as the start of a tag.
We can tell PHP to replace all the characters that have special meaning in HTML, like <, with their HTML entities. This is harmless:
<script>
alert("Evil JavaScript");
</script>
It won’t be executed, because there is no < to indicate the start of an HTML tag.
Here’s is what we do on line 10:
$comment = htmlentities($comment);
htmlentities() is a function that will go through a string, and replace all of the special characters with HTML entities.
Let’s say someone types this into the comment field:

Figure 11. Evil JavaScript
Here is what would be stored into the comments file:
<p><script>
alert(\"Evil JavaScript\");
</script></p><hr>
The only HTML tags are the <p> and <hr> we added ourselves.
This lesson shows you how to add form data to a file. PHP gets the form data, opens the file in append mode, and writes the data.
We also looked as some security measures. We limited the total size of the comments file, the size of any one comment, and made sure that JavaScript typed into a comment would not execute.
Time for some exercises.
Write a PHP program that emails a light bulb joke. Here is the form:

Figure 1. Form
When the user clicks the button, some PHP sends an email somewhere, and then shows this:

Figure 2. Output
Only use your own email address! Don’t spam.
You can try my version (it doesn’t actually send email, just shows the output). You can download the files.
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
Create an application to save jokes to a file. The main menu is like this:

Figure 1. Main page
Click “See joke list” and you see the jokes file:

Figure 2. Show jokes
The user can also type a new joke into the main page:

Figure 3. Adding a joke
Clicking the button will add the joke to the file, and show a confirmation page:

Figure 4. Joke added
You can try my solution, though it doesn’t actually save new jokes. You can also download the files.
Upload your solution to your server. Put the URL of the main page below.
(Log in to enter your solution to this exercise.)
Write a PHP application that lets you collect quotes you like. It should let you:
You can try the application.
The page that shows the quotes should say “No quotes yet” if there are none. Otherwise, it should show the quotes.
Separate quotes with <hr> tags.
save-quotes.php is the file that saves a new quote. It should:
stripslashes() to remove PHP’s “helpful” backslashes.<br> tags.These two lines will help:
$quote = stripslashes($_POST['quote']);
$quote = str_replace("\n", '<br>', $quote);
You can download a zip file that has everything except two PHP files: see-quotes.php and save-quotes.php.
You can also download my solution, but do the exercise yourself first!
Put the URL of your solution below.
(Log in to enter your solution to this exercise.)
A zoo has a ticket booth. Visitors come up to the booth, and order tickets for adult and children (e.g., 1 adult and two children). The booth attendant collects the entry fee, and prints out the tickets.
Write a PHP application for the attendant. It starts out like this:

Figure 1. Empty form
Notice that the focus is in the first input field.
You can download the background image. It’s from a design on OSWD, by GGGDesign.
The attendant enter the numbers of adults and children, and clicks the Total button. The page shows the total, and a Print button:

Figure 2. Form with data
When the attendant clicks the Print button, three things happen.
Clicking on the “Show log” link shows the transaction log:

Figure 3. Log
Each log entry has the date and time, as well as the number of tickets ordered. This code might be useful:
date('l, F jS Y, h:i:s A')
You can try the application (although I have disabled the logging, to keep the file size down).
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
In a Web application, data validation takes place on the browser and on the server. You learned how to some validation with JavaScript in a ClientCore lesson.
This lesson reminds you how to do validation in JavaScript. It also tells you how to do better error reporting.
By the end of this lesson, you should:
Let’s review some JavaScript and jQuery. Let’s start with a page like this:

Figure 1. Empty form
Notice that the input focus is on the first field; you can tell because the cursor is there.
The user completes the field clicks the Go button. Here is the result.

Figure 2. Complete form
The output area only appears after the button is clicked. It appears with an animated effect.
You can try the page.
Here is the code.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Review</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
body {
font-family: Verdana, sans-serif;
font-size: 14px;
background-color: #FFFFE0;
}
/* Output area starts out hidden. */
#output_area {
display: none;
}
/* Used to highlight the user's name. */
.highlight {
font-weight: bold;
color: red;
}
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
//Put the input focus in the first field.
$("#first_name").focus();
//Set up the button click.
$("#go").click(function(){
//Get the input.
var first_name = $("#first_name").val();
var last_name = $("#last_name").val();
//Compute the full name.
var full_name = first_name + ' ' + last_name;
//Show output.
$("#full_name")
.text(full_name)
.addClass('highlight');
$("#output_area").show('medium');
});
});
</script>
</head>
<body>
<h1>Review</h1>
<p>
First name:
<input type="text" id="first_name" size="20">
</p>
<p>
Last name:
<input type="text" id="last_name" size="20">
</p>
<p>
<button type="button" id="go">Go</button>
</p>
<div id="output_area">
<p>Your full name is <span id="full_name"></span>.</p>
</div>
</body>
</html>
Figure 3. Code for the page
Line 23 loads the jQuery library, from one of Google’s fast servers.
Line 25 starts the JavaScript. The line is:
$(document).ready(function() {
The code in the ready() function runs when the page has loaded.
Line 27:
$("#first_name").focus();
sets the input focus on to the HTML element with the id of first_name. That element is:
<input type="text" id="first_name" size="20">
That’s how the input focus appears in the first name field when the page first appears.
The field has an id attribute so that jQuery can reference the fields. But it doesn’t have a name attribute. name is used when sending data to a server. We’re not doing that here, so no name attribute is needed.
There is no <form> tag around the <input> elements. It’s only needed when data is being sent to a server, which we aren’t doing.
This line (number 29):
$("#go").click(function(){
attaches some code to the button’s click event. The code runs when the user clicks the button.
The tag for the button says type="button". The forms we’ve seen recently have used type="submit". A submit button is only used when send (submitting) data to a server. We’re not doing that, so type="button" makes more sense.
What do we want to happen when the user clicks the button? Here’s some pseudocode.
Get the first and last name the user typed. Put them together to make the full name. Show the full name.
Figure 4. click pseudocode
It’s a good idea to write pseudocode for every function you create. This helps:
Here’s the JavaScript:
//Get the input.
var first_name = $("#first_name").val();
var last_name = $("#last_name").val();
//Compute the full name.
var full_name = first_name + ' ' + last_name;
//Show output.
$("#full_name")
.text(full_name)
.addClass('highlight');
$("#output_area").show('medium');
Figure 5. click code
Let’s see how this achieves the steps given in the pseudocode (Figure 4).
The first step in the pseudocode is:
Get the first and last name the user typed.
I added a comment in line 30, explaining what the code is doing.
Line 31 gets the value the user typed into the first_name field, and puts it into the JavaScript variable first_name:
var first_name = $("#first_name").val();
The names of the field and the variable don’t have to be the same, but it makes the code easier to understand. Line 32 gets the last name.
That’s the first step in the pseudocode: get the user input.
The second step in the pseudocode (Figure 4) is:
Put them together to make the full name.
Line 34 takes the first name, appends a space to it, and then appends the last name. The result gets put into the variable full_name:
var full_name = first_name + ' ' + last_name;
The third step in the pseudocode (Figure 4) is:
Show the full name.
Line 36 finds an element with an id of full_name:
$("#full_name")
That element is the <span>:
<p>Your full name is <span id="full_name"></span>.</p>
Next, set the text of that element to the contents of the variablefull_name:
$("#full_name")
.text(full_name)
Then add the class highlight to the element:
$("#full_name")
.text(full_name)
.addClass('highlight');
The class is defined in the CSS as:
.highlight {
font-weight: bold;
color: red;
}
Lines 36 to 38 use method chaining (“method” is a type of function). The statement could have been typed all on one line, like this:
$("#full_name").text(full_name).addClass('highlight');
Spreading across three lines makes it easier to read.
Line 39 is:
$("#output_area").show('medium');
It makes the element with the id of output_area appear. It uses an animated effect at medium speed.
Let’s look at the structure of the output area again, just to be sure what is going on.
<div id="output_area"> <p>Your full name is <span id="full_name"></span>.</p> </div>
Part of Figure 3 (again). Code for the page
output_area is a container for, well, the output area. Inside it are places for the individual output pieces. There is only one in this example: full_name. But there could be more.
This is a common pattern.
Create a container for the output. Have individual output elements inside the container. Fill in the individual output elements. Show the entire output container at once.
Figure 6. Pattern for output with HTML and JavaScript
You can try the page.
Create a page that looks like this when first displayed:

Figure 1. Empty form
Notice that the input focus is in the first field.
When the user fills in the form and clicks the button:

Figure 2. Form with data
You can try my solution to see how it works. But don’t look at the source code until you try it yourself first!
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
Let’s see how you can use JavaScript to check the data that users type into form fields. Start with this one:

Figure 6. Age check form
The user types something in the field, and clicks the button. Some JavaScript checks the value, and shows a message, like this:

Figure 7. Age error
You can try it.
Here is the code:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Check age 1</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
body {
font-family: Verdana, sans-serif;
font-size: 14px;
background-color: #FFFFE0;
}
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#age").focus();
$("#go").click(function(){
var age = $("#age").val();
if ( age == "" ) {
alert("Sorry, please enter your age.");
}
else if ( isNaN(age) ) {
alert("Sorry, please enter a number.");
}
else if ( age < 0 || age > 120 ) {
alert("Sorry, please enter a valid age.");
}
else if ( age < 21 ) {
alert("Sorry, you are too young.");
}
else {
alert("Thanks! You can proceed.");
}
$("#age").focus();
});
});
</script>
</head>
<body>
<h1>Check age 1</h1>
<p>
Age:
<input type="text" id="age" size="3">
</p>
<p>
<button type="button" id="go">Go</button>
</p>
</body>
</html>
Figure 8. Code for age error check
Line 18 put the value the user typed into the variable age.
var age = $("#age").val();
Here is the first check:
if ( age == "" ) {
This is true if the variable is an empty string. Remember:
== and not =. "" is an empty string. The quotes are together with no space between them. This is not the same as " ", which is a string with a space in it.Line 22 is:
else if ( isNaN(age) ) {
The isNaN() function returns true if the parameter is Not a Number.
This (line 25):
else if ( age < 0 || age > 120 ) {
means “if age is less than 0 or greater than 120.” || means “or,” && means “and,” and ! means “not.”
Here’s another example.
width > 10 && width < 50
widthis more than 10 and less than 50. Note that leaving out the secondwidthwill not work:
width > 10 && < 50
Some more.
x < 10 || y == 10
xis less than 10, oryis equal to 10. You can have as many different variables as you want in aniftest.
(x + y) < 100 && z != 11
xplusyis less than 100, andzdoes not equal 11. You can do computations like this inifstatements.
Create a page that looks like this when first displayed:

Figure 1. Empty form
Notice that the input focus is in the first field.
When the user clicks the button, JavaScript code checks the values in the fields. Each field must have a valid number. Minutes must be from 0 to 120. Seconds must be from 0 to 59.
Here’s an error message:

Figure 2. Error
If the data is OK, show this:

Figure 3. Data OK
You can try my solution to see how it works. But don’t look at the source code until you try it yourself first!
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
Time to see how you can check strings. Here’s a log in form.

Figure 9. Empty log in form
Here’s an error message showing.

Figure 10. Log in error
Here’s the code:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Check log in 1</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
body {
font-family: Verdana, sans-serif;
font-size: 14px;
background-color: #FFFFE0;
}
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#user_name").focus();
$("#login").click(function(){
var user_name = $("#user_name").val();
var password = $("#password").val();
if ( user_name == "" || password == "" ) {
alert("Sorry, you must enter both your user name and password.");
}
else if ( user_name.length < 6 ) {
alert("Sorry, the user name is too short.");
}
else if ( password.length < 6 ) {
alert("Sorry, the password is too short.");
}
else if ( user_name.toLowerCase() == "renata" ) {
alert("Sorry, Renata! You need to get permission.");
}
else {
alert("Thanks! You can proceed.");
}
$("#user_name").focus();
});
});
</script>
</head>
<body>
<h1>Check log in 1</h1>
<p>
User name:
<input type="text" id="user_name" size="20">
</p>
<p>
Password:
<input type="password" id="password" size="20">
</p>
<p>
<button type="button" id="login">Login</button>
</p>
</body>
</html>
Figure 11. Log in code
Line 48 has something new:
<input type="password" id="password" size="20">
A field with a type of password is just like a regular text field, except that it hides what the user types.

Figure 12. Password field in action
I gave the field an id of password. It could have been anything, but this id makes sense.
Lines 18 and 19:
var user_name = $("#user_name").val();
var password = $("#password").val();
get the values the user typed into the form.
This (line 20):
if ( user_name "" || password "" ) {
checks whether user_name or password are empty.
Here’s line 23:
else if ( user_name.length < 6 ) {
user_name.length returns the number of characters in a string.
Here’s line 29:
else if ( user_name.toLowerCase() == "renata" ) {
user_name.toLowerCase() returns a string user_name, but with all of the characters converted to lower case. So the if statement will succeed if the user enters renata, Renata, RENATA, renatA, etc.
This does not change the value of user_name. If we wanted to do that, we could write:
user_name = user_name.toLowerCase();
Create a page that looks like this when first displayed:

Figure 1. Empty form
Notice that the input focus is in the first field.
When the user clicks the button, JavaScript code checks the value in the field. The field cannot be empty. It must contain a value that is longer than one character, and shorter than 21 characters.
Here’s an error message:

Figure 2. Error
If the data is OK, show this:

Figure 3. Data OK
Hint: this combines the error checking in this section with the output method we saw earlier – showing an output container.
You can try my solution to see how it works. But don’t look at the source code until you try it yourself first!
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
So far, we have used simple alert() statements to report errors. But we can do better.
Let’s change the age page. The new page starts off like this:

Figure 13. Empty form
When there is an error, a message appears in the content of the page, right below the field the message is about. Like this:

Figure 14. Showing an error
Try the page. You’ll see that the error message appears with an animated effect.
Here is the code.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Check age 2</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
body {
font-family: Verdana, sans-serif;
font-size: 14px;
background-color: #FFFFE0;
}
#error_message {
font-weight: bold;
color: red;
}
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#age").focus();
$("#error_message").hide();
$("#go").click(function(){
var age = $("#age").val();
if ( age == "" ) {
$("#error_message").text("Sorry, please enter your age.");
$("#error_message").show('medium');
}
else if ( isNaN(age) ) {
$("#error_message").text("Sorry, please enter a number.");
$("#error_message").show('medium');
}
else if ( age < 0 || age > 120 ) {
$("#error_message").text("Sorry, please enter a valid age.");
$("#error_message").show('medium');
}
else if ( age < 21 ) {
$("#error_message").text("Sorry, you are too young.");
$("#error_message").show('medium');
}
else {
$("#error_message").hide();
$("#ok_message").text("Thanks! You can proceed.");
}
$("#age").focus();
});
});
</script>
</head>
<body>
<h1>Check age 2</h1>
<p>
Age:
<input type="text" id="age" size="3"><br>
<span id="error_message"/>
</p>
<p>
<button type="button" id="go">Go</button>
</p>
<p id="ok_message"/>
</body>
</html>
Figure 15. Code for improved error reporting
Let’s look at the HTML first. There’s a place for an error message after the input field (see line 53):
<input type="text" id="age" size="3"><br>
<span id="error_message"/>
The span will show an error message when there is one.
Note that you can use <span id="error_message"/> as a shortcut for <span id="error_message"></span>, when the tag is empty.
The error message is hidden when the page loads:
$(document).ready(function() {
...
$("#error_message").hide();
error_message is highlighted by some CSS (lines 12 to 15).
#error_message {
font-weight: bold;
color: red;
}
If there is an error, some text is put into error_message, and it is shown. Here are lines 25 and 26:
$("#error_message").text("Sorry, please enter your age.");
$("#error_message").show('medium');
All of the errors are handled the same way. For example:
$("#error_message").text("Sorry, please enter a number.");
$("#error_message").show('medium');
What if there is no error? Here’s what happens (line 41):
$("#error_message").hide();
$("#ok_message").text("Thanks! You can proceed.");
This hides whatever error message was showing (if any). Then an “OK” message shows in a different place.
Create a page that looks like this when first displayed:

Figure 1. Empty form
Notice that the input focus is in the first field.
When the user clicks the button, JavaScript code checks the value in the field. The field cannot be empty. It must contain a value that is longer than one character, and shorter than 21 characters.
Here’s an error message:

Figure 2. Error
If the data is OK, show this:

Figure 3. Data OK
You can try my solution to see how it works. But don’t look at the source code until you try it yourself first!
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
functionHave another look at the error checking code.
var age = $("#age").val();
if ( age == "" ) {
$("#error_message").text("Sorry, please enter your age.");
$("#error_message").show('medium');
}
else if ( isNaN(age) ) {
$("#error_message").text("Sorry, please enter a number.");
$("#error_message").show('medium');
}
else if ( age < 0 || age > 120 ) {
$("#error_message").text("Sorry, please enter a valid age.");
$("#error_message").show('medium');
}
else if ( age < 21 ) {
$("#error_message").text("Sorry, you are too young.");
$("#error_message").show('medium');
}
else {
Part of Figure 15 (again). Code for improved error reporting
There’s a lot of repeated code:
$("#error_message").text(A message);
$("#error_message").show('medium');
It would be better to create a function. Here’s the new code:
var age = $("#age").val();
if ( age == "" ) {
show_error("Sorry, please enter your age.");
}
else if ( isNaN(age) ) {
show_error("Sorry, please enter a number.");
}
else if ( age < 0 || age > 120 ) {
show_error("Sorry, please enter a valid age.");
}
else if ( age < 21 ) {
show_error("Sorry, you are too young.");
}
else {
...
//Show an error message.
function show_error(message) {
$("#error_message").text(message);
$("#error_message").show('medium');
}
Figure 16. Error function
This moves the repeated code into a function. Whatever message you send to the function will show in error_message. For example:
show_error("Sorry, you have fleas.");
show_error("Sorry, your " + toy_name + " is broken.");
The main advantage of this is that we can change the error processing all at once. For example, suppose we want to change the animation speed of all the error messages. So we want:
$("#error_message").show('medium');
to be:
$("#error_message").show('slow');
In Figure 15, we have several lines to change. It would be easy to miss one.
But with the function, we just change medium to slow on one line:
function show_error(message) {
$("#error_message").text(message);
$("#error_message").show('slow');
}
Functions give us another productivity win when changing a page.
Create a page that looks like this when first displayed:

Figure 1. Empty form
Notice that the input focus is in the first field.
When the user clicks the button, JavaScript code checks the value in the field. The field cannot be empty. It must contain a value that is longer than one character, and shorter than 21 characters.
Here’s an error message:

Figure 2. Error
Use a function to show error messages. Call the same function for every error message.
If the data is OK, show this:

Figure 3. Data OK
You can try my solution to see how it works. But don’t look at the source code until you try it yourself first!
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
W00f!
In this lesson, you learned:
This lesson looked at client-side validation. Let’s start learning about server-side validation.
We just looked at client-side validation with JavaScript. Time to start working on the server side.
By the end of this lesson, you should:
You need to validate form data on the server (with PHP) as well as on the client (with JavaScript). Why? Three reasons.
Reason 1: There are some checks you can only do on the server.
Suppose you have an online store selling dog toys. A user enters the number of toys s/he wants:

Figure 1. Ordering toy
Maybe you’ve sold out of the Giant Chew Rope. You want to tell the user:

Figure 2. Out of stock
The code for this will be something like:
if number_ordered > current_inventory then
(This is pseudocode, of course.)
You get number_ordered from the form the user filled out. But where does current_inventory come from?
As you’ll learn later, you look that up in a database. This is typical:

Figure 3. DB lookup
The if statement above is usually done by PHP code on the server, not the browser. The server has access to the database. The browser does not.
Reason 2: Security.
This is the second reason why you do validation on the server as well as the browser.
Remember that every page a user sees in his or her browser is downloaded to his or her computer. That includes the JavaScript that has the validation code.
A clever hacker might be able to create a new version of your page, without the JavaScript checking. S/he could then fool your server into accepting invalid data.
Reason 3: Coding mistakes.
You might make a mistake coding the JavaScript. For example, you might write:
if ( age = "" ) { //WRONG!
Tell the user the field is empty
}
else {
Accept the data
}
The first line is wrong. It should be:
if ( age == "" ) {
An easy thing to miss, but it could mean that you get bad data in your databases. This can mess up sales, event registration, or whatever business your Web site supports.
So, even though you know how to do validation by writing JavaScript that runs on a browser, you also need to know how to do it on a server. Because:
Let’s start out easy (as usual). Let’s make this:

Figure 4. Empty order form
The user types a number a clicks the button:

Figure 5. Order form with data
S/he sees this:

Figure 6. Order output
But what if the user enters an invalid number?

Figure 7. Bad input
This is the result:

Figure 8. Result from bad input
You can try it.
Here’s the page:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Order Processing</title>
</head>
<body>
<h1>Order Processing</h1>
<?php
//Get the input.
$giant_chew_ropes = $_POST['giant_chew_ropes'];
//Validate input.
if ( ! is_numeric($giant_chew_ropes) ) {
print '<p>Invalid number.</p>';
print '<p>Please click the Back button on your browser and try again.</p>';
}
else {
//Input OK, show the order.
//Compute total.
$giant_chew_ropes_total = $giant_chew_ropes * 12.95;
//Output total.
?>
<p>Thank you for your order.</p>
<table cellpadding="5" cellspacing="0" border="1">
<tr>
<th>Product</th>
<th>Number<br>ordered</th>
<th>Unit price</th>
<th>Total price</th>
</tr>
<tr>
<td>Giant chew ropes</td>
<td><?php print $giant_chew_ropes; ?></td>
<td>$12.95</td>
<td>$<?php print number_format($giant_chew_ropes_total, 2); ?></td>
</tr>
</table>
<?php
}
?>
</body>
</html>
Figure 9. Simple validation
Line 11 gets the value the user typed into the form field:
$giant_chew_ropes = $_POST['giant_chew_ropes'];
The if statement in line 13 checks it:
if ( ! is_numeric($giant_chew_ropes) ) {
print '<p>Invalid number.</p>';
print '<p>Please click the Back button on your browser and try again.</p>';
}
else {
Do this if number is OK.
}
The function is_numeric() is true if the argument it’s given is a valid number. Otherwise, it returns false. Recall that ! means “not.” So, if $giant_chew_ropes is not numeric, then output the HTML for an error message.
Remember the JavaScript isNaN() function? It returns true when a value is not a number. So isNaN() is like a backwards is_numeric(). is_numeric() is true when a value is a number, while isNaN() is true when a value is not a number.
There some other things to notice about Figure 9. Look at the else on line 17. There’s a lot of code in the else, all the way up to line 39. That’s OK. You can put as much code as you like in braces.
Now look at 22. This switches back from PHP to HTML mode. But it’s still inside the else! So the <table> is only shown if the user’s input passes the validation test.
The opening brace ({) in line 17 needs a closing brace (}) to go along with it. You can see that in line 39. But notice that the brace is a PHP brace. So you need to switch back to PHP mode to close the brace in line 17.
Switching modes like this is very common in PHP land. But it can be tricky for beginners. Remember that when you need to close a brace, make sure you’re in PHP mode.
Create a page with a form:

Figure 1. Input
Send the form data to a PHP page. If the user enters an invalid number, show this:

Figure 2. Error
If there’s a valid number, show this:

Figure 3. Output
You can see my solution. You can also download the files. Of course, do the exercise yourself before you look at my solution.
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
You learned:
Let’s see how you can do better validation.
You learned to send a form value to a PHP page, and do one check on it. Let’s see how to do a bunch of checks.
Here’s the order form again:

Figure 1. Order form
On the previous page we checked whether the user typed a non-numeric value into the field. But there are other errors the user could make, like:
Let’s improve the validation, so that it checks for these things. You can try the improved version.
Here’s the validation code:
//Get the input.
$giant_chew_ropes = $_POST['giant_chew_ropes'];
//Validate input.
$error_message = '';
if ( $giant_chew_ropes == '' ) {
$error_message = 'Please enter the number you want to order.';
}
else if ( ! is_numeric($giant_chew_ropes) ) {
$error_message = 'Please enter a valid number.';
}
else if ( $giant_chew_ropes <= 0 ) {
$error_message = 'Please enter a positive number.';
}
else if ( $giant_chew_ropes > 10 ) {
$error_message = 'Sorry, we don\'t have that many in stock.';
}
//Any error made?
if ( $error_message != '' ) {
print "<p>$error_message</p>";
print '<p>Please click the Back button on your browser and try again.</p>';
}
else {
//Input OK, show the order.
...
Figure 2. Improved validation code
The first thing to notice is the variable $error_message. It’s created on line 13, and set to an empty string.
$error_message = '';
Then there’s a bunch of error checking if statements. Any one of them could find an error, and give $error_message a value, like this:
if (there is something wrong) {
$error_message = 'An error message';
}
There can be as many error checks as are needed: 3, 8, 18, whatever.
What happens after all of the error checks have been run? If there is any error at all, then $error_message will have some text in it. If there have been no errors, then $error_message will have the same empty string it started with.
This is checked in line 27:
if ( $error_message != '' ) {
print "<p>$error_message</p>";
print '<p>Please click the Back button on your browser and try again.</p>';
}
If $error_message does equal an empty string, it means that one of the if statement found an error, and put something in $error_message.
Line 28 has double quotes (”) in it:
print "<p>$error_message</p>";
Remember, this means that PHP will insert the value of the variable $error_message before printing anything.
Notice how flexible this is. We can add and remove as many error checks as we like. As long as each one uses $error_message, it will all work.
Here’s line 23:
else if ( $giant_chew_ropes > 10 ) {
This is the inventory level check. It assumes that we have 10 items to sell. In a real program, the 10 would probably be pulled from a database.
Actually, in a real business, we would probably allow back orders.
Create a page with a form:

Figure 1. Input
Send the form data to a PHP page. If the user enters an invalid number, show an error message:

Figure 2. Error
Check for:
If there’s a valid number, show this:

Figure 3. Output
You can see my solution. You can also download the files. Of course, do the exercise yourself before you look at my solution.
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
In this lesson, you learned how to do a sequence of checks on a value passed to a PHP page.
You’ve learned a lot about validation. But we still need to make it better. For example, one problem is that, when there is an error, you get a message like this:

Figure 3. Error message
This isn’t very good. Let’s make it better, more like something you see in a real application.
But before we do that, let’s take a detour, and talk about functions in PHP. It will make our work easier.
You know how to run a bunch o’ validation in PHP. We can improve the code by using functions. Let’s see how.
By the end of this lesson, you should:
A function is a piece of code that has three things:
validate_order, or compute_area. Functions do three Good Things for us.
Let’s see an example. Here’s a page that computes the area of some rectangles.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Rectangle Areas</title>
</head>
<body>
<h1>Rectangle Areas</h1>
<?php
$r1_width = 7;
$r1_length = 3;
$r1_area = rectangle_area($r1_width, $r1_length);
print "<p>A rectangle with sides of $r1_width and $r1_length has an area of $r1_area.</p>";
$r2_width = 5;
$r2_length = 6;
$r2_area = rectangle_area($r2_width, $r2_length);
print "<p>A rectangle with sides of $r2_width and $r2_length has an area of $r2_area.</p>";
function rectangle_area($width, $length) {
$area = $width * $length;
return $area;
}
?>
</body>
</html>
Figure 1. Area code
The functions starts in line 20. Its name is rectangle_area. It has two input parameters: $width, and $length. The return statement stops the function, and sends back some data.
A function call sends some data to the function, and (maybe) does something with the result that comes back from the function’s return statement. For example:
$r1_area = rectangle_area($r1_width, $r1_length);
The values in $r1_width and $r1_length go into the function. The return statement sends back a result, which gets stored in $r1_area.
The arguments in the call and the function match by position, not name.

Figure 2. Argument matching
That’s why the function can be called with different variables each time.
You can try the program.
Here’s part of Figure 1 again:
<?php
$r1_width = 7;
$r1_length = 3;
$r1_area = rectangle_area($r1_width, $r1_length);
print "<p>A rectangle with sides of $r1_width and $r1_length has an area of $r1_area.</p>";
$r2_width = 5;
$r2_length = 6;
$r2_area = rectangle_area($r2_width, $r2_length);
print "<p>A rectangle with sides of $r2_width and $r2_length has an area of $r2_area.</p>";
function rectangle_area($width, $length) {
$area = $width * $length;
return $area;
}
?>
Figure 1 (again). Area code
The function is after the rest of the code on the page. But it can be in other places as well. For example, it could be above the other code, like this:
<?php
function rectangle_area($width, $length) {
$area = $width * $length;
return $area;
}
$r1_width = 7;
$r1_length = 3;
$r1_area = rectangle_area($r1_width, $r1_length);
print "<p>A rectangle with sides of $r1_width and $r1_length has an area of $r1_area.</p>";
$r2_width = 5;
$r2_length = 6;
$r2_area = rectangle_area($r2_width, $r2_length);
print "<p>A rectangle with sides of $r2_width and $r2_length has an area of $r2_area.</p>";
?>
Figure 3. Moving the function up
Or it could be in the middle:
<?php
$r1_width = 7;
$r1_length = 3;
$r1_area = rectangle_area($r1_width, $r1_length);
print "<p>A rectangle with sides of $r1_width and $r1_length has an area of $r1_area.</p>";
function rectangle_area($width, $length) {
$area = $width * $length;
return $area;
}
$r2_width = 5;
$r2_length = 6;
$r2_area = rectangle_area($r2_width, $r2_length);
print "<p>A rectangle with sides of $r2_width and $r2_length has an area of $r2_area.</p>";
?>
Figure 4. Moving the function to the middle
The function can even be in another file. As we’ll see.
The function rectangle_area works, but is so simple that it is hardly worth using. Let’s change things by:
Here is the page:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Normal probability density function</title>
</head>
<body>
<?php
require 'normal-pdf.inc';
print normal_pdf(.5, 0, 1);
?>
</body>
</html>
Figure 5. Another example
Line 10 calls a function called normal_pdf and sends it three arguments. It prints the return value. Note that you can do anything with the return value: print it, put it in a variable, use it in an if statement, and so on.
The function isn’t in Figure 5. It’s in a separate file, normal-pdf.inc. Here’s the contents of that file.
<?php
function normal_pdf($x, $mu, $sigma) {
$denominator = sqrt(2 * pi()) * $sigma;
$numerator = exp(-($x - $mu) * ($x - $mu) / ( 2 * $sigma));
$density = $numerator / $denominator;
return $density;
}
?>
Figure 6. The function
Being in a separate file, it can be inserted into any number of PHP pages. And it’s a lot more complex than rectangle_area.
The function returns a probability value from a normal distribution. It was adapted from code on this page.
When variables are created inside a function, they are called “local variables.” They are not available outside the function. They are destroyed when the function exits. So if you tried to print the variable $denominator outside the function, you would get an error. It only exists inside the function.
This is a Good Thing. It means you can write a program like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Normal probability density function</title>
</head>
<body>
<?php
require 'normal-pdf.inc';
$numerator = 1;
$denominator = 2;
$p = normal_pdf($numerator/$denominator, 0, 1);
?>
<p>Numerator: <?php print $numerator; ?></p>
<p>Denominator: <?php print $denominator; ?></p>
<p>p: <?php print $p; ?></p>
</body>
</html>
Figure 7. Local variables in action
If you try the program, you will see that it works. Neither $numerator nor $denominator – names that appear in both the function and the code that calls it – get clobbered.
Why is there no confusion between $numerator in line 10 of Figure 7, and $numerator in line 4 of the inserted file, shown in Figure 6? When the function normal_pdf runs, it creates a new $numerator variable all of its own. It gets wiped out when the function exits.
Why is this a Good Thing? Because it makes it easier to use function libraries. You can insert normal-pdf.inc into any PHP file you want, without worrying about whether variables will get hurt. W00f!
Here’s another one:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Farm animal plurals</title>
</head>
<body>
<h1>Farm animal plurals</h1>
<p>The plural of "dog" is <?php print farm_animal_plural('dog'); ?>.</p>
<p>The plural of "cat" is <?php print farm_animal_plural('cat'); ?>.</p>
<p>The plural of "goose" is <?php print farm_animal_plural('goose'); ?>.</p>
<?php
function farm_animal_plural($animal) {
if ( $animal == 'sheep' ) {
return 'sheep';
}
if ( $animal == 'goose' ) {
return 'geese';
}
if ( $animal == 'fish' ) {
return 'fish';
}
return $animal . 's';
}
?>
</body>
</html>
Figure 8. String function
The function takes the name of a farm animal, and returns the plural. It deals with a few special cases, and then just returns the input value, with an “s” attached.
It isn’t a very good function.
Some things to notice about this example:
return statements. As soon as one is encountered, PHP exits. This lesson explains:
Let’s see how you can write a PHP validation function.
You know how to do some validation. You know what a PHP function is. Let’s put them together, and create a PHP validation function.
By the end of this lesson, you should:
Suppose we have two products. Here is a form:

Figure 1. Form
As usual, the user enters the number of each product s/he wants, and clicks the button.
With the data in Figure 1, the user would get:

Figure 2. Output
But what if the user typed this?

Figure 3. Form with bad data
S/he would see:

Figure 4. Error report
Notice that each product has its own error line.
You can try the program. You can also download the files.
The same tests apply to both fields. For example:
We could have separate code to test each field. But that would duplicate work. Instead, let’s use the same function to test both fields.
Here’s the function:
//Check the number ordered.
//Input:
// $number_ordered: The number the user wants.
// $number_on_hand: The number in inventory.
//Returns:
// An error message, or an empty string if there was no error.
function check_number_ordered($number_ordered, $number_on_hand) {
if ( $number_ordered == '' ) {
return 'Please enter the number you want to order.';
}
else if ( ! is_numeric($number_ordered) ) {
return 'Please enter a valid number.';
}
else if ( $number_ordered <= 0 ) {
return 'Please enter a positive number.';
}
else if ( $number_ordered > $number_on_hand ) {
return 'Sorry, we don\'t have that many in stock.';
}
return '';
}
Figure 5. Validation function
The comments from lines 1 to 6 explain what the function does and how to use it. They describe the parameters going into the function, and the return value that comes out.
We need two pieces of information to check whether the number ordered is valid. First, we need the number ordered. We can do most of the checks just with that. But we also need to know the number on hand, for one of the checks. That’s why the function has two parameters.
There are four if statements, each one checking for a different error. Let’s look at the first one.
if ( $number_ordered == '' ) {
return 'Please enter the number you want to order.';
}
If $number_ordered is empty, the function ends immediately, sending back a message describing the error. It doesn’t get beyond line 9. Otherwise, the next if statement runs.
If there are no errors, none of the returns in the if statements will be run. So what do we do? The last line of the function is:
return '';
If the program gets to this point, none of the if statements has been true (because otherwise one of them would have exited the function already). That means there are no errors; the data is valid. The function returns an empty string, to show that there were no errors.
We could have done something else to show there were no errors. For example, we could have returned “NO ERRORS!” Returning an empty string is common practice, but not the only choice.
Let’s see how the function is used.
Here’s the code, with some less interesting stuff omitted:
<h1>Order Processing</h1>
<?php
//Get the input.
$frisbees = $_POST['frisbees'];
$giant_chew_ropes = $_POST['giant_chew_ropes'];
//Validate input.
$error_message_frisbees = check_number_ordered($frisbees, 15);
if ( $error_message_frisbees != '' ) {
print "<p>Frisbees: $error_message_frisbees";
}
$error_message_giant_chew_ropes = check_number_ordered($giant_chew_ropes, 10);
if ( $error_message_giant_chew_ropes != '' ) {
print "<p>Giant chew ropes: $error_message_giant_chew_ropes";
}
//Any error made?
if ( $error_message_frisbees != '' || $error_message_giant_chew_ropes != '') {
print '<p>Please click the Back button on your browser and try again.</p>';
}
else {
//Input OK, show the order.
//Compute total.
$frisbees_total = $frisbees * 8.95;
$giant_chew_ropes_total = $giant_chew_ropes * 12.95;
$order_total = $frisbees_total + $giant_chew_ropes_total;
//Output.
?>
<p>Thank you for your order.</p>
...
Figure 6. Calling the function
Line 14 is:
$error_message_frisbees = check_number_ordered($frisbees, 15);
This calls the function for the frisbees, and puts the return value in $error_message_frisbees. Line 18 is:
$error_message_giant_chew_ropes = check_number_ordered($giant_chew_ropes, 10);
This does the same thing for giant chew ropes. It puts the function’s return value in a different variable, $error_message_giant_chew_ropes.
As you can see, we’ve used the same validation function both times. If we had, say, 23 products on the page, we’d call check_number_ordered() 23 times. But we’d only write it once. If we needed to change the code, we’d only need to change one thing, and all the checks on the page would change.
Here are lines 15 to 17 in Figure 6:
if ( $error_message_frisbees != '' ) {
print "<p>Frisbees: $error_message_frisbees";
}
This checks to see what the function sent back for frisbees. If it sent back an empty string (''), there was no error. If the function sent back something else, there was an error. Line 16 shows it.
The same check is done for $error_message_giant_chew_ropes is lines 18 to 21.
Now, if there was an error, any error at all, we want to ask the user to go back to the order page. It doesn’t matter which field the error was in. Could be with frisbees, or giant ropes, or both.
Look at lines 23 to 25:
if ( $error_message_frisbees != '' || $error_message_giant_chew_ropes != '') {
print '<p>Please click the Back button on your browser and try again.</p>';
}
Line 23 says “if $error_message_frisbee is not empty, or $error_message_giant_chew_ropes is not empty, then ask the user to go back.”
This is what we want. If there is any error at all, go back to the order page. We only show the order output if there were no errors.
You learn:
All of our examples use two pages:
Webers often use one page for both input and server-side validation. Let’s see how that’s done.
[ learningexercise:? ]
You know how to get data from a form, and validate it. You know how to use a validation function. Let’s improve the way that’s done. Again.
By the end of this lesson, you should:
Up to now, we’ve separated the page with the input form from the page that does validation and processing. Like this:

Figure 1. Pattern so far
The .hmtl page has a <form>. The action attribute of the form has the name of the processing page. When the user clicks the submit button, the form data is sent to the processing page.
The processing page validates the data. If there is a problem, it shows a message to the user, and suggests going back to the order form again.
If all the form data is OK, the order is processed. This means telling the shipping department to send the order, processing payment, and other businessy things.
But there’s another way of doing things. The new pattern is to put both the form and the server-side PHP validation code on the same page.
The strangest thing about this is that the page with the form submits data to itself.

Figure 2. A new pattern
This is helpful when you do both client-side and server-side validation, and want to show both types of errors to the user in the same way. The new pattern lets you be more consistent in the way you show errors to users.
The key to this is that the page works in two modes:
The page uses if statements to act differently, depending on which mode it’s in.
Let’s be sure we know what I mean by “modes” here. You’re used to thinking about modes that are built in to devices. For example, you might have an SUV that works in two-wheel or four-wheel drive. Two different modes. You use a switch (or whatever) to choose the mode you want.
The engineers who designed the vehicle built those modes into it.
Another example is the camera in your cell phone. It can work in still photo mode. Or it can work in video mode, where it shoots movies. You choose the modes you want.
The engineers who designed the cell phone built those modes into it.
But when you write PHP, you are the engineer. You get to create modes yourself. You decide:
You do this by the way you write your code. There is no special “mode” statement in PHP. The modes are created by the way you put together if statements and other things.
Let’s see how all this works.
Here’s an empty order form, from order.php.

Figure 3. Empty order form
Suppose the user enters some bad data into order.php:

Figure 4. Bad data
The form data is sent from order.php to order.php; the page sends the form data to itself. Here is what it shows:

Figure 5. Error messages
Now, what if the user enters valid data?

Figure 6. Valid data
Here is the result:

Figure 7. Order confirmation
You can try it. Enter different combinations of good and bad data into the fields, and see what you get.
Here’s order.php.
<?php
if ( $_POST ) {
//There is order data to validate.
require 'validation-library.inc';
//Warning: Extra characters at the end of validation-library.inc
//will break this page!
//Get the order amounts.
$frisbees = $_POST['frisbees'];
$giant_chew_ropes = $_POST['giant_chew_ropes'];
//Validate input.
$error_message_frisbees = check_number_ordered($frisbees, 15);
$error_message_giant_chew_ropes = check_number_ordered($giant_chew_ropes, 10);
//Jump if no errors.
if ( $error_message_frisbees == '' && $error_message_giant_chew_ropes == '') {
$destination_url = "process.php?frisbees=$frisbees&giant_chew_ropes=$giant_chew_ropes";
header("Location:$destination_url");
exit();
}
}?><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Order Form</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>Order Form</h1>
<?php
//Any error messages to display?
if ( $error_message_frisbees != '' ) {
print "<p>Frisbees: $error_message_frisbees</p>";
}
if ( $error_message_giant_chew_ropes != '' ) {
print "<p>Giant chew ropes: $error_message_giant_chew_ropes</p>";
}
?>
<form method="post" action="order.php">
<p>
Frisbees:
<input type="text" name="frisbees" size="3"
<?php
if ( $_POST['frisbees'] ) {
print ' value="' . $_POST['frisbees'] . '"';
}
?>
>
at $8.95 each
</p>
<p>
Giant chew ropes:
<input type="text" name="giant_chew_ropes" size="3"
<?php
if ( $_POST['giant_chew_ropes'] ) {
print ' value="' . $_POST['giant_chew_ropes'] . '"';
}
?>
>
at $12.95 each
</p>
<p>
<button type="submit">Order</button>
</p>
</form>
</body>
</html>
Figure 8. order.php
Line 2:
if ( $_POST ) {
checks whether there is any form data coming in. If there is no post data, then $_POST will be empty, which, in PHP, is lihe bring false.
This is the mode check, if you want to think of it that way. If there is post data, we enter “check input” mode. Otherwise, we’re in “empty form” mode.
We created a validation function on the previous page. We’ll put it in a separate file, and bring it in like this (lines 4):
require 'validation-library.inc';
We’ll talk about the warning in lines 5 and 6 later.
Lines 8 and 9 get the form data into variables, as usual. Here’s one of the lines:
$frisbees = $_POST['frisbees'];
As before, the function check_number_ordered() checks the value passed in, and returns either an error message or a blank string (line 11):
$error_message_frisbees = check_number_ordered($frisbees, 15);
The next step is to check whether there are any errors. There were no errors if both the frisbees value and the giant ropes value were OK.
Here is the code:
if ( $error_message_frisbees == '' && $error_message_giant_chew_ropes == '') {
$destination_url = "process.php?frisbees=$frisbees&giant_chew_ropes=$giant_chew_ropes";
header("Location:$destination_url");
exit();
}
Part of Figure 8 (again). order.php
Line 14 has the test. Remember that the validation function check_number_ordered() returns an empty string. So that if both $error_message_frisbees and $error_message_giant_chew_ropes are empty strings, there were no errors.
If the test is true, we need to tell the browser to jump to the order processing page (order.php). To jump to another page, send the HTTP header location, like this:
header('location: Destination ');
So to jump to the order processing page (line 16):
header('location: order.php');
But we also need to send the order data to order.php. How?
By doing a pretend get.
Huh?
Remember that there are two ways to send form data:
<form method="get">
and
<form method="post">
It’s the get method we care about here.
The get method attaches form data to the URL, like this:
order.php?frisbees=2&giant_chew_ropes=4
The destination page – order.php – can retrieve the data like this:
$frisbees = $_GET['frisbees'];
$giant_chew_ropes = $_GET['giant_chew_ropes'];
OK, so how to send the data? We make a pretend get by attaching data to the URL ourselves, rather than having a form do it. Like this:
$destination_url = "process.php?frisbees=$frisbees&giant_chew_ropes=$giant_chew_ropes";
Remember that PHP replaces variables with their values in strings with double quotes (”). So if $frisbees had 2 in it, and $giant_chew_ropes had 4 in it, we would get”
$destination_url = "process.php?frisbees=2&giant_chew_ropes=4";
This is the same URL that a form with method="get" would produce. W00f!
Cool!
Once we have the URL, we can send it to the browser:
header("Location:$destination_url");
There is no point processing the rest of the page, so the next line is:
exit();
This tells the PHP interpreter to stop processing the page immediately, and return whatever it has so far to the Web server. All that it has is the header. The Web server returns that to the browser. The browser jumps to process.php, sending the order along for the ride.
If there are errors, we want this:

Figure 5 (again). Error messages
We need to do three things:
Here’s how the error messages are shown.
<h1>Order Form</h1>
<?php
//Any error messages to display?
if ( $error_message_frisbees != '' ) {
print "<p>Frisbees: $error_message_frisbees</p>";
}
if ( $error_message_giant_chew_ropes != '' ) {
print "<p>Giant chew ropes: $error_message_giant_chew_ropes</p>";
}
?>
Part of Figure 8 (again). order.php
Now the second thing, showing the form. We use the usual HTML tags: <form>, <input>, etc.
The third thing we need to do is restore the data the user had typed into the fields. Whether the data was valid or not.
Why put data the user typed back into the input fields? Especially when some of it is invalid.
Two reasons.
So how to do it? You need to know about another attribute of the <input> tag: value. It lets you set the contents of a form field in HTML, without the user having to type anything. For example, here’s some HTML:
<input type="text" value="Dogs rock!">
This renders as:

Figure 9. value attribute
You can try it. Notice that text appears in the field, without your typing anything.
Here is some code for our order form:
Frisbees:
<input type="text" name="frisbees" size="3"
<?php
if ( $_POST['frisbees'] ) {
print ' value="' . $_POST['frisbees'] . '"';
}
?>
>
Part of Figure 8 (again). order.php
If $_POST['frisbees'] has, say, 7 in it, the code will create the HTML:
<input type="text" name="frisbees" size="3" value="7">
That’s the end of the page. W00f!
Here’s the error checking and reporting code.
//Validate input.
$error_message_frisbees = check_number_ordered($frisbees, 15);
$error_message_giant_chew_ropes = check_number_ordered($giant_chew_ropes, 10);
//Jump if no errors.
if ( $error_message_frisbees == '' && $error_message_giant_chew_ropes == '') {
...
//Any error messages to display?
if ( $error_message_frisbees != '' ) {
print "<p>Frisbees: $error_message_frisbees</p>";
}
if ( $error_message_giant_chew_ropes != '' ) {
print "<p>Giant chew ropes: $error_message_giant_chew_ropes</p>";
}
Part of Figure 8 (again). order.php
In this code, we have a different error message variable for each product. That’s fine, because we have only two products.
What if we had more than two products? For each one, we’d need to create a new variable. And Line 14 would get more complex. And we’d need to remember to add another if statement to report the error.
Here’s a better way of doing it.
//Validate input.
$messages = ''; //Variable to accumulate messages.
$error_message = check_number_ordered($frisbees, 15);
if ( $error_message != '' ) {
$messages .= "<p>Frisbees: $error_message</p>";
}
$error_message = check_number_ordered($giant_chew_ropes, 10);
if ( $error_message != '' ) {
$messages .= "<p>Giant chew ropes: $error_message</p>";
}
//Jump if no errors.
if ( $messages == '' ) {
...
<h1>Order Form</h1>
<?php
//Any error messages to display?
if ( $messages != '' ) {
print $messages;
}
?>
Figure 10. Improved error reporting
Line 11 creates a variable called $messages. It’s used to collect error messages. When a new error is detected, some text is added to this variable.
Line 12 checks the frisbees:
$error_message = check_number_ordered($frisbees, 15);
Remember that check_number_ordered() returns an empty string if $frisbees is OK, otherwise it returns an error message.
Here’s the next bit of code:
if ( $error_message != '' ) {
$messages .= "<p>Frisbees: $error_message</p>";
}
.= is a shortcut operator. Remember that . means string concatenation, so:
'this' . 'that'
is:
thisthat
The line:
$x .= $y;
is a shortcut for:
$x = $x . $y;
So .= appends something to an existing variable.
This line:
$messages .= "<p>Frisbees: $error_message</p>";
creates an HTML paragraph tag with an error message in it, and adds it to whatever is already in $messages.
The next few lines are:
$error_message = check_number_ordered($giant_chew_ropes, 10);
if ( $error_message != '' ) {
$messages .= "<p>Giant chew ropes: $error_message</p>";
}
If there is a problem with $giant_chew_ropes, an error message gets added to whatever is already in $messages.
If more products are added to the page, we can just repeat these few lines as needed. So if, for example, we started selling dog collars, we would have:
$error_message = check_number_ordered($dog_collars, 8);
if ( $error_message != '' ) {
$messages .= "<p>Dog collars: $error_message</p>";
}
There is no need to create another variable for a dog collar error message. If there is an error, just add the new message to the other messages.
After checking each individual form field, we need an if statement to decide whether to jump to the order processing page. The browser should jump only if there were no errors.
Here’s what we had in the original code:
if ( $error_message_frisbees == '' && $error_message_giant_chew_ropes == '') {
Each time we add a new product, we’d have to change this line. For example, if we had five products:
if ( $error_message_frisbees == '' && $error_message_giant_chew_ropes == '' && $error_message_dog_collars == '' && $error_message_squeaky_balls == '' && $error_message_hide_bone == '' ) {
Ack! What a pain!
But with the new approach, we don’t need to do that. All error messages are added to the variable $messages. So if we want to check whether there have been any errors, we can just check that one variable (line 21 in Figure 10):
if ( $messages == '' ) {
Ten products? Twenty? It doesn’t matter. This line will never change.
When it comes to reporting errors to the user, we have this code:
if ( $messages != '' ) {
print $messages;
}
Again, it doesn’t matter how many products there are, or how many potential error messages. Our code has collected all the error messages in $messages.
This is another example of how Webers think about productivity. Web pages change. Products are added and removed. This approach – accumulating error messages in a variable – makes it easier to make those changes.
In this lesson, you learned:
Here’s the error message display again:

Figure 5 (again). Error messages
That’s, well, yucky. Let’s improve the user’s experience.
By the end of this lesson, you should:
In the last couple of lessons, we improved the server-side error processing. We came up with something like this:

Figure 1. Ugly error messages
This is, well, blah. Let’s improve it. We want the order form to look like this:

Figure 2. Empty order form
Suppose there are errors:

Figure 3. Order form with errors
Let’s show error messages like this:

Figure 4. Error messages
Try it, and you’ll see the error messages are animated.
Each field has its own place for error messages, right below the field. In addition, at the top of the form, there’s what I am calling a “global error message.” It shows that there is some error on the page.
Why have a global error message? Two reasons.
Let’s change the way a blank field is interpreted. If a user leaves a field blank, we’ll assume the user doesn’t want to order any of the product. We won’t make the user enter zero into the field.
But this creates a problem. Suppose the user leaves all the fields blank and clicks the order button. There’s an error; nothing was ordered, so clicking the order button makes no sense. But the error isn’t associated with any one field.
Having a global error message gives us a place to put an error message like this:

Figure 5. Nothing ordered
You might have a long form with, say, 12 fields. This gives you a long page. The user won’t be able to see all of the fields at the same time.

Figure 6. Browser window
The user has to scroll to see the entire page.
The global error message makes it easier for the user to see whether there are any errors. The small error icon:
helps the user scan down the page and find information about specific errors.
Let’s look at the client side error reporting: the HTML, CSS, and JavaScript. We’ll combine it with PHP in the next lesson.
Let’s look at the HTML code for the global error message. That’s the message at the top of the page, telling the user whether there were any errors at all. Remember that it should look like this when displayed:

Figure 7. Global error message
Here’s the HTML:
<p id="global_error_message_container" class="message_container"> <img src="error.png" alt="Error"> <span id="global_error_message"/> </p>
Figure 8. HTML for the global error message
There’s a <span> inside a <p>. The <span> is used to show the text of the error message. The <p> is a container for both the <span> and an error icon. Showing and hiding the <p> will affect both the icon and the message, treating them as a single unit.
Notice how I have named them. The <span> has an id of global_error_message. The <p> has an id of global_error_message_container. I’ve used that convention throughout the page. The id of the outer element ends in _container.
The icon came from the famfamfam silk icon set. It’s one of the most complete icon sets you can find.
That was the global error message. The HTML for each field’s error message is similar. Here’s one of them.
<p>
<input type="text" name="frisbees" id="frisbees" size="3">
Frisbees ($8.95 each)<br>
<span id="frisbees_message_container" class="message_container">
<img src="error.png" alt="Error">
<span id="frisbees_message"/>
</span>
</p>
Figure 9. HTML for the frisbee message
The error message starts on line 136. Again, there’s an error message element and a container element. This time, the container is a <span>.
The product error messages have an extra naming convention. For product x, the elements are called x_message and x_message_container. So for frisbees, the ids are frisbees_message and frisbees_message_container.
Why do you use these naming rules?
Because it makes programming easier. You’ll see why later.
Here’s the code that’s executed when the user clicks the Order button.
$("#order_button").click(function() {
//Set flag showing everything is OK.
var data_ok = true;
//Check the frisbees.
var frisbees = $("#frisbees").val();
var frisbees_message = check_order_value(frisbees);
if ( frisbees_message != '' ) {
data_ok = false;
show_product_error_message(frisbees_message, 'frisbees');
}
else {
hide_error_message('frisbees');
}
//Check the giant chew ropes.
var giant_chew_ropes = $("#giant_chew_ropes").val();
var giant_chew_ropes_message = check_order_value(giant_chew_ropes);
if ( giant_chew_ropes_message != '' ) {
data_ok = false;
show_product_error_message(giant_chew_ropes_message, 'giant_chew_ropes');
}
else {
hide_error_message('giant_chew_ropes');
}
//Check the squeaky balls.
var squeaky_balls = $("#squeaky_balls").val();
var squeaky_balls_message = check_order_value(squeaky_balls);
if ( squeaky_balls_message != '' ) {
data_ok = false;
show_product_error_message(squeaky_balls_message, 'squeaky_balls');
}
else {
hide_error_message('squeaky_balls');
}
//Make sure something was ordered.
if ( frisbees + giant_chew_ropes + squeaky_balls == 0 ) {
show_global_error_message("Sorry, you must order at least one item.");
data_ok = false;
}
if ( data_ok ) {
hide_global_error_message();
alert('Everything is OK... so far.')
}
});
Figure 10. JavaScript
Line 25:
var data_ok = true;
creates a variable that keeps track of whether the data is OK, that is, whether there has been an error. When we get to the end of the validation, we can check this variable to know whether there have been any errors.
Variables like this are often called “flags.” They are usually either true or false, like a physical flag being up or down.
Flag up, data OK. Variable
data_okistrue.
Flag down, data not OK. Variable
data_okisfalse.
When the flag is up (the data is OK), we want to do one thing. When the flag is down (there has been an error), we want to do another thing.
Look at lines 28 and 29:
var frisbees = $("#frisbees").val();
var frisbees_message = check_order_value(frisbees);
The first one gets the value the user typed into the frisbee field and puts it into the variable frisbees. Line 29 calls the check_order_value() function, sending in frisbees.
check_order_value() is a JavaScript version of the PHP validation function we saw earlier. It does the same thing; returns either an error message, or an empty string if the the value is OK.
Here is the code:
//Check whether an order value is valid.
//Input
// value_to_check: the value to check.
//Return: An error message, or empty string if no error.
function check_order_value(value_to_check) {
if ( isNaN(value_to_check) ) {
return 'Please enter a number';
}
if ( value_to_check != Math.round(value_to_check) ){
return 'Please enter whole numbers only';
}
if ( value_to_check < 0 ){
return 'Please enter positive numbers only';
}
return '';
}
Figure 11. check_order_value()
Line 80 contains something new.:
if ( value_to_check != Math.round(value_to_check) ){
This tests whether value_to_check is an integer, that is, a whole number. Math.round() rounds a number up or down. So:
Math.round(1.4)is 1
Math.round(1.7)is 2
Math.round(1)is 1
If you round a number that is already a whole number, you get the same number you started with. That is why the test works.
You can try round().
Back the to button pressing script. Here’s some more of it.
//Check the frisbees.
var frisbees = $("#frisbees").val();
var frisbees_message = check_order_value(frisbees);
if ( frisbees_message != '' ) {
data_ok = false;
show_product_error_message(frisbees_message, 'frisbees');
}
else {
hide_error_message('frisbees');
}
Part of Figure 10 (again). JavaScript
Line 29 puts either an error message or an empty string in frisbees_message.
If frisbees_message isn’t empty (line 30), the flag gets set to false (line 31), so we can know later that there’s been a problem. Then this is run:
show_product_error_message(frisbees_message, 'frisbees');
This shows the message in the frisbees error spot. It might end up looking like this:

Figure 11. Frisbee error
I want the show_product_error_message() function to work for every product. That’s why I pass in the product name (frisbees in this case), so the function will know where to show the error message.
Here’s what show_product_error_message() does, in pseudocode.
Set the product error text.
Show the text.
Set the global error text.
Show it.
Here’s the code:
//Show an error message for a product.
//Input
// message: The message to show.
// product_name: The product name the message is about.
//Return: nothing.
function show_product_error_message(message, product_name) {
//Show the error message in the product's message area.
$('#' + product_name + '_message').text(message);
if ( $('#' + product_name + '_message_container').is(':hidden') ) {
$('#' + product_name + '_message_container').show('medium');
}
//Show the global error message at the top of the page.
show_global_error_message("Sorry, I can't process this order.");
}
Figure 13. show_product_error_message()
Look at line 96:
$('#' + product_name + '_message').text(message);
product_name is something like frisbee. So line 96 becomes:
$('#frisbee_message').text(message);
This matches the id in the HTML element:
<p>
<input type="text" name="frisbees" id="frisbees" size="3">
Frisbees ($8.95 each)<br>
<span id="frisbees_message_container" class="message_container">
<img src="error.png" alt="Error">
<span id="frisbees_message"/>
</span>
</p>
Figure 9 (again). HTML for the frisbee message
There it is, in line 138.
Aha! So that’s why you used those naming rules!
Yes! That’s it.
Here are the next lines in show_product_error_message() (Figure 12):
if ( $('#' + product_name + '_message_container').is(':hidden') ) {
$('#' + product_name + '_message_container').show('medium');
}
Part of Figure 12 (again). show_product_error_message()
This will detect whether the message’s container is hidden, and, if it is, show it.
Remember the pseudocode.
Set the product error text.
Show the text.
Set the global error text.
Show it.
We’ve done the first two. How about the last two? Like this:
show_global_error_message("Sorry, I can't process this order.")
That’s right, another function.
//Show a global error message. It applies to the entire
// page, not just one product.
//Input
// message: The message to show.
//Return: nothing.
function show_global_error_message(message) {
$("#global_error_message").text(message);
if ($('#global_error_message_container' ).is(':hidden') ) {
$('#global_error_message_container').show('medium');
}
}
Figure 13. show_global_error_message()
It’s much like show_product_error_message(), but without the product name part.
Let’s look at the last part of a field’s validation:
//Check the frisbees.
var frisbees = $("#frisbees").val();
var frisbees_message = check_order_value(frisbees);
if ( frisbees_message != '' ) {
data_ok = false;
show_product_error_message(frisbees_message, 'frisbees');
}
else {
hide_error_message('frisbees');
}
Part of Figure 10 (again). JavaScript
If there’s no error, then line 35 runs:
hide_error_message('frisbees');
Here’s the function:
//Hide a product error message.
//Input
// product_name: The product name the message is about.
//Return: nothing.
function hide_error_message(product_name) {
$('#' + product_name + '_message_container').hide('medium');
}
Figure 14. hide_error_message()
Send it a product_name, and it hides the error message for that product.
There is one more check we have to do. What if the user orders none of every product?
//Make sure something was ordered.
if ( frisbees + giant_chew_ropes + squeaky_balls == 0 ) {
show_global_error_message("Sorry, you must order at least one item.");
data_ok = false;
}
Figure 15. Was anything ordered?
What if there are no errors?
if ( data_ok ) {
hide_global_error_message();
alert('Everything is OK... so far.')
}
Figure 16. Nothing wrong
hide_global_error_message() does what you think. Here it is:
//Hide global error message.
//Return: nothing.
function hide_global_error_message() {
if ($('#global_error_message_container' ).is(':visible') ) {
$('#global_error_message_container').hide('medium');
}
}
Figure 17. Hide the global error message
hide() only runs if the error message is visible.
Nothing else happens, except for an alert(). We’ll add the PHP in the next lesson.
Here’s all the code together. Run through it again in your mind, so you see how it all fits together.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Order Form</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
body {
font-family: Verdana, sans-serif;
font-size: 14px;
background-color: #FFFFE0;
}
.message_container {
display: none;
font-weight: bold;
color: red;
}
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#global_error_message_container").hide();
$("#frisbees").focus();
$("#order_button").click(function() {
//Set flag showing everything is OK.
var data_ok = true;
//Check the frisbees.
var frisbees = $("#frisbees").val();
var frisbees_message = check_order_value(frisbees);
if ( frisbees_message != '' ) {
data_ok = false;
show_product_error_message(frisbees_message, 'frisbees');
}
else {
hide_error_message('frisbees');
}
//Check the giant chew ropes.
var giant_chew_ropes = $("#giant_chew_ropes").val();
var giant_chew_ropes_message = check_order_value(giant_chew_ropes);
if ( giant_chew_ropes_message != '' ) {
data_ok = false;
show_product_error_message(giant_chew_ropes_message, 'giant_chew_ropes');
}
else {
hide_error_message('giant_chew_ropes');
}
//Check the squeaky balls.
var squeaky_balls = $("#squeaky_balls").val();
var squeaky_balls_message = check_order_value(squeaky_balls);
if ( squeaky_balls_message != '' ) {
data_ok = false;
show_product_error_message(squeaky_balls_message, 'squeaky_balls');
}
else {
hide_error_message('squeaky_balls');
}
//Make sure something was ordered.
if ( frisbees + giant_chew_ropes + squeaky_balls == 0 ) {
show_global_error_message("Sorry, you must order at least one item.");
data_ok = false;
}
if ( data_ok ) {
hide_global_error_message();
alert('Everything is OK... so far.')
}
});
});
//Check whether an order value is valid.
//Input
// value_to_check: the value to check.
//Return: An error message, or empty string if no error.
function check_order_value(value_to_check) {
if ( isNaN(value_to_check) ) {
return 'Please enter a number';
}
if ( value_to_check != Math.round(value_to_check) ){
return 'Please enter whole numbers only';
}
if ( value_to_check < 0 ){
return 'Please enter positive numbers only';
}
return '';
}
//Show an error message for a product.
//Input
// message: The message to show.
// product_name: The product name the message is about.
//Return: nothing.
function show_product_error_message(message, product_name) {
//Show the error message in the product's message area.
$('#' + product_name + '_message').text(message);
if ( $('#' + product_name + '_message_container').is(':hidden') ) {
$('#' + product_name + '_message_container').show('medium');
}
//Show the global error message at the top of the page.
show_global_error_message("Sorry, I can't process this order.");
}
//Show a global error message. It applies to the entire
// page, not just one product.
//Input
// message: The message to show.
//Return: nothing.
function show_global_error_message(message) {
$("#global_error_message").text(message);
if ($('#global_error_message_container' ).is(':hidden') ) {
$('#global_error_message_container').show('medium');
}
}
//Hide a product error message.
//Input
// product_name: The product name the message is about.
//Return: nothing.
function hide_error_message(product_name) {
$('#' + product_name + '_message_container').hide('medium');
}
//Hide global error message.
//Return: nothing.
function hide_global_error_message() {
if ($('#global_error_message_container' ).is(':visible') ) {
$('#global_error_message_container').hide('medium');
}
}
</script>
</head>
<body>
<h1>Order Form</h1>
<p>What would you like?</p>
<p id="global_error_message_container" class="message_container">
<img src="error.png" alt="Error">
<span id="global_error_message"/>
</p>
<p>
<input type="text" name="frisbees" id="frisbees" size="3">
Frisbees ($8.95 each)<br>
<span id="frisbees_message_container" class="message_container">
<img src="error.png" alt="Error">
<span id="frisbees_message"/>
</span>
</p>
<p>
<input type="text" name="giant_chew_ropes" id="giant_chew_ropes" size="3">
Giant chew ropes ($12.95 each)<br>
<span id="giant_chew_ropes_message_container" class="message_container">
<img src="error.png" alt="Error">
<span id="giant_chew_ropes_message"/>
</span>
</p>
<p>
<input type="text" name="squeaky_balls" id="squeaky_balls" size="3">
Squeaky balls to chase ($1.95 each)<br>
<span id="squeaky_balls_message_container" class="message_container">
<img src="error.png" alt="Error">
<span id="squeaky_balls_message"/>
</span>
</p>
<p>
<button id="order_button" type="button">Order</button>
</p>
</body>
</html>
Figure 18. The entire page
Make sure you try the page. Type in invalid data, and see what happens.
Make a sign-up page for a Web service. It starts out like this:

Figure 1. Empty form
All fields must be completed. Suppose the user does this:

Figure 2. An empty field
Here is the result:

Figure 3. Error messages
Hint: Make a function called check_value() that returns either an error message or an empty string. The only error is an empty field.
You can see my solution, but don’t look at the source code until you do it yourself first!
Upload your solution to your server. Enter the URL below.
(Log in to enter your solution to this exercise.)
You learned:
Let’s put everything together. We’ll do form validation that:
Thanks to Miles Thompson for his help with this lesson.
OK, let’s bring it all together.
Here’s how things start:

Figure 1. Empty form
Client-side checking makes error messages like this:

Figure 2. Error messages
What if the user wants to order more than we have on hand?

Figure 3. Too many
The error message in Figure 3 tells the user how many we have on hand.
Again, this may not be good business practice, but it’s good for learning about PHP.
Try the page. Try making various kinds of errors. You can also download the files.
The overall processing pattern is the same as before:

Figure 4. Overall pattern
The form page passes data to itself for validation. If everything is OK, the page passes the data to a processing page.
There are a few improvements over the client-side only version on the previous page.
order.php codeHere’s the complete code. We’ll talk about the changes in the next section.
<?php
if ( $_POST ) {
//There is order data to process.
require 'validation-library.inc';
//Get the order amounts.
$frisbees = $_POST['frisbees'];
if ( $frisbees == '' ) {
$frisbees = 0;
}
$giant_chew_ropes = $_POST['giant_chew_ropes'];
if ( $giant_chew_ropes == '' ) {
$giant_chew_ropes = 0;
}
$squeaky_balls = $_POST['squeaky_balls'];
if ( !isset($squeaky_balls) || $squeaky_balls == '' ) {
$squeaky_balls = 0;
}
//Flag for data OK.
$data_ok = true;
//JavaScript to run after page load.
$js = '';
//Check frisbees.
$frisbees_message = check_number_ordered($frisbees);
if ( $frisbees_message != '' ) {
$data_ok = false;
$js .= 'show_product_error_message("'.$frisbees_message . '", "frisbees");';
}
//Check giant chew ropes.
$giant_chew_ropes_message = check_number_ordered($giant_chew_ropes);
if ( $giant_chew_ropes_message != '' ) {
$data_ok = false;
$js .= 'show_product_error_message("'.$giant_chew_ropes_message . '", "giant_chew_ropes");';
}
//Check squeaky balls.
$squeaky_balls_message = check_number_ordered($squeaky_balls);
if ( $squeaky_balls_message != '' ) {
$data_ok = false;
$js .= 'show_product_error_message("'.$squeaky_balls_message . '", "squeaky_balls");';
}
//Check that at least one item was ordered.
if ( $frisbees + $giant_chew_ropes + $squeaky_balls == 0 ) {
$data_ok = false;
$js .= 'show_product_global_error_message("Sorry, you must order at least one item.");';
}
//Check number on hand.
//Normally number on hand would be loaded from a database.
//For simplicity, I just made up some numbers.
$frisbees_on_hand = 5;
$giant_chew_ropes_on_hand = 4;
$squeaky_balls_on_hand = 8;
if ( $frisbees > $frisbees_on_hand ) {
$data_ok = false;
$js .= "show_product_error_message('Sorry, we only have $frisbees_on_hand on hand.', 'frisbees');";
}
if ( $giant_chew_ropes > $giant_chew_ropes_on_hand ) {
$data_ok = false;
$js .= "show_product_error_message('Sorry, we only have $giant_chew_ropes_on_hand on hand.', 'giant_chew_ropes');";
}
if ( $squeaky_balls > $squeaky_balls_on_hand ) {
$data_ok = false;
$js .= "show_product_error_message('Sorry, we only have $squeaky_balls_on_hand on hand.', 'squeaky_balls');";
}
//Is everything OK?
if ( $data_ok ) {
//Process the order.
$destination_url = "process.php?frisbees=$frisbees&giant_chew_ropes=$giant_chew_ropes&squeaky_balls=$squeaky_balls";
header("Location:" . $destination_url);
exit();
}
}
?><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Order Form</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
body {
font-family: Verdana, sans-serif;
font-size: 14px;
background-color: #FFFFE0;
}
.message_container {
display: none;
font-weight: bold;
color: red;
}
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#global_error_message_container").hide();
$("#frisbees").focus();
<?php print $js; ?>
$("#order_form").submit(function() {
//Set flag showing everything is OK.
var data_ok = true;
//Check the frisbees.
var frisbees = $("#frisbees").val();
var frisbees_message = check_order_value(frisbees);
if ( frisbees_message != '' ) {
data_ok = false;
show_product_error_message(frisbees_message, 'frisbees');
}
else {
hide_error_message('frisbees');
}
//Check the giant chew ropes.
var giant_chew_ropes = $("#giant_chew_ropes").val();
var giant_chew_ropes_message = check_order_value(giant_chew_ropes);
if ( giant_chew_ropes_message != '' ) {
data_ok = false;
show_product_error_message(giant_chew_ropes_message, 'giant_chew_ropes');
}
else {
hide_error_message('giant_chew_ropes');
}
//Check the squeaky balls.
var squeaky_balls = $("#squeaky_balls").val();
var squeaky_balls_message = check_order_value(squeaky_balls);
if ( squeaky_balls_message != '' ) {
data_ok = false;
show_product_error_message(squeaky_balls_message, 'squeaky_balls');
}
else {
hide_error_message('squeaky_balls');
}
//Make sure something was ordered.
if ( frisbees + giant_chew_ropes + squeaky_balls == 0 ) {
show_global_error_message("Sorry, you must order at least one item.");
data_ok = false;
}
return data_ok;
});
});
//Check whether an order value is valid.
//Input
// value_to_check: the value to check.
//Return: An error message, or empty string if no error.
function check_order_value(value_to_check) {
if ( isNaN(value_to_check) ) {
return 'Please enter a number';
}
if ( value_to_check != Math.round(value_to_check) ){
return 'Please enter whole numbers only';
}
if ( value_to_check < 0 ){
return 'Please enter positive numbers only';
}
return '';
}
//Show an error message for a product.
//Input
// message: The message to show.
// product_name: The product name the message is about.
//Return: nothing.
function show_product_error_message(message, product_name) {
//Show the error message in the product's message area.
$('#' + product_name + '_message').text(message);
if ( $('#' + product_name + '_message_container').is(':hidden') ) {
$('#' + product_name + '_message_container').show('medium');
}
//Show the global error message at the top of the page.
show_global_error_message("Sorry, I can't process this order.");
}
//Show a global error message. It applies to the entire
// page, not just one product.
//Input
// message: The message to show.
//Return: nothing.
function show_global_error_message(message) {
$("#global_error_message").text(message);
if ($('#global_error_message_container' ).is(':hidden') ) {
$('#global_error_message_container').show('medium');
}
}
//Hide a product error message.
//Input
// product_name: The product name the message is about.
//Return: nothing.
function hide_error_message(product_name) {
$('#' + product_name + '_message_container').hide('medium');
}
</script>
</head>
<body>
<h1>Order Form</h1>
<p>What would you like?</p>
<p id="global_error_message_container" class="message_container">
<img src="error.png" alt="Error">
<span id="global_error_message"/>
</p>
<form id="order_form" method="post" action="order.php">
<p>
<input type="text" name="frisbees" id="frisbees" size="3"
<?php
if ( isset($_POST['frisbees']) ) {
print ' value="' . $_POST['frisbees'] . '"';
}
?>
>
Frisbees ($8.95 each)<br>
<span id="frisbees_message_container" class="message_container">
<img src="error.png" alt="Error">
<span id="frisbees_message"/>
</span>
</p>
<p>
<input type="text" name="giant_chew_ropes" id="giant_chew_ropes" size="3"
<?php
if ( isset($_POST['giant_chew_ropes']) ) {
print ' value="' . $_POST['giant_chew_ropes'] . '"';
}
?>
>
Giant chew ropes ($12.95 each)<br>
<span id="giant_chew_ropes_message_container" class="message_container">
<img src="error.png" alt="Error">
<span id="giant_chew_ropes_message"/>
</span>
</p>
<p>
<input type="text" name="squeaky_balls" id="squeaky_balls" size="3"
<?php
if ( isset($_POST['squeaky_balls']) ) {
print ' value="' . $_POST['squeaky_balls'] . '"';
}
?>
>
Squeaky balls to chase ($1.95 each)<br>
<span id="squeaky_balls_message_container" class="message_container">
<img src="error.png" alt="Error">
<span id="squeaky_balls_message"/>
</span>
</p>
<p>
<button type="submit">Order</button>
</p>
</form>
</body>
</html>
Figure 5. Complete order.php
We’ve seen most of this before. But there are some changes.
When a field is empty, the program decides the user wants none of that product:
//Get the order amounts.
$frisbees = $_POST['frisbees'];
if ( $frisbees == '' ) {
$frisbees = 0;
}
Figure 6. When a field is empty
On the previous page, where we did client-side only, we didn’t have a <form> tag. We attached validation code to a button:
$(document).ready(function() {
...
$("#order_button").click(function() {
...
});
});
...
<button id="order_button" type="button">Order</button>
Figure 7. Validation without a form
This time, we do have a <form>. We don’t attach the validation code to the click() event of a <button>. We attach it to the submit() event of the <form>.
$(document).ready(function() {
...
$("#order_form").submit(function() {
...
});
});
...
<form id="order_form" method="post" action="order.php">
...
<button type="submit">Order</button>
...
</form>
Figure 8. Triggering validation with a form
We need a way to stop data from being submitted if it is invalid. We do this by returning either true or false from the submit() function.
submit() function returns true, the browser goes ahead and submits the data to the page given in the action property (e.g., action="order.php"). submit() function returns false, the browser does not send the data. It does nothing.We can use this as follows:
$("#order_form").submit(function() {
...
var data_ok = true;
...
if ( [there is an error] ) {
...
data_ok = false;
}
...
return data_ok;
});
Figure 9. Stopping form submission
The variable data_ok is set to true initially. If there is an error, data_ok is set to false. Eventually, the browser gets to the line:
return data_ok;
If there have been no errors, data_ok will still be set to true, the value it started off with. Return it, and the browser will send the data. But if there has been an error, data_ok will be false. Return it, and the browser will not send the data to the server.
Let’s look at the code the reports server-side errors to the user.
By the way, the variable $data_ok is a server-side PHP variable. It has nothing to do with the JavaScript variable data_ok.
//Flag for data OK.
$data_ok = true;
//JavaScript to run after page load.
$js = '';
//Check frisbees.
$frisbees_message = check_number_ordered($frisbees);
if ( $frisbees_message != '' ) {
$data_ok = false;
$js .= 'show_product_error_message("'.$frisbees_message . '", "frisbees");';
}
Figure 10. Error reporting
Most of it should be familiar, but there is one new thing. Line 22:
$js = '';
This PHP variable holds JavaScript code that reports errors to the user. Let’s see how it works.
Suppose the user entered “bark!” in the frisbees field. Line 25:
$frisbees_message = check_number_ordered($frisbees);
would put “Please enter a number” in $frisbees_message. Remember, this is PHP code, running on the server.
We want to give this error message to the user. We want it to look the same as the error messages produced by the client-side code.
How? Well, we already have a JavaScript function that will report error messages. We saw it on the previous page. Here it is again:
function show_product_error_message(message, product_name) {
//Show the error message in the product's message area.
$('#' + product_name + '_message').text(message);
if ( $('#' + product_name + '_message_container').is(':hidden') ) {
$('#' + product_name + '_message_container').show('medium');
}
//Show the global error message at the top of the page.
show_global_error_message("Sorry, I can't process this order.");
}
Figure 11. JavaScript function to show an error message
show_product_error_message() takes two arguments: the error message, and the product name.
So, we already have this function that will report errors. But it’s a JavaScript function. It runs in the browser. We want to call it from PHP code that runs on the server.
How?
Well, how about writing PHP code that writes JavaScript code that calls the function?
Like this (line 28 in Figure 10):
$js .= 'show_product_error_message("'.$frisbees_message . '", "frisbees");';
Suppose $frisbees_message has ‘Please enter a number’ in it. We get the string:
show_product_error_message("Please enter a number", "frisbees");
The string is appended to the PHP variable $js.
We can do something similar for the on-hand check:
if ( $frisbees > $frisbees_on_hand ) {
$data_ok = false;
$js .= "show_product_error_message('Sorry, we only have $frisbees_on_hand on hand.', 'frisbees');";
}
Figure 12. Checking inventory on hand
Notice that we have:
$js .=…
We keep adding to $js as we find errors.
What do we do with $js? If we could insert it into the page along with the other JavaScript, it would get executed, and the error message(s) would appear. Here’s code that would do it.
<script type="text/javascript">
$(document).ready(function() {
$("#global_error_message_container").hide();
$("#frisbees").focus();
<?php print $js; ?>
Figure 13. Inserting JavaScript error reporting code
Line 100 puts the contents of the PHP variable $js into the output stream. The variable contains some executable JavaScript code. The code is added into the $(document).ready() (starts in line 97), so it runs when the page loads.
To see how this works, try it, and enter large numbers in the form fields:

Figure 14. Too many again
When you get a display like this, have a look at the source code of the page. You’ll see something like this:
<script type="text/javascript">
$(document).ready(function() {
$("#global_error_message_container").hide();
$("#frisbees").focus();
show_product_error_message('Sorry, we only have 5 on hand.', 'frisbees');show_product_error_message('Sorry, we only have 4 on hand.', 'giant_chew_ropes');show_product_error_message('Sorry, we only have 8 on hand.', 'squeaky_balls');
Figure 15. Generated code
Line 5 was generated by PHP code. But the browser doesn’t know where it came from. It’s just there, and gets run along with everything else.
I’m confused. The PHP adds JavaScript? Won’t it be part of the page forever, even when the user didn’t make a mistake?
Remember that order.php is a PHP program, not a static HTML file. order.php can send different HTML and JavaScript to the browser, depending on the circumstances.
So when it inserts JavaScript into the HTML stream, it doesn’t permanently change order.php itself. Just what the user sees that particular time order.php is run.
But this can be hard to understand. There’s a lot going on. Let’s break it down.
We want to give users a smooth experience with the form. That’s what matters in the end: can users easily order products? They don’t care about clients and servers and such. They just care what they see in the browser.
Let’s see how one user, Jeff, experienced the order form. Keep this is mind:

Figure 16. Structure
![]() Jeff |
![]() Browser |
![]() Server |
|---|---|---|
|
Looking at site's home page. Clicks link to go to order form. |
||
Asks server for order.php |
||
Starts order.php.No form data passed (1 in Figure 16). Generate page to show empty form (3 in Figure 16). Send HTML and JavaScript to browser. |
||
Render page from server. Blank form.
|
||
|
Types "lots" into frisbee field. Clicks Order button.
|
||
|
Runs JavaScript validation code. "lots" is not a valid number. Reports error. ![]() Note: Nothing sent to the server. The browser handles this error itself. |
||
|
Changes "lots" to 400. Clicks Order button. |
||
|
Runs JavaScript validation code. 400 is a valid number. Asks server for order.phpSends data: frisbees=400
|
||
Starts order.php.Note: This is the same page! order.php sent data to itself.Form data passed: frisbees=400Note: The first time the browser asked for order.php, it passed
no form data. This time is different.Run validation check. Only have 5 frisbees. Generate HTML and JavaScript for page. Add into $(document).ready():show_product_error_message(Send HTML and JavaScript to browser. |
||
|
Render page. Show error message during page load. ![]() Note: This error looks the same as the "Invalid number" error. At this point, Jeff has only ever seen order.php.
But the server has run order.php twice. The first run created
a blank form. The second run created a page with an error message and
a form.
|
||
|
Erases 400 from frisbees field. Puts 4 into frisbees field. Clicks Order button. |
||
|
Runs JavaScript validation code. 4 is a valid number. Asks server for order.phpSends data: frisbees=4Note: Calling the same page again. Still on order.php.
|
||
Starts order.php.Form data passed: frisbees=4Run validation check. Data valid. Generate HTTP response with header: Location:process.php?Send HTTP response to browser. |
||
|
Gets HTTP redirect from server. Asks server for process.phpNote: Finally! Going to a different page. Sends data: frisbees=4
|
||
Starts process.php.Form data passed: frisbees=4Run validation check. Data valid. Process order. (Send messages to accounting, shipping, etc.) Generate HTML for order confirmation page. Send page to browser. |
||
| Render confirmation page. | ||
|
W00f! Prints confirmation page. |
Try the page, and run through the same steps Jeff did.
An important note:
Jeff’s experience is seamless.
The order page reloads after Jeff enters 400 frisbees and clicks the Order button. But he might not even notice the reload, if the server is fast enough. The browser shows the same color and fonts and headings and fields.
The key to this is that the same program – order.php – creates different HTML and JavaScript, depending on the situation.
One last piece of code to look at. This is from the HTML that makes the order form.
<input type="text" name="frisbees" id="frisbees" size="3"
<?php
if ( isset($_POST['frisbees']) ) {
print ' value="' . $_POST['frisbees'] . '"';
}
?>
>
Figure 17. <input> field
Look at the source code in your browser again, you’ll see:
<input type="text" name="frisbees" id="frisbees" size="3" value="100">
(I left out some spaces.)
You can see that it puts the value the user typed into the field.
But the other thing is that the <input> tag has both name and id attributes. Why both? The name is needed to send the form data to the server. The id is used by the client-side JavaScript code.
This is by far the most complex page we’ve talked about. To get a better sense of how the whole thing fits together, download the files.
This lessons shows a complete form validation example.
Time for more exercises.
Create a form like this:

Figure 1. Empty form
The user enters a model number and the quantity. Model number must be either 23 or 39. Quantity must be a number greater than zero.
The data is sent to a PHP page for validation. All validation takes place in PHP!
If the data is OK, just show it:

Figure 2. Valid data
But suppose there’s an error, like no model number:

Figure 3. No model
Show an error message like this:

Figure 4. Error message
Here are the error messages your page should generate:
You can try my solution.
Upload your solution to your server. Put your URL below.
(Log in to enter your solution to this exercise.)
Create a form that lets a user enter information about a new product. (Later, you’ll learn how to add this data to a database. This exercise is just about the form part.)
The form looks like this when opened:

Figure 1. Empty form
If the user leaves all the fields blank and clicks the button, show:

Figure 2. Empty form with error messages
Numbers must be valid:

Figure 3. Bad number
All of these checks are done on the client side.
There is one check that is done on the server side only: the selling price cannot be less than the purchase cost. If it is, show:

Figure 4. Purchase cost less than selling price
If everything is OK, show a confirmation page:

Figure 5. Confirmation
You can try my solution and download the files. But do it yourself first!
Here is the error icon:
The icon came from the famfamfam silk icon set. It’s one of the most complete icon sets you can find.
Hints:
stripslashes() function that we talked about earlier. For example:
$product_name = stripslashes($_POST['product_name']);
print stripslashes($_POST['description']);
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
Write a PHP application that saves ideas for projects to a file. You can try the application. (I disabled the idea saving bit.)
Here is the home page:

Figure 1. Home page
Note that the input focus is in the first field when the page loads.
You can download the background image. It’s from an OSWD template. You can also download the error icon.
The user completes all of the fields. If any fields are left blank, the user sees a page like this:

Figure 2. Missing data
Checks for missing data are done on the client.
Only some users are allowed to post ideas about some projects.
beth.jackson and zon.plaske are allowed to post ideas about the Grendel project.jed.mccurry and fhit.jhoges are allowed to post ideas about the Bathron project.If the user is not allowed to post about the project, the following appears:

Figure 3. No permission
The message is “Sorry, that user does not have permission to post ideas about that project. Please check the project and user name.”
If the user is allowed…

Figure 4. Valid data
... the post is saved to a file, and a confirmation shown:

Figure 5. Confirmation
The project/user permission check must be done on the server! In PHP. Not in JavaScript! You can use the pattern from the Complete validation page.
Click the “Show ideas” link, and you see a list of all the ideas saved:

Figure 6. Idea list
Upload your solution to your server. Put the URL below.
(Log in to enter your solution to this exercise.)
A database is an collection of structured data. “Structured” means that the same data is stored for a bunch of different things. This timetable has some structured data:

Figure 1. Timetable
There are a bunch of rail stops. Each one has a name (e.g., Hicksville) and a price (56 cents). The same type of information is stored for each stop: name and price.
Here’s another example of structured data. It’s part of actress Alyson Hannigan’s filmography.

Figure 2. Alyson Hannigan’s filmography
OK, so I have a thing for Willow.
There are a bunch of films. For each one, there’s the same data: year, film name, and role. The format of the data is predictable.
Structured data is often shown in tables, as you can see.
Here’s one more example.

Figure 3. Most popular dog breeds in the US
Again, there’s a table, with rows and columns. Each row is about a dog breed. Each column has data about breeds. Every row has the same format.
So structured data has predictable information about things of the same type, like rail stops, films, or dog breeds. What’s unstructured data look like?
Here’s some unstructured data.

Figure 4. Ozymandias
It’s the poem Ozymandias, by Shelley. There’s no predictable format.
PHP programs have an easier time processing structured than unstructured data. For example, a PHP program could take the dog breed data and compute the total number of registrations.
But there isn’t much a PHP program can do with a poem, except show it. It couldn’t tell you what the poem meant.
So a database is a collection of structured data. Some Web sites draw information from databases to create Web pages. For example, Amazon.com has a database, with data on the products they have. When your browser asks for a page, a program runs on their server. It gets product information from their database, and makes an HTML page out of it.

Figure 5. Amazon page
When you order something, your order goes into the database. Order data is structured. Each order has the same type of data: date, customer, products, etc. So one database can have structured data about different types of things: orders, customers, products, ...
A program tells the shipping people that there is a new order. They look in the database, and see your order. They put your stuff in a box, mail it to you, and update the database to show that the order was shipped.
The database ties different parts of the business together. Sales, shipping, receiving, billing, ... They all use data from the database.

Figure 6. DB ties business together
This chapter shows you how database Web applications work. As usual, you’ll do a lot of hands-on stuff.
The chapter talks about databases that have just one table in them. I’ve found that people have trouble understanding databases with more than one table, because of table relationships. So I’ll delay talking about relationships until you know how single-table databases work.
You’ll need a Web hosting account with MySQL. If you followed my earlier recommendations, you have one.
Let’s start by looking at some sample database applications, so we know what we’re trying to acomplish.
This chapter is about creating database Web applications. We need to know what a “database Web application” is.
In this lesson, we:
Let’s look at some examples, where DB tech is key to a Web application.
DogToys is an online store selling toys, like frisbees, chew ropes, and squeaky balls.
Customers see pages like this:

Figure 1. Product catalog
The catalog is the public face of the business. There’s a lot of activity behind the scenes. DogToys has to:
DogToys has Web pages to help with these tasks. Customers can’t use these pages, just DogToys’ employees.
For example, here’s a screen that would let someone change a price:

Figure 2. Updating product data
Changing prices is quick and easy. No HTML. Change a number in a form, and click the Save button.
Here is what the customer sees after the change:

Figure 3. The new catalog
How to make such an application? It’s easiest if there is one central data store, with all of the information about the products. A database.
The database has a products table, with every price in it, Here’s part of the product data:

Figure 4. Part of the product data in the DB
When a customer visits the catalog Web page, a PHP program fetches the product from the database and shows it.

Figure 5. Customer viewing product data
When a marketer changes a price, s/he fills in an HTML form. The form sends the data to a PHP program that writes the new price to the database.

Figure 6. Marketer changing product data
DogToys has a bunch of PHP programs that all connect to the same database.

Figure 7. The system
One PHP page shows the product catalog. This is all the customers are allowed to see.
Another PHP page updates product data. Another page adds a new product. Another removes a product. Employees use these pages to keep the catalog up-to-date.
The database is at the center of it all. Different PHP pages use the same database to show different people what they need.
Let’s look at another example of a Web application.
DogRock is a site about dog rock music. It has articles about new dog bands, albums, concerts, tours, and such.
Here’s a sample article:

Figure 8. An article
People who write for DogRock aren’t Web experts. They don’t know what FTP means, or about nav bars and such. How to let writers add new stories without knowing much about Web tech?
That’s what a content management system (CMS) is for. CMS let nontech people manage Web site content.
When a user goes to the site, s/he sees an article list:

Figure 9. Article list
Suppose Turlough, one of DogRock’s authors, writes a new article. He creates it by filling in a form:

Figure 10. Writing an article
When Turlough has finished writing his story, it will show up on the article list:

Figure 11. New article list
Click on the link, and read the article:

Figure 12. Article
Turlough created the new article by filling in a form on a Web page. He didn’t make an HTML file, or FTP anything.
This is a Big Thing that CMS do: non-tech people can create Web pages.
Articles aren’t stored in HTML files. Instead, they’re stored in a database table.

Figure 13. Article table
A bunch of PHP pages let people read articles, write new ones, edit articles, and delete them. Which pages people are allowed to access depends on whether they are a reader or writer.

Figure 14. DogRock architecture
Both of these systems – ecommerce and CMS – help different people do different tasks. But they have things in common.
There’s a central database in each one.
In both systems, people do four things to data:
DogToys and DogRock are CRUD systems.
People do different tasks with the same system, that is, they have different roles. For the ecommerce system, people with the “customer” role do some things (like look at the catalog). People with the “employee” role do different things, like change prices.
With the CMS, there’s the “writer” role and the “reader” role. Writers can create and edit stories. Readers can only read them.
Here are the main steps in making a CRUD Web application:
For example, for the DogRock CMS: “Create article, read article, update article, and delete article.”
Each article has a title (“The Barkers to tour”), a publication date (June 15, 2011), and a body (“Those old time rockers, The Barkers, are at it again! The band last toured in 2007, when…”).
add-article.phpis a page that lets people add a new article,delete-article.phpis a page that lets people erase an article, ...
articles.phplists all the articles,show-article.phpshows a particular article, ...
Learning how to do all of this at once is too hard. Let’s start with the things that are easy to learn (like making a table), and move towards the more complicated stuff (like updating data).
By the time we get to the complicated stuff, you’ll have so much background that it won’t be so difficult.
We looked at two examples of Web applications: ecommerce and CMS. For each one, there is:
To create an application, you:
Let’s dig down a little, and see how a PHP page and a database management system (DBMS) work together in a Web application.
You’ve seen an overview of PHP database applications that run two small businesses. We’ll work slowly towards being able to write applications like that.
Let’s see how PHP programs and MySQL work together. Not in detail yet. That comes later.
In this lesson, you will learn:
OK) and, sometimes, some data.Remember that a server is software (or an entire computer) that does something for other software (or other computers).
An email server helps email clients. They exchange messages using protocols. Recall that a protocol is a language, a set of standards for things like saying hello and saying goodbye. SMTP is an email protocol.

Figure 1. Email server and client
A Web server helps Web clients (like browsers). They exchange messages in the protocol HTTP.

Figure 2. Web server and client
A database server works with database clients. The client sends requests in the language SQL. The server responds with data and status messages.

Figure 3. DB server and client
Figure 3 is a little different from the others. There’s a client and a server, and they communicate. But often the client and server software are on the same machine. They don’t use the Internet to communicate. Instead, they use something like a socket file to exchange messages. A socket file is a way of sending messages between software running independently on the same computer.
Another difference is the type of client. We will be writing our own clients, in PHP. In the DogRock CMS, each PHP program like add-article.php and delete-article.php is a client. It will generate SQL, and send it to the server.
DB server software is usually called a database management system (DBMS). An individual data set is a database.
The pattern for interaction is:
Suppose you want your PHP page to add data to a database.

Figure 4. Adding new data
Your page sends an SQL statement that includes the data to add. The statement would start with the word INSERT. The DBMS executes the SQL, and returns a status code, like “OK.”
If you want your PHP page to delete some data, your page would send a DELETE statement.

Figure 5. Deleting data
To fetch some data from the database, send an SQL statement. This one starts with the word SELECT.

Figure 6. Fetching data
The DBMS returns a status code, and some data. Your PHP can then do something with the data, like show it to the user.
There are many different DBMS. The one we’ll talk about is MySQL. Why this one? Because:
MySQL is included with the XAMPP package we talked about earlier. So if you installed XAMPP, you already have MySQL. W00f!
SQL stands for structured query language. It’s been part of the geek toolkit for about 30 years. Yes, you read that right. SQL has survived for a long time. And it’s not going away anytime soon.
We’ll talk more about SQL throughout this chapter. But let’s start with some examples, so you get the flavor of it.
Remember, don’t get hung up on the details. I just want you to understand the idea of “send SQL, get status and data.”
Suppose we have some dog data. For each dog, we know its name, breed, and weight. Like this:

Figure 7. Dog data
This data is sitting on the DB server.
We could send this SQL query to the server:
select name, weight from dogs;
This means “fetch the name and weight of every dog.” Here’s what the DBMS would return:

Figure 8. Two columns
Earlier you wrote about SELECT, and now about select. Which is right?
Both are. MySQL doesn’t care whether keywords like select and insert are in upper- or lowercase.
It also doesn’t care whether SQL statements have semicolons(;) at the end. Strictly, they should be there, but you can leave them off if you like.
Here’s another query:
select name from dogs where weight > 50;
This says “Give me the names of the dogs whose weight is more than 50.”
The DBMS would return:

Figure 9. Heavy dogs
This shows that a DBMS can filter data, sending back only what the client asks for. In our case, the client will be a PHP program.
Another example:
select name, weight from dogs order by weight;
This gives:

Figure 10. Sorted dogs
The DBMS sorts the data before returning it.
Another example:
insert into dogs (name, breed, weight) values ('Brian', 'Lab', 51);
This adds a new row to the dogs table:

Figure 11. New dog
This shows that SQL statements can change a database, as well as fetch data from it.
One more example:
delete from dogs where weight < 20;
This would remove all of the light dogs. You would get:

Figure 12. No light dogs
PHP pages send SQL statements to DBMS. So, to mess with data, you write PHP that writes SQL statements. Then you send the SQL to the DBMS.
For example:
$query = "insert into dogs (name, breed, weight)
values ('Brian', 'Lab', 51)";
$db->query($query);
Figure 13. Adding a dog
The first line puts the SQL statement into a variable. The second line sends it to the DBMS.
Here’s another one:
$query = "delete from dogs where weight < 20"; $db->query($query);
Figure 14. Removing light dogs
The first line creates the SQL, and the second one executes it.
What happens when you get a bunch of data back from a database? Remember this query:
select name from dogs where weight > 50;
This gives:

Figure 9 (again). Heavy dogs
You write PHP that to handle each row. The general pattern is:
Here’s an example. Don’t worry about all of the details; just look for the general pattern. The comments explain what each line does.
//Run the SQL query.
$query = 'select name from dogs where weight > 50';
$record_set = $db->query($query);
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for a row.
$name = $row['name'];
//Output
print "<p>Dog name: $name</p>";
}
Figure 13. Showing heavy dogs
Lines 2 and 3 run the query. The DBMS sends back some data, in a table (see Figure 9). The table has one column, and a bunch of rows. Each row is also called a record. The bunch of rows the DBMS returns is called a record set.
Lines 5 to 10 form a loop. The loop runs once for each row in the record set. (Don’t worry about how it works; just know that each record is passed through lines 6 to 9.)
Line 7 gets some data from a record, and puts it into a variable. Line 9 shows the data.
I’m getting worried here. It’s looking awfully complex.
I don’t blame you. It does get messy. It’s important to take baby steps. Just one little thing at a time.
That’s what we’ll do. But it won’t work if you skip the exercises. The more difficult the material, the more important the hands-on stuff is.
OK) and, sometimes, some data.Let’s look at a tool that can make your database work easier.
You’ve seen that clients send SQL statements to DBMS. There are tools that write SQL statements for you, saving you some work. Let’s talk about phpMyAdmin, the most widely used tool for managing MySQL databases.
You’ll learn:
Almost everything you do with MySQL is done by sending SQL commands to the server:

Figure 1. Everything is SQL
When you make Web applications, there’s some work you need to do to get things ready for your PHP pages. You create databases, make tables, add fields (columns), and other things.
This is done with SQL. But doing it all manually can be a pain. You have to type out long commands like this:
CREATE TABLE products (
product_id int(11) NOT NULL AUTO_INCREMENT,
'name' char(50) NOT NULL,
description text NOT NULL,
image_file_name char(50) NOT NULL,
price decimal(10,2) NOT NULL,
PRIMARY KEY (product_id)
);
And you have to get everything just right. You might make a mistake, like this:
CREATE TABLE products (
product_id int(11) NOT NULL AUTO_INCREMENT,
'name' char(50) NOT NULL,
description text NOT NULL,
image_file_name char(50) NOT NULL,
price decimal(10,2) NOT NULL,
PRIMARY KEY (prodct_id)
);
It wouldn’t work, but the problem is hard to spot. Grrrr!
There are tools that make it easier.
The one that most people use is phpMyAdmin. It’s actually just a bunch of PHP pages.
Most Web hosting companies install phpMyAdmin for you. It’s also part of the XAMPP package. If you installed XAMPP, you already have phpMyAdmin installed.
So you will have two copies of phpMyAdmin:
You also have two different MySQL instances:
Open the XAMPP control panel. Start both Apache and MySQL:

Figure 2. XAMPP control panel
phpMyAdmin is a bunch of Web pages. It needs Apache (or another Web server) to run.
Start your browser, and go to http://localhost/. You’ll see this on the left:

Figure 3. XAMPP home page
Click the phpMyAdmin link to start it.
Log in to your Hostgator control panel. Scroll down to the Databases section:

Figure 4. Starting phpMyAdmin on Hostgator
If you aren’t using Hostgator, you’ll start phpMyAdmin in a different way. Check you hosting company’s help pages.
How do I start MySQL on my hosting account?
You don’t. MySQL is always running, just like Apache is always running.
Start MySQL on your computer. Then open a browser, and run phpMyAmdin.
Run phpMyAdmin on your hosting account.
(Log in to enter your solution to this exercise.)
The phpMyAdmin installation on your local computer might be a different version from the one on your hosting account. As I write this, I’m running version 2.10.3 on my local machine, and version 2.11.9.5 on my hosting account.
Even the versions of MySQL are different. I’m running MySQL version 5.0.45 on my local machine, and version 5.1.30 on my hosting account.
That’s OK. We’re only covering DB basics. At the level we’re working, having different versions won’t matter. The things we’re looking at have been more-or-less the same for twenty years.
And they’re still current. The core of DB tech has been unusually stable.
In earlier chapters, I recommended that you install XAMPP on your own computer, and write PHP programs there. When everything is working, then you upload the finished programs to your hosting account.
Adding databases complicates things a little, but not much. You can create databases on your local machine, and then move them to your hosting account when you’re ready.
phpMyAdmin can both import and export databases, tables, and other objects. So you can just copy most of your work from your local computer to your hosting account.
I’ll go over an example when we get to creating a table.
There are many other tools that can help you work with databases. Earlier, I recommended Netbeans for writing Web pages. Netbeans has an integrated DB tool that works nicely with MySQL.
However, to keep things simple, we’ll only use phpMyAdmin. It’s already installed on both your development (local) and production (hosting account) servers. The same tool works no matter where you are.
You can read a review of Netbean’s database explorer.
You’re starting to see how PHP programs can work with databases. Let’s look at DogToys and DogRock again. We’ll go through all of their screens, and see how they fit together.
You’ve seen how PHP pages can send SQL statements to MySQL. We’re going to see how you use this ability to write Web applications.
But first, let’s look at two simple applications. Understanding what they do will help you understand why database stuff is done the way it is.
Recall that DogToys has an online product catalog, with things like squeaky balls, and chew ropes. In this lesson, we’ll look at what the site does. Later, you’ll see how it can be built.
By the end of this lesson, you should:
The ToyDogs site has three main goals:
Let’s talk about each one.
Customers want to be able to see pictures, descriptions, and prices of products. The product list should be easy to get to, from anywhere in the site.
Customers should be able to sort product data the way they want. We’ll let them sort the data by price (low to high, and high to low), and by product name.
We want DogToys employees to be able to change product data. The employees who change product data will be from the marketing department. They will not have strong technical skills. They don’t know any HTML.
We’ll create an administration section of the Web site. Employees will be able to fill in forms to change product data.
We will want to change the site. Maybe change the color scheme, add buttons to the nav bar, change the page footer, etc. Webers should be able to make these changes quickly and easily.
We’ll make a template-based site.
You can try the site. The admin parts of the site don’t actually update the database.
You can download the code.
Time for some screens. Here’s the home page:

Figure 1. Home page
Notice the nav bar on the left. This will be the same on every page. Customers can click on the Products button to see the products catalog. This meets the goal of having the products list available from every page on the site. It’s always just one click away.
Here’s the product catalog:

Figure 2. Product catalog
Each product has a name, picture, description and price.
The user can sort the data. Clicking on the Name link sorts the data by name. Clicking on the up and down arrows next to the price sorts by price.
You can try it.
The administration section of the site lets employees change product data. The administration section is just a bunch of Web pages.
There is no clickable link from the main part of the DogToys site to the administration section. To get to it, you need to know the URL. Add /admin to the home page’s URL to get to the main administration menu.
That isn’t very secure.
You’re right, it isn’t. Later in the book, you’ll learn how to add user names and passwords to sites.
Here’s the main administration screen:

Figure 3. Main administration screen
There’s a link users can click to add a new product.
All the products are listed. It looks like the product catalog, but each product has an Edit and Delete link.
Here’s the form for adding product data:

Figure 4. Adding product data
There are four fields:
Most are familiar, but what about the image file name? What’s that about?
We want to show a picture of each product. We’ll take a photo of each one, with a digital camera. We’ll store the image files on the Web site. Let’s put all the photos in a directory called product-images.
How to show the photo on a Web page? With the <img> tag, like this:
<img src="product-images/Image file name" alt="Product name">
Suppose we take a photo of the ball. We name the file with the photo ball.jpg, and put the file into the product-images directory. To show it on the product catalog page:
<img src="product-images/ball.jpg" alt="Squeaky ball">
So the user would type “ball.jpg” into the image file name field in Figure 4.
Image file name is just a regular text field. It’s used to make the <img> tag. The tag itself is just text. But when the browser renders the tag, it will download the image file.
The user clicks a product’s Edit link to edit a product record:

Figure 5. Edit link
This goes to a page like this:

Figure 6. Edit form
It looks like the new product form in Figure 4, but the fields already have data in them.
When on the main administration page, the user can click the Delete link to remove product data:

Figure 7. Delete link
This shows a confirmation page, to make sure the user really wants to delete the product record:

Figure 8. Confirm delete
The user has to click the Confirm button to actually delete the record.
Using a site is a good way to get to know it. But you should use it in the same way that other people would use it.
Pretend you’re a DogToys customer, who wants to buy the most expensive toy. Start at the site’s home page. Write down the steps you would take to complete the task.
Now pretend you are a DogToys employee. DogToys is having a sale on squeaky balls. You want to reduce the price of squeaky balls by 50 cents. Start at the site’s home page. Write down the steps you would take to complete the task.
Put your answers below.
(Log in to enter your solution to this exercise.)
We’ve talked about two of the site’s goals so far:
Now for the last one.
We want to make it easy for Webers to change the site. We’ll use the PHP template approach we talked about before.
So we’ll be using PHP for two things:
We looked at the DogToys Web site. The site lets:
Let’s tour the DogRock site.
Let’s look at another application. Understanding it will help you understand why database stuff is done the way it is.
Recall that DogRock’s site shows articles about dog rock music. It’s a content management system (CMS).
In this lesson, we’ll look at what the site does. Later, you’ll see how it can be built.
By the end of this lesson, you should:
The DogRock site has three main goals:
You can try the site. The admin pages don’t actually change the database.
You can download a zip file of all of the sites files.
Let’s talk about each goal.
Users want to get to new articles quickly. They also want to be able to access a list of older articles.
Each article has a title, a body, an author, and a publication date. Users want to be able to see a list of articles sorted by publication date, so they can see recent stuff. They also want to be able to sort by title and author.
DogRock’s writers are not computer experts. They can handle some basic HTML tags, like <p>, but that’s about it. They don’t know how to create entire Web pages.
We’ll create an administration section of the Web site. Writers will use forms to add and change articles. We’ll let them delete articles as well.
We will want to change the site. Maybe change the color scheme, add buttons to the nav bar, change the page footer, etc. Webers should be able to make these changes quickly and easily.
We’ll make a template-based site.
Time for some screens. Here’s the home page:

Figure 1. Home page
The latest three articles are listed on the home page, from most recent to least recent. You can try it.
Notice the nav bar on the left. This will be the same on every page. Users can click on the Articles button to see a complete article list.
Here’s the article list:

Figure 2. Article list
Clicking on the column names (title, date, and author) sorts the list. Clicking on the title of an article shows the article.
You can try it.
The administration section of the site lets writers add, edit, and remove articles. The administration section is just a bunch of Web pages.
There is no clickable link from the main part of the DogRock site to the administration section. To get to it, you need to know the URL. Add /admin to the home page’s URL to get to the main administration menu.
Here’s the main administration screen:

Figure 3. Main administration screen
There’s a link users can click to add a new article.
All the articles are listed. It looks like the article list, but each article has an Edit and Delete link.
Here’s the form for adding an article:

Figure 4. Adding an article
There are three fields:
Some PHP sets the publication date when the article is saved.
The user clicks an article’s Edit link to edit an article record:

Figure 5. Edit link
This goes to a page like the add article form, but with the fields filled in.
When on the main administration page, writers can click the Delete link to remove an article:

Figure 6. Delete link
This shows a confirmation page, to make sure the user really wants to delete the article record:

Figure 7. Confirm delete
The user has to click the Confirm button to actually delete the record.
Using a site is a good way to get to know it. But you should use it in the same way that other people would use it.
Pretend you’re a DogRock reader, who wants to read the latest article. Start at the site’s home page. Write down the steps you would take to complete the task.
Now pretend you’re a reader who wants to see what Bounder has written. Start at the site’s home page. Write down the steps you would take to complete the task.
Now pretend you are a DogRock writer. You want to fix a spelling error in the article “A new howl on the prowl.” Start at the site’s home page. Write down the steps you would take to complete the task.
Put your answers below.
(Log in to enter your solution to this exercise.)
We want to make it easy for Webers to change the site. We’ll use the PHP template approach we talked about before.
So we’ll be using PHP for two things:
The DogRock site lets:
Each application – DogToys and DogRock – uses a database. Let’s see how you create a database, and then add a table to it.
You’ve seen how PHP programs can send SQL statements to MySQL. You’ve seen two sample applications. Time to get your hands dirty.
By the end of this lesson, you should:
Start phpMyAdmin on your computer (see this explanation).
You’ll see the phpMyAdmin home page. If you get lost, click the home button to get back to it:

Figure 1. Go to the phpMyAdmin home page
Type in the name of the new database on the phpMyAdmin home page, and click the Create button:

Figure 2. Creating a database
phpMyAdmin will make the SQL statement that creates a database, and send it to MySQL. You’ll see something like this:

Figure 3. Feedback from creating a database
That’s it! You’ve created a database.
There are lots of options you can choose. As usual, CoreDogs only talks about the really important stuff.
Create two databases on your local machine:
You’ll use them to install your own copies of the DogToys and DogRock applications.
(Log in to enter your solution to this exercise.)
MySQL uses its own security system, with user names and passwords. When you create a database, the next thing is:
You don’t have to create a new user for each database, but it’s common practice.
Remember this:
MySQL user accounts have nothing to do with other user accounts!
MySQL accounts are used for MySQL access, and nothing else. Not FTP, or email, or Web access. Just MySQL.
How you do it is a little different on different versions of phpMyAdmin. But the concepts are the same.
Go to the phpMyAdmin home page:

Figure 1 (again). Go to the phpMyAdmin home page
Depending on your version of phpMyAdmin, either click the Privileges link, or the Privileges tab:

Or

Figure 4. Privileges link or tab
Click the add user link:

Figure 5. Add user link
Fill in the user’s name, host, and password and click the Go button:


Figure 6. Add user
Don’t set any global privileges for the user.
I often make the user name the same as the database name. You can type in your own password, or have MySQL generate one for you. If you do that, make sure you copy and paste the password to a file on your computer. You don’t want to lose it!
What’s that “host” thing? This is which computer(s) the user is allowed to connect from. The right value is almost always localhost, because your Web server and your DB server are running on the same machine. Type in localhost unless your hosting company tells you otherwise.
I’ve only ever run into one hosting company that didn’t use localhost. That was Dreamhost.
Once you have created the user, you can give the user access to a database. Scroll down to the “Database-specific privileges” section, and fill it in:

Figure 7. Add database privileges
Enter the database you want to give the user access to.
<Begin alternate route>
You can also get to Figure 7 from the home page. Go to the home page:

Figure 1 (again). Go to the phpMyAdmin home page
Then go to the privileges screen:

Or

Figure 4 (again). Privileges link or tab
Click the edit user icon:

Figure 8. Edit user privileges
</End alternate route>
Now that you’ve give the user access to the database, you tell MySQL what that user is allowed to do:


Figure 9. Select privileges
Check the privileges in the figure.
You’re done.
Create two database users on your local machine, one for each of the databases you created earlier. You can name them anything you like. I usually give them the same names as the databases:
You’ll use them for your own copies of the DogToys and DogRock applications.
Choose your own passwords, and write them down!
Give the users access to the server at localhost.
Give the users access to their respective databases. Give them the following privileges:
(Log in to enter your solution to this exercise.)
But did it work? When you create a new database and user, you should run some PHP to make sure it worked.
Here’s a test page.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<h1>PHP-MySQL connection test</h1>
<?php
//DB connection data.
//Probably leave $host alone.
$host = 'localhost';
//Set $db to the name of your database.
$db = 'dogrock';
//Set user_name to the name of the MySQL user you made.
$user_name = 'dogrock';
//Set $password to the user's password.
$password = '[Secret password here]';
?>
<p>Trying to connect to MySQL.</p>
<ul>
<li>Host name: <?php print $host; ?></li>
<li>Database name: <?php print $db; ?></li>
<li>User name: <?php print $user_name; ?></li>
<li>User password: (Secret)</li>
</ul>
<?php
//Connect to the MySQL server.
$db = new mysqli($host, $user_name, $password, $db);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
}
else {
print '<p>W00f! It worked!</p>';
}
?>
</body>
</html>
Figure 10. Test a connection
Line 29 makes the connection. It needs four pieces of data:
localhostdogrockdogrockYou type in those values in lines 11 to 18.
Line 31 tests whether the connection worked or not. If not, it shows an error message. If there is no error, line 36 will tell you about it.
Grab this code, and put it somewhere on your local computer.
Here’s what happens when everything works:

Figure 11. Connection OK
Here’s what happens if MySQL is not running:

Figure 12. MySQL not running
The error message is “Can’t connect to MySQL server.”
Here’s what happens if the password is wrong:

Figure 13. Wrong password
If the database name is wrong? Here it is:

Figure 14. Wrong database name
Write PHP programs to test your two local databases:
Copy the test code, and adjust the connection parameters.
If there’s a problem:
localhost and the databases.(Log in to enter your solution to this exercise.)
Now you know how to:
What about your hosting account?
When you create a database on your hosting account, you do things a little differently. The exact details depend on your hosting company. I’ll run through the procedures for Hostgator.
Log in to your control panel. Find the database icons:
![]()
Figure 14. Control panel database icons
Click the “MySQL databases” icon:
![]()
Figure 15. Control panel database icons
Type in the name of the new database and click the button:

Figure 16. Create database
You’ll get some feedback, like this:

Figure 17. Create database feedback
The next thing is important!
The real name of the database might not be the name you typed!
Hostgator prepends (adds in front) your user name to the database name, and chops off extra characters, if there are any. So it your Hostgator user name is woof121 and you type dogrock for your database name, the real name of your database might be woof121_dogrock.
You would use woof121_dogrock as the database name in your PHP programs.
Why does Hostgator do this?
Remember that many sites run on the same server. That what’s “shared hosting” means. There’s one copy of MySQL that everyone shares.
Several people might create a database names books. How is the server to tell them apart?
Oh, I see. Hostgator changes the names, so they are all different.
Right! So if the user lab200 typed in books as the name of a database, the actual name of the database would be lab200_books. If the user pug77 typed in books as the name of a database, the actual name of the database would be pug77_books.
Copy and paste the real name of the database into your code.
Go back to the MySQL database page. Remember, to get there from the control panel:
![]()
Figure 15 (again). Control panel database icons
To create a user, scroll down to the Add User form, and fill it in:

Figure 18. Create a user
You’ll get feedback like this:

Figure 19. Create a user – feedback
But this is misleading. The control panel changes the user name the same way it changes the database name. So if lab200 creates the user dogrock, the user is really lab200_dogrock.
Hostgator’s control panel won’t change the password. If you type a password of secret_ThinG_12, then that will be the password you use in your PHP.
Now you need to give the user access to the database.
On the MySQL Databases page, you’ll see something like this:

Figure 20. Giving a user access to a database
Select the user and the database, and click the Add button.
Then you select which privileges the user has:

Figure 21. Setting privileges
Use the ones shown in the figure.
You’ll get feedback like this:

Figure 22. Setting privileges – feedback
Hooray! You’ve:
Remember: use the real names of the database and the user. You can see them on the control panel’s MySQL Databases page.
How to test that everything worked? Same as before. Put a file with this code on your hosting account:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<h1>PHP-MySQL connection test</h1>
<?php
//DB connection data.
//Probably leave $host alone.
$host = 'localhost';
//Set $db to the name of your database.
$db = 'dogrock';
//Set user_name to the name of the MySQL user you made.
$user_name = 'dogrock';
//Set $password to the user's password.
$password = '[Secret password here]';
?>
<p>Trying to connect to MySQL.</p>
<ul>
<li>Host name: <?php print $host; ?></li>
<li>Database name: <?php print $db; ?></li>
<li>User name: <?php print $user_name; ?></li>
<li>User password: (Secret)</li>
</ul>
<?php
//Connect to the MySQL server.
$db = new mysqli($host, $user_name, $password, $db);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
}
else {
print '<p>W00f! It worked!</p>';
}
?>
</body>
</html>
Figure 10 (again). Test a connection
Replace the database name, user name, and password. Use the real names, like lab200_dogrock. Load the page in your browser, and see what you get.
Create two databases on your hosting account:
Your hosting account’s control panel might adjust the names of the databases. Make sure you know the real names.
Create users for each database. Give them access to localhost. Write down the users’ passwords!
A few hosting companies don’t use localhost.
Give the users the following privileges on their respective databases:
Copy the test code, and adjust the connection parameters. Upload to your server, and run your test programs.
Put the URLs of your test pages below.
If there’s a problem:
locahost. (Log in to enter your solution to this exercise.)
In this lesson, you learned how to:
Now you have some databases. Time to add a table to each one.
You know how to create a database. Now learn how to add a table to a database.
Learn:
INT, DECIMAL, CHAR, TEXT, and DATE.INT field to be a primary key. Make it unsigned and auto_increment. MySQL will fill in a unique value.All MySQL data is in tables. Here’s an example:

Figure 1. dogs table
The data is in rows and columns.
Each row is data about a single dog. So all the data on the first row is about Francis. A row is also called a record.
Each column is a characteristic of a dog, like the dog’s name, breed, or weight. Columns are also called fields. Every row has the same fields.
Here’s the articles table from the DogRock content management system (CMS):

Figure 2. articles table
Each row is data about one article. Each column in an attribute of articles, like the title.
Here’s the products table from the DogToys product catalog:

Figure 3. products table
Each row is data about one product. Each column in an attribute of products, like the price.
Suppose you want a table to store your favorite jokes. Each row of the table would have a joke.
What fields would you put in the table?
(Log in to enter your solution to this exercise.)
Each column contains a specific type of data. Here are the data types for the dogs fields.

Figure 4. dogs data types
When you create a table, you tell MySQL the data type of each column. Don’t worry about how you do that just yet; we’ll talk about it soon. For now, let’s focus on the data types.
There are many different data types. The core types are in three categories:
Characters are text, as in “I like the number 96.” Character fields are also called string fields.
You can tell MySQL how long character fields should be. For example…
CHAR(10)
... tells MySQL that a field can have up to 10 characters. Try to put more in the field, and the extra characters will be cut off.
Another character data type is:
TEXT
A TEXT field can have up to about 65,000 characters.
We’ll only talk about two number types:
Integer fields can contain whole numbers only, like 7. Try to put 7.32 into an integer, and the .32 will be cut off.
Make integer fields with the INT data type.
Decimals are declared like this:
DECIMAL(10,2)
This is a number that is 10 digits long, with 2 decimal places.
MySQL has several date/time types. You can store times down to a fraction of a second, if you want.
We’ll just use the simplest date/time type: DATE. It stores just a date, and not a time.
But how to format a date? In the US, it’s month/day/year, so June 3, 2013 would be 6/3/13. In Australia, you would use day/month/year, as in 3/6/13.
Just as there’s a standard for HTTP, and another one for HTML, and another one for CSS, there’s an international standard for dates. It’s YYYY-MM-DD. So June 3, 2013 would be 2013-06-03.
Most Webers use the standard format when storing dates in a database. That’s what we’ll do. But when we show the dates to people, we’ll use the US format. You can adjust the format, if you like.
There are many other data types. Other types of strings, other types of numbers, and things that aren’t either. For example, coordinates of a place on the earth, used in mapping programs.
We’ll stick with CHAR, TEXT, INT, DECIMAL, and DATE for now. I’ll explain others if we need them.
Here’s the products table again:

Figure 3 (again). products table
Most tables have a column that is the table’s primary key. This uniquely identifies each row in the table.
For products, only one product will have a product_id of 1. Only one product will have a product_id of 2. There will never be any duplicates.
Could the name field be a primary key? No. There might be two products from different manufacturers that have the same name. Maybe two manufacturers make a product they call “Giant chew rope.” They will have different product_ids, so the database can tell them apart.
Here’s the articles table again:

Figure 2 (again). articles table
Two articles could have the same name. For example, Wendy could write an article called “Old Time Howl.” In three years, Bounder could write an article called “Old Time Howl.”
The field article_id takes care of it. Each row has a different value for article_id. It’s the table’s primary key.
Here’s the dogs:

Figure 1 (again). dogs table
What is the primary key? Is it name? That’s the best field we have, but it isn’t very good. Two dogs could have the same name. There might be two Ralphs, for example.
This table isn’t designed well. It would be better if it had a primary key:

Figure 5. dogs table with primary key
It’s common practice to add an integer field (whole number, remember) to a table, and make it the primary key. That’s what I did with the dogs table.
The first row has a value of 1 for this field. The next row has 2, and so on. Actually, it doesn’t matter what the values are, as long as each one is different. I could make the dog_id of the first row 432, the second row 89, the third row 299, etc. But making them 1, 2, 3, etc., is usual.
When we add a new record to the table, we want dog_id to have a new unique value. We can tell MySQL to do this for us. When we create the table, we can mark an integer primary key field as auto_increment.
When our PHP program adds a new record to dogs, we send the values for each field to MySQL. Like this:
insert into dogs (name, breed, weight) values ('Jamie', 'Scottie', 9);
Notice that we didn’t give a value for dog_id. Since dog_id is an auto_increment field, MySQL will set the value for us. It will take the next value in sequence. So if the last record we added have a value of 3,409, then MySQL will fill in the value 3,410.
You listed the fields for a jokes table. What is the data type of each one? Choose from INT, DECIMAL, CHAR, TEXT, and DATE.
Remember to include a primary key.
(Log in to enter your solution to this exercise.)
Let’s see how you actually add a table to the a database.
I added a humans table to the dog database. It has fields for:
First, I opened the database in phpMyAdmin. Do this by selecting the database in the drop-down:

Figure 5. Open a database
Then I typed the name of the new table and the number of fields into the Create table form:

Figure 6. Create a new table
Then I clicked the Go button.
Here are the settings for the primary key, human_id:

Figure 7. The human_id field
The data type is INT. I didn’t need to give a length; INT has a standard length, and MySQL knows what it is.
Primary keys are not negative; they could be, but it would be strange. So I made the field UNSIGNED. It’s also set to AUTO_INCREMENT. You should make your primary keys INT, UNSIGNED, and AUTO_INCREMENT.
I marked as human_id the table’s primary key, using the radio button next to the picture of a table with a key on it. Don’t forget to mark your primary keys!
Here are the settings for name:

Figure 8. The name field
It’s set to CHAR and given a length of 20. You always need to give a length to CHAR fields.
Here are the settings for weight:

Figure 9. The weight field
It’s set to INT.
I clicked the Save button to make the table. phpMyAdmin created an SQL statement, and told MySQL to run it. Here’s the feedback I got.

Figure 10. Create table feedback
Now I have some tables. How about adding data?
Let’s say I want to add data to the dogs table. First, I open the table.

Figure 11. Opening the dogs table
The I click the Insert tab.

Figure 12. Insert tab
I see a form that lets me add data.

Figure 13. Add record form
I fill in the form, but not the auto_increment field!

Figure 14. Adding data
dog_id, the primary key, is an auto_increment field. MySQL will fill in that value for me.
I click the Save button, and get this feedback:

Figure 15. Feedback on adding data
You can see the SQL INSERT statement that phpMyAdmin created. You can also see the message “Data truncated for column ‘breed’ at…” Why? Because I declared breed as CHAR(10). The value I typed in – Whale hound – is 11 characters long. So the last character was lost.
Here’s what the table looks like with the new record:

Figure 16. New data
You can see the truncated breed.
You can also see that the dog_id is 6. Remember that I didn’t type in that value. Because it’s an auto_increment field, MySQL filled in the value itself.
I created the dogs table on my local PC, and added some data. How do I get it on my Hostgator server? I could retype everything, but there’s an easier way:
When you export a table in phpMyAdmin (on your local machine), phpMyAdmin recreates the SQL statements that made the table and added the data. Then you can tell phpMyAdmin (the one on your hosting account) to execute those statements.
Open the table you want to export:

Figure 17. Open table
Click the Export tab:

Figure 18. Export tab
Make sure SQL is the export format:

Figure 19. Export format
Check “Save as file”:

Figure 20. Save as file
Click the Go button. Your browser will save the file.
Open the file in Notepad++, Netbeans, or some other editor. You’ll see the SQL that phpMyAdmin exported.
Start phpMyAdmin on your hosting account. If you forget how, here’s a reminder.
Open the database you want to put the table into.
Click the import tab:

Figure 21. Import tab
Click the Browse button:

Figure 22. Browse button
Select the file to import. Click the Go button. phpMyAdmin will grab the file and run the SQL statements inside it.
In this exercise, you will get DogToys and DogRock running on your computer.
If you haven’t already, download the zip files for DogToys and DogRock. Extract them into separate directories on your computer. Put them under the document root of your local Web server (probably c:\xampp\htdocs\).
If you haven’t already, create two databases on your computer: dogtoys and dogrock. Create a user for each one, with access to the databases.
Now it’s time to create the tables. Both DogToys and DogRock have an SQL file you can import into phpMyAdmin. Each file will create a table and add some data. For DogToys, the file is dogtoys.sql. For DogRock, it’s dogrock.sql.
Import the files into their respective databases on your local computer.
The last step is to tell DogToys and DogRock what connection data (database, host, and user) to use. Both applications have a file called /library/db-connect.php. Open the one for DogToys. You’ll see something like this:
<?php //DB connection data. $host = 'localhost'; $db = 'dogtoys'; $user_name = 'dogtoys'; $password = 'dogtoys'; ?>
Figure 1. DogToys database connection parameters
Change these values to match your database. For example, if you gave your dogtoys user a password of playallday, use that.
Do the same for DogRock.
Start Apache and MySQL on your computer. Try the applications.
(Log in to enter your solution to this exercise.)
You created a jokes table on your local computer. Export it. This will create an SQL file on your computer. Store the file wherever you like.
Now import the file into the jokes database on your hosting account.
(Log in to enter your solution to this exercise.)
INT, DECIMAL, CHAR, TEXT, and DATE.INT field to be a primary key. Make it unsigned and auto_increment. MySQL will fill in a unique value.You’ll need a way to apply your database skills. In the next lesson, you’ll set up the Jokes application. You’ll add to it as we work through the rest of the chapter.
You’ll need a way to apply your database skills. In this lesson, you’ll set up the Jokes application. As we work through this chapter, you’ll add pages to Jokes, until it’s complete.
The Jokes application is much like DogToys and DogRock. It’s a template application, so it uses PHP for more than database work. You can try it.
In this lesson, you’ll:
jokes databaseCreate a database called jokes on your machine. A brief recap of the steps:
Create a MySQL user for the database. You’ll need this to connect you PHP code to the database.
Run the test program we used earlier. Here is the code again:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<h1>PHP-MySQL connection test</h1>
<?php
//DB connection data.
//Probably leave $host alone.
$host = 'localhost';
//Set $db to the name of your database.
$db = 'jokes';
//Set user_name to the name of the MySQL user you made.
$user_name = '[User name here]';
//Set $password to the user's password.
$password = '[Secret password here]';
?>
<p>Trying to connect to MySQL.</p>
<ul>
<li>Host name: <?php print $host; ?></li>
<li>Database name: <?php print $db; ?></li>
<li>User name: <?php print $user_name; ?></li>
<li>User password: (Secret)</li>
</ul>
<?php
//Connect to the MySQL server.
$db = new mysqli($host, $user_name, $password, $db);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
}
else {
print '<p>W00f! It worked!</p>';
}
?>
</body>
</html>
Figure 1. Test a database connection
Change the code on lines 12 to 18 to match your database.
jokes tableCreate a jokes table in the jokes database. Here are the fields and types I used in mine:
joke_id – INT, primary key, auto_incrementtitle – CHAR(30). A few words reminding me what the joke is about.joke_text – TEXT. The text of the joke itself.funniness – INT. How funny the joke is, from 1 (not funny) to 5 (hilarious).when_added – DATE. When the joke was added to the database.You can modify this list, if you want.
Add two or three jokes to the table.
Rather than asking you to write the entire application from scratch, I’ll give you all of the files with the database stuff removed. The PHP for templating is included.
Download the zip file. Extract it to a directory under your Web root, like c:\xampp\htdocs\jokes.
Try it in your browser.
Look through the files. Remind yourself how templating works.
You set up the Jokes application. It’s a starting point. You’ll add database code to it as we work though the chapter. You:
Let’s see how you let users add data to a database.
You know how to create databases, add tables, and test connections. Now let’s get our hands (brains?) dirty. Let’s see how you help users add data.
In this lesson, you will learn:
INSERT statement does the work.stripslashes() to remove backslashes that PHP adds to form data.$db->escape_string() to foil SQL injection attacks.Let’s see how data gets added to the DogToys database.
What’s the workflow when a user enters a new record?
First, the user clicks the Add product link on the administration menu.

Figure 1. Main DogToys administration screen
The user sees a form:

Figure 2. Adding product data
The user types in the data, and clicks the Save button. This sends the data to a PHP page that saves the data. That page then goes back to the administration menu.
So the workflow is:

Figure 3. Adding product workflow
Here’s the code for the form. I omitted some validation stuff; I’ll add it back in the next lesson.
<form id="new_product_form" method="post" action="save-new-product.php">
<p>
Name<br>
<input type="text" name="name" id="name" size="30">
</p>
<p>
Description<br>
<textarea name="description" id="description" rows="5" cols="30"></textarea>
</p>
<p>
Image file name<br>
<input type="text" name="image_file_name" id="image_file_name" size="30">
</p>
<p>
Price<br>
<input type="text" name="price" id="price" size="10">
</p>
<p>
<button type="submit">Save</button>
</p>
</form>
Figure 4. HTML for the form
First there’s the <form> tag:
<form id="new_product_form" method="post" action="save-new-product.php">
action tells the browser where to go to save-new-product.php when the user submits the form.
The rest of the code should be familiar. The <input type="text"> tags create one-line input fields. <textarea> creates a multi-line input field.
OK, let’s look at save-new-product.php, the PHP that saves the data. The pattern for the page is:
INSERT statement.Here is the entire code. We’ll run through it a step at a time.
<?php
//Save a new product.
//Input (all POST):
// name: Name of the product.
// description: Description of the product.
// image_file_name: Name of the file containing an image of the product.
// price: Selling price of the product.
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Get the form fields.
$name = stripslashes($_POST['name']);
$description = stripslashes($_POST['description']);
$image_file_name = stripslashes($_POST['image_file_name']);
$price = stripslashes($_POST['price']);
//Make the fields safe.
$name = $db->escape_string($name);
$description = $db->escape_string($description);
$image_file_name = $db->escape_string($image_file_name);
$price = $db->escape_string($price);
//Create and run the SQL.
$query = "insert into products
(name, description, image_file_name, price)
values ('$name', '$description', '$image_file_name', $price)";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 5. save-new-product.php
Let’s break it down into steps.
Lines 2 to 7 are documentation. They explain what the page does, and what input it expects. It’s good practice to add comments like this.
Line 9 sets the variable $path_to_root to the path from save-new-product.php (the file we’re talking about) to the root of the site. This is part of the templating system. Recall that one of the business goals of DogToys was to make the site easy to change. The templating system lets us do that.
The first step in the pattern is:
INSERT statement.Lines 11 and 12 connect to the database.
You could make a database connection with a line like this:
$db = new mysqli('localhost', 'dogtoys', 'password', 'dogtoys');
We’re going to be making connections to the database in many different PHP pages. The page that adds a record to the database needs to connect to the database. So does the page that saves edited data. And the page that deletes a record. And the product catalog page itself.
All of these pages would have a line like:
$db = new mysqli('localhost', 'dogtoys', 'password', 'dogtoys');
Now, what if we need to change the password? We’d need to find every one of these lines, in every page, and change it.
Ack!
There’s an easier way: put the connection information in a separate file, then include that file when needed. Here are the two lines from save-new-product.php:
require $path_to_root . '/library/db-connect.php'; $db = new mysqli($host, $user_name, $password, $db);
Part of Figure 5 (again). save-new-product.php
Line 11 loads db-connect.php. Here’s what’s in it:
<?php //DB connection data. $host = 'localhost'; $db = 'dogtoys'; $user_name = 'dogtoys'; $password = 'password'; ?>
Figure 6. db-connect.php
Line 12 uses the variables set in db-connect.php to make the connection.
Every page that needs to connect to the database uses db-connect.php.
If I want to change the password? I change one line in db-connect.php, and it’s changed for every page on the site.
Hooray! That’s another productivity win from reuse.
So we have a connection to the database. What’s next? Let’s look at the pattern.
INSERT statement.Here’s some more code from save-new-product.php.
//Get the form fields. $name = stripslashes($_POST['name']); $description = stripslashes($_POST['description']); $image_file_name = stripslashes($_POST['image_file_name']); $price = stripslashes($_POST['price']);
Part of Figure 5 (again). save-new-product.php
This gets the data the user typed into the form fields, and puts it into variables.
As we talked about earlier, PHP sometimes adds backslashes (\) to data typed into form fields. It’s trying to be “helpful.” The stripslashes() function gets rid of them. It’s good to send every value from the user through stripslashes().
What’s next?
INSERT statement.The next few lines are:
//Make the fields safe. $name = $db->escape_string($name); $description = $db->escape_string($description); $image_file_name = $db->escape_string($image_file_name); $price = $db->escape_string($price);
Part of Figure 5 (again). save-new-product.php
We’re going to create an SQL command from the data the user types in. It’s possible for an Evil Doer – one who knows SQL – to make trouble. Unless we do something about it.
Suppose someone typed this into the form:

Figure 7. Evil Doer at work
The DROP statement is an SQL command that erases a table. By inserting quotes, semicolons, and SQL, a clever Evil Doer can make our PHP program do bad things.
This is called an SQL injection attack. You can see the consequences at XKCD.
escape_string() will foil the Evil Doer. It will mess up the quotes, semicolons, and other special characters needed for an SQL injection attack.
Hooray!
So run all form data through escape_string() before you do anything with it.
What’s next?
INSERT statement.Here are the next few lines:
//Create and run the SQL.
$query = "insert into products
(name, description, image_file_name, price)
values ('$name', '$description', '$image_file_name', $price)";
$db->query($query);
Part of Figure 5 (again). save-new-product.php
This makes an SQL statement, and puts it into the variable $query. I split the statement across several lines to make it easier to read. PHP lets you do this when you use double quotes (”) around the string. SQL doesn’t care that statements are split across lines.
Here’s another example of line splitting in PHP:
$temp = "
<blockquote>
<p>I love dogs!</p>
</blockquote>";
The HTML inside the quotes is easy to follow.
Note all the single quotes (’) on line 29. It’s important to get them right. The field name is a text field, so you need to put quotes around the values you put in it.
The field price is a number. So no quotes for its value.
Line 30 sends the SQL to MySQL for execution.
What’s next?
INSERT statement.The end of save-new-product.php is:
header('location:index.php');
This tells the browser to jump back to index.php. There’s no directory, just a file name. save-new-product.php is in the /admin directory, so the browser will jump to /admin/index.php.
So that’s it.
INSERT statement.W00f!
That was DogToys. Let’s have a look at DogRock.
Writers add articles to DogRock. Let’s look at the overall workflow.
What’s the workflow when a user enters a new record?
First, the user clicks the “Add article” link on the administration menu.

Figure 8. Main DogRock administration screen
The user sees a form. S/he user types in the data, and clicks the Save button.

Figure 9. Adding an article
This sends the data to a PHP page, which saves the data. That page then goes back to the administration menu.
So the workflow is:

Figure 10. Workflow for adding an article
Here’s the code for the form. Again, I stripped out some validation stuff.
<form id="new_article_form" method="post" action="save-new-article.php">
<p>
Title<br>
<input type="text" name="title" id="title" size="30">
</p>
<p>
Author<br>
<input type="text" name="author" id="author" size="30">
</p>
<p>
Body<br>
<textarea name="body" id="body" rows="10" cols="30"></textarea>
</p>
<p>
<button type="submit">Save</button>
</p>
</form>
Figure 11. HTML for the form
First there’s the <form> tag:
<form id="new_article_form" method="post" action="save-new-article.php">
action tells the browser to send the data to the page save-new-article.php.
The rest of the code should be familiar. The <input type="text"> tags create one-line input fields. <textarea> creates a multi-line input field.
Let’s look at save-new-article.php, the PHP that saves the data. The pattern is:
INSERT statement.The pattern is the same as before, except for the fourth line.
Here’s the code. I’ll go over it step by step in a moment.
<?php
//Save a new article.
//Input (all POST):
// title: Title of the article.
// body: Body of the article.
// author: Author of the article.
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Get the form fields.
$title = stripslashes($_POST['title']);
$body = stripslashes($_POST['body']);
$author = stripslashes($_POST['author']);
//Make the fields safe.
$title = $db->escape_string($title);
$body = $db->escape_string($body);
$author = $db->escape_string($author);
//Prepare the publish date.
$when_published = date('Y-m-d');
//Create and run the SQL.
$query = "insert into articles
(title, author, body, when_published)
values ('$title', '$author', '$body', '$when_published')";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 12. save-new-article.php
Let’s break it down.
Lines 2 to 6 are documentation. They explain what the page does, and what input it expects.
The pattern is:
INSERT statement.Lines 10 and 11 connect to the database. As before, the connection parameters (host, user name, etc.) are in a separate file.
What’s next?
INSERT statement.Here’s some more code from save-new-article.php.
//Get the form fields. $title = stripslashes($_POST['title']); $body = stripslashes($_POST['body']); $author = stripslashes($_POST['author']);
Part of Figure 12 (again). save-new-article.php
This gets the data the user typed into the form fields, and puts it into variables. The stripslashes() function gets rid of the backslashes that PHP adds to form field data.
What’s next?
INSERT statement.Here’s the code for that step:
//Make the fields safe. $title = $db->escape_string($title); $body = $db->escape_string($body); $author = $db->escape_string($author);
Part of Figure 12 (again). save-new-article.php
As before, this messes up SQL injection attack.
What’s next?
INSERT statement.One of the things we store in the record for each article is the publication date. Rather than making the user type it, we can ask PHP what the current date is, and use that.
Here’s the line that does it:
$when_published = date('Y-m-d');
The date() function gets today’s date. The Y-m-d stuff gives a format: four digits for year, then a dash, then two digits for month, then a dash, then two digits for day. This is the international format that MySQL usually expects dates to be in.
What happens to the formatted date? It goes into the variable $when_published. We can use it just like the variables that contain the form data ($title, $body, and $author).
What’s next?
INSERT statement.Here’s the code:
//Create and run the SQL.
$query = "insert into articles
(title, author, body, when_published)
values ('$title', '$author', '$body', '$when_published')";
$db->query($query);
Part of Figure 12 (again). save-new-article.php
This makes an SQL statement, and puts it into the variable $query. Again, the single quotes need to be right on line 29.
Line 30 sends the SQL to MySQL for execution.
What’s next?
INSERT statement.The end of save-new-article.php is:
header('location:index.php');
This tells the browser to jump back to index.php. save-new-article.php is in the /admin directory, so the browswer will jump to /admin/index.php.
So that’s it.
INSERT statement.W00f!
Change the Jokes application so that users can add jokes.
Remember that there are two pages:
admin/add-joke.php).admin/save-new-joke.php). The first one you already have. It was in the zip file you downloaded. Your job is to write save-new-joke.php.
You can base it on save-new-product.php from DogToys and save-new-article.php from DogRock.
Don’t forget to adjust library/db-connect.php to use the right connection parameters.
Once you’ve finished, you can compare your solution with mine. But don’t look at it now!
If you want to share your solution with other people, you’ll need to:
(Log in to enter your solution to this exercise.)
INSERT statement does the work.stripslashes() to remove backslashes that PHP adds to form data.$db->escape_string() to foil SQL injection attacks.You know how to add a record. But what happens when something goes wrong? Let’s see.
Yow know how to help people add new data to a database. But what happens when things go wrong?
We’ve talked about error handling before. Both on the client side and the server side.
Using databases adds more things that can go wrong. In this lesson, we’ll see how you can handle database errors.
We’ll talk about two new types of errors:
You can check for both.
We’ll also talk about simple server-side validation. Even if you can check all data on the client, you should check it on the server as well. To foil Evil Doers.
There are two new types of errors:
A connection error is when a PHP program can’t connect to the database. An SQL error is when MySQL says something is wrong with an SQL statement. Like SELECT being typed as SELCT.
Let’s look at each type of error.
We had this earlier:
require $path_to_root . '/library/db-connect.php'; $db = new mysqli($host, $user_name, $password, $db);
Figure 1. Connecting to a database
If all is well, $db will be a valid connection object, something your PHP can use to send SQL to the database. But what if you have the wrong password, or user name? Or if the database server is down? Here’s how you check:
$db = new mysqli(...);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
Do something.
Figure 2. Checking for a connection error
mysqli_connect_error() will be FALSE if there was no connection error. Otherwise, it will contain an error message. So if there was no connection error, the if will skip over lines 4 to 7, and continue with normal processing on line 8.
exit() causes PHP to stop immediately and end the page.
So this is how you handle a connection error. But there are also…
It’s easy for errors to find their way into SQL. For example, suppose I tried to use this to create a new record in the DogRock database:
$db = new mysqli(...); ... $query = "insert into articles (title, author, body, when_published) values ($title, $author, $body, $when_published)"; $db->query($query);
Figure 3. SQL error
Can you see what’s wrong?
Quotes are missing. Suppose $title had the value Why I like fish. This:
...
values($title,…
would become:
...
values(Why I like fish,…
What we want is:
...
values('Why I like fish',…
The quotes keep everything together, so the title is treated as one value. When the quotes are missing, MySQL doesn’t know what to do.
Here’s how to check that an SQL query is correct:
$db = new mysqli(...);
...
$query = "insert into articles
(title, author, body, when_published)
values ($title, $author, $body, $when_published)";
$db->query($query);
if ( $db->error != '' ) {
print '<p>SQL error! Message: ' . $db->error . '</p>';
print "<p>Query:</p>
<blockquote>
$query
</blockquote>";
exit();
}
Figure 4. SQL error checking
Line 7 is:
if ( $db->error != '' ) {
If there is no error, then $db->error will be empty. If there is an error, then $db->error will not be empty.
If there’s an error, line 8 shows the error message. Lines 9 to 12 show the query that caused the error.
Line 13 stops the program immediately.
Summary so far – There are two new types of errors:
You’ve seen how to test for them.
So, if you don’t get any errors from the $db->error check, that means that everything is OK?
No!
It means that the format of the SQL statement was correct. But you might still have coding errors.
Suppose this was in your PHP:
$query = "insert into products
(name, description, image_file_name, price)
values ('$name', '$image_file_name', '$description', $price)";
$db->query($query);
Does that look OK?
Let me check… Hmm… Wait, the image file name and description are backwards. You have:
(name, description, image_file_name, price)
in one place, and:
('$name', '$image_file_name', '$description', $price)
in another.
Right! It’s a bug.
But the format of the SQL statement is just fine. MySQL would detect no errors, and would do what you told it to.
So what’s the point, if it won’t tell you about all of the errors?
$db->error will tell you about some of the errors. And that’s better than nothing. But you still need to look in the database, and make sure that your data was added correctly.
And we could use phpMyAdmin to check the data, right?
Yes. You can use phpMyAdmin to check whether your PHP program did what you thought it should.
Let’s see a new version of save-new-product.php. Recall that save-new-product.php is sent the form data the user types, and saves it to the database.
Here is the code with error checking:
<?php
//Save a new product.
//Input (all POST):
// name: Name of the product.
// description: Description of the product.
// image_file_name: Name of the file containing an image of the product.
// price: Selling price of the product.
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
//Get the form fields.
$name = stripslashes($_POST['name']);
$description = stripslashes($_POST['description']);
$image_file_name = stripslashes($_POST['image_file_name']);
$price = stripslashes($_POST['price']);
//Make the fields safe.
$name = $db->escape_string($name);
$description = $db->escape_string($description);
$image_file_name = $db->escape_string($image_file_name);
$price = $db->escape_string($price);
//Create and run the SQL.
$query = "insert into products
(name, description, image_file_name, price)
values ('$name', '$description', '$image_file_name', $price)";
$db->query($query);
if ( $db->error != '' ) {
print '<p>SQL error! Message: ' . $db->error . '</p>';
print "<p>Query:</p>
<blockquote>
$query
</blockquote>";
exit();
}
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 5. save-new-product.php
The code starting at line 14 checks whether the connection to MySQL was made successfully.
The code starting at line 37 checks whether the SQL statement ran without error.
Let’s do the same with DogRock.
<?php
//Save a new article.
//Input (all POST):
// title: Title of the article.
// body: Body of the article.
// author: Author of the article.
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
//Get the form fields.
$title = stripslashes($_POST['title']);
$body = stripslashes($_POST['body']);
$author = stripslashes($_POST['author']);
//Make the fields safe.
$title = $db->escape_string($title);
$body = $db->escape_string($body);
$author = $db->escape_string($author);
//Prepare the publish date.
$when_published = date('Y-m-d');
//Create and run the SQL.
$query = "insert into articles
(title, author, body, when_published)
values ('$title', '$author', '$body', '$when_published')";
$db->query($query);
if ( $db->error != '' ) {
print '<p>SQL error! Message: ' . $db->error . '</p>';
print "<p>Query:</p>
<blockquote>
$query
</blockquote>";
exit();
}
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 6. save-new-article.php
It has shiny new error checking code.
I can see how this error checking is needed.
But aren’t most errors made by people? Maybe typing something wrong? How are they checked?
Ooo, good question! You’re right, people make more mistakes than computers. Let’s see how to check for those sorts of errors
We looked earlier at how to do validation. Remember that we did some client-side checking, and some server side checking.
We ended up with an architecture like this:

Figure 7. Validation architecture
order.php would do client-side checking with JavaScript. It would also do server-side checking with PHP. order.php would send form data to itself to do any server-side checking. Then, if all was OK, it would send the data to process.php.
We hadn’t written process.php at that point. But now we can write it, storing information to a database.
Let’s see how that will work for our two sample applications
Here’s the input form for adding a new product:

Figure 8. Add product form – add-product.php
What errors can the user make? Not many.
We could add other checks, like making sure that the image name is a valid file name. But let’s leave it for now.
Both of these checks can be done on the client side. And I added code to do them to add-product.php. Like this:
...
//Check the name field.
var name = $("#name").val();
if ( name == '' ) {
data_ok = false;
show_field_error_message('Sorry, there must be a name.', 'name');
}
else {
hide_error_message('name');
}
...
<p>
Name<br>
<input type="text" name="name" id="name" size="30"><br>
<span id="name_message_container" class="message_container">
<img src="<?php print $path_to_root; ?>/library/error.png" alt="Error">
<span id="name_message"/>
</span>
</p>
...
Figure 9. Client-side error checking
I used the same approach we used earlier to show error messages. The result is like this:

Figure 10. Client-side error
So all of our validation can be done on the client, in JavaScript.
But we want to add server-side validation anyway, in PHP.
Why?
Security. A smart hacker could grab the code from add-product.php (the page that shows the form). S/he could delete the JavaScript code that did the client-side error checks, and run the modified page. It would accept bad data (like a negative price), and send it to save-new-product.php for storage in the database. If save-new-product.php didn’t check the data, it would the evil data into the database.
So we’ll add some simple validation to save-new-product.php. Nothing fancy, but enough to make sure that evil data doesn’t get into the database.
Here’s yet another version of save-new-product.php.
<?php
//Save a new product.
//Input (all POST):
// name: Name of the product.
// description: Description of the product.
// image_file_name: Name of the file containing an image of the product.
// price: Selling price of the product.
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
//Get the form fields.
$name = stripslashes($_POST['name']);
$description = stripslashes($_POST['description']);
$image_file_name = stripslashes($_POST['image_file_name']);
$price = stripslashes($_POST['price']);
//Validate
if ( $name == '' ) {
print '<p>Error! Name is missing.</p>';
exit();
}
if ( $description == '' ) {
print '<p>Error! Description is missing.</p>';
exit();
}
if ( $image_file_name == '' ) {
print '<p>Error! Image file name is missing.</p>';
exit();
}
if ( $price == '' ) {
print '<p>Error! Price is missing.</p>';
exit();
}
if ( is_nan($price) ) {
print "<p>Error! Price is not a number: $price.</p>";
exit();
}
if ( $price <= 0 ) {
print "<p>Error! Price is too low: $price.</p>";
exit();
}
//Make the fields safe.
$name = $db->escape_string($name);
$description = $db->escape_string($description);
$image_file_name = $db->escape_string($image_file_name);
$price = $db->escape_string($price);
//Create and run the SQL.
$query = "insert into products
(name, description, image_file_name, price)
values ('$name', '$description', '$image_file_name', $price)";
$db->query($query);
if ( $db->error != '' ) {
print '<p>SQL error! Message: ' . $db->error . '</p>';
print "<p>Query:</p>
<blockquote>
$query
</blockquote>";
exit();
}
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 11. Another version of save-new-product.php
The new stuff starts at line 26. If there’s an error, a message is shown, and the program exits at once. This gives ugly error messages, but that’s acceptable. These errors should never happen, unless either:
Line 43 shows the “is not a number” function in PHP:
is_nan()
The syntax is a little different from JavaScript. In JS, it’s:
isNaN()This is JavaScript, not PHP.
But they do the same thing.
You could also use:
if ( ! is_numeric($price) ) {
is_numeric() is TRUE if you give it a numeric value. ! means “not.” So this version of the if says: “if it is not true that $price is numeric.”
Use either isNaN() or ! is_numeric(). Use the one that makes more sense to you.
Line 44 shows a good practice: it outputs the value that is incorrect. This helps in debugging.
Here’s some new code for save-new-article.php:
//Validate
if ( $title == '' ) {
print '<p>Error! Title is missing.</p>';
exit();
}
if ( $body == '' ) {
print '<p>Error! Body is missing.</p>';
exit();
}
if ( $author == '' ) {
print '<p>Error! Author is missing.</p>';
exit();
}
Figure 12. Code for save-new-article.php
It just makes sure that each field has a value.
Add error checking to save-new-joke.php. Check for:
How to check whether your error checking worked? You need to break stuff. Here are some suggestions.
add-joke.php form, and, before you click the Save button, use the XAMPP control panel to stop MySQL. Then click the Save button. This will simulate a DBMS crash.add-joke.php form, and, before you click the Save button, change admin/db-connect.php. Use the wrong password. Then click the Save button.save-joke.php. Now try to save bad data, like a joke with a funniness rating of -8. This simulates a hacker attack.One way to sabotage the client-side error checking is to add a new line:
$("#new_joke_form").submit(function() {
return true;
The return statement will make sure that the form data is always accepted, even if some fields are invalid.
Once you have it working, you can check my solution. But don’t look at it now!
If you want to share your solution with other people, you will need to upload it to your hosting server.
(Log in to enter your solution to this exercise.)
We talked about error checking and validation on this page. We saw two new types of errors:
You can check for them both.
We also talked about simple server-side validation. Even if you can check all data on the client, you should check it on the server as well. To foil Evil Doers.
You know how to add data to the database. But how do you show the data that was added? That’s coming up next.
You know how to add data to a table. Now let’s see how you can show it.
In this lesson, you learn that:
SELECT statement returns a record set. A record set has one or more rows of data.We want to make something like this:

Figure 1. Product catalog
The HTML uses the <table> tag. There’s a row for each product.
<Start table detour>
Let’s have a quick review of <table>s. Suppose we want to make a table like this:

Figure 2. Rendered table
Here’s the HTML code:
<table>
<thead>
<tr>
<th>Food</th>
<th>Rating</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bacon</td>
<td>Yum!</td>
</tr>
<tr>
<td>Brussel sprouts</td>
<td>Yuck!</td>
</tr>
</tbody>
</table>
Figure 3. The <table> tag
The whole thing is wrapped in <table>.
The <tr> tag makes a row. The <td> makes a cell in a row. The “d” in <td> stands for “data.”
<th> makes a cell as well, but with some special formating that’s good for column headers (hence the “h” in <th>).
The <thead> and <tbody> tags separate the table into two regions, header and body. The tags help with styling. For example, you can easily give different background colors to the header and body of the table.
To get the spacing in Figure 2, use the following CSS:
table {
border-spacing: 10px;
}
td {
padding: 10px;
}
Figure 4. CSS for table spacing
This puts 10 pixels between each cell (line 2) and 10 pixels between the edges of each cell and its content (line 5).
That’s all you need to know about tables for now. You can read the table chapter if you want to know more.
</ End table detour>
So our PHP will output HTML code with the table tag. Here’s the rendered product page:

Figure 1 (again). Product catalog
Here’s the HTML that will make it.
<tr>
<td>
<img class='product_image' src='./product-images/ball.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Squeaky ball</p>
<p class='product_description'>Bouncing, squeaking fun!</p>
</td>
<td class='product_price'>1.99</td>
</tr>
<tr>
<td>
<img class='product_image' src='./product-images/frisbee.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Frisbee</p>
<p class='product_description'>A whirling disk of pure goodness! From <a href="http://www.wham-o.com/">Wham-O</a>.</p>
</td>
<td class='product_price'>10.95</td>
</tr>
<tr>
<td>
<img class='product_image' src='./product-images/rope.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Giant chew rope</p>
<p class='product_description'>Nom nom nom, and nom again. Chew this, not human shoes. You know how touchy they are.</p>
</td>
<td class='product_price'>12.95</td>
</tr>
Figure 5. HTML created by PHP
Here’s the pattern for PHP that will take data from a MySQL table, and make HTML to show it:
SELECT statement. SELECT is the SQL command to grab data.SELECT. Could be one record, or five, or 1,289.Here’s the code that will generate the HTML to show the product list. To make the code easier to follow, I’ve omitted some stuff having to do with sorting. I’ll add it back in the next lesson.
I’ve also omitted error checking, e.g., checking that the database connection was successful. That would clutter things, making it harder for you to focus on the core ideas of this lesson.
Here’s the code. We’ll go through it piece by piece.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "
select product_id, name, description, image_file_name, price
from products";
//Run the query.
$record_set = $db->query($query);
//Start the output table.
?>
<table class="product_table">
<thead>
<tr>
<th> </th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for a product.
$product_id = $row['product_id'];
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output
print "
<tr>
<td>
<img class='product_image' src='$path_to_root/product-images/$image_file_name'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>$name</p>
<p class='product_description'>$description</p>
</td>
<td class='product_price'>$price</td>
</tr>";
} //End while.
?>
</tbody>
</table>
Figure 6. PHP to show the product catalog
Lines 3 and 4 open the database. As usual.
Lines 6 to 8 create the SQL query that fetches the data. I broke it across lines to make it easier for me to read. The statement has the form:
SELECTfieldsFROMtable;
When you send this to MySQL:
select name, description, image_file_name, price from products;
You are telling MySQL:
Open up the
productstable, and, for each row, fetchname,description,image_file_name, andprice.
Here’s what you get:

Figure 7. A record set from MySQL
Hmm, that looks like part of a phpMyAdmin screen.
It is. I copied the SQL query from my code, clicked the SQL tab in phpMyAdmin, and pasted in the query. Clicked the Go button, and MySQL ran it.
That’s a good way to test your own queries.
Once the query has been created, it gets run in line 10:
$record_set = $db->query($query);
query() returns a record set, that gets put into the variable $record_set. The record set is this:

Figure 7 (again). A record set from MySQL
It’s the results of running the query.
We’re used to a variable containing something simple, like a number or some text:
$x = 42;
$y = 'The answer!';
But $record_set is different. It contains a complex object.
We don’t care too much what an object really is. Just think of it as a blob of data. There can be all kinds of data in an object.
You need special functions to get at the data inside an object. We’ll see some in a moment.
OK, so we’ve:
Now we need to show the records in a <table>. There’s some code in Figure 6 to output the <table> tag and the header:
<table class="product_table">
<thead>
<tr>
<th> </th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
Part of Figure 6. PHP to show the product catalog
Line 16 is a little strange.
<th> </th>
The first column in the output table has the item’s picture. It doesn’t need a heading. So line 16 creates an empty heading cell.
Here’s the next part of the code, where the meat is:
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for a product.
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output
print "
<tr>
<td>
<img class='product_image' src='$path_to_root/product-images/$image_file_name'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>$name</p>
<p class='product_description'>$description</p>
</td>
<td class='product_price'>$price</td>
</tr>";
} //End while.
?>
Part of Figure 6. PHP to show the product catalog
while is a new PHP statement for us. It’s a loop. It executes the same PHP statements a bunch of times.
Its format is:
while(test) {
Do this
}
It says:
Keep doing “Do this” while the test is true.
Here’s an example:
<?php
$x = 1;
while ( $x <= 5 ) {
print "$x<br>";
$x += 1;
}
?>
Figure 8. while loop example
This says:
While
$xis less than or equal to 5:
Output$xand a line break tag.
Add 1 to$x.
The PHP engine will keep doing lines 4 and 5 while the condition is true.
Line 5 is a common PHP shortcut. += means “add to.” So:
$x += 1;
means “Add 1 to what is already in $x.”
Here’s the HTML the code generates:
1<br>2<br>3<br>4<br>5<br>
Here’s how it renders:

Figure 9. Rendered output
Let’s have another look at the loop in our database program:
while( $row = $record_set->fetch_assoc() ) {
Stuff to do
}
The PHP engine will keep looping around, doing “Stuff to do” while $row = $record_set->fetch_assoc() is true. When $row = $record_set->fetch_assoc() stops being true (when it is false), the PHP engine stops the loop. It will skip to the code after the loop.
Let’s look at:
$record_set->fetch_assoc()
Recall that $record_set is a record set object. It contains this data:

Figure 7 (again). A record set from MySQL
fetch_assoc() is a function that returns one row.
When a function belongs to an object, it’s really called a “method.” But let’s keep calling it a function for simplicity.
A record set object has other data besides some rows. It also has a record pointer:

Figure 10. Record pointer
The record pointer starts off pointing to the first record.
When you use the fetch_assoc() function, you are saying two things:
What does “Give me the current record” mean? Here’s the code again:
$row = $record_set->fetch_assoc()
fetch_assoc() copies $record_set’s current record into $row. fetch_assoc() doesn’t remove the data from $record_set. It’s still there. fetch_assoc() copies the row, and sends it to $row.
Then fetch_assoc() moves the pointer down. So after fetch_assoc() has run once, we would have:

Figure 11. fetch_assoc() has run once
fetch_assoc() has returned the record it was pointing to, and moved the pointer down one record.
Run fetch_assoc() a second time, and you get:

Figure 12. fetch_assoc() has run twice
Run fetch_assoc() a third time, and you get:

Figure 13. fetch_assoc() has run three times
The record pointer has moved beyond the end of the table.
Run fetch_assoc() a fourth time, and you get:

Figure 14. fetch_assoc() has run four times
The record pointer is pointing to an empty space, so $row = $record_set->fetch_assoc() puts NULL into $row. NULL acts like FALSE in PHP’s if(), while(), and other statements.
Here’s the loop again:
while( $row = $record_set->fetch_assoc() ) {
Stuff to do
}
This will do “Stuff to do” again and again. The first time, $row will have the first record. The second time, $row will have the second record. And so on.
The last time, $row will have the last record. When the while() tries to run again, fetch_assoc() will return NULL, which acts like FALSE. So the loop will stop. Execution will pick up after the brace (}) that ends the loop.
What we have now is a way to grab each record in the record set. Each one gets put into $row. But what do we do with $row?
Remember our goal. We want to create a <table> that contains rows like this:
<tr>
<td>
<img class='product_image' src='./product-images/ball.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Squeaky ball</p>
<p class='product_description'>Bouncing, squeaking fun!</p>
</td>
<td class='product_price'>1.99</td>
</tr>
<tr>
<td>
<img class='product_image' src='./product-images/frisbee.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Frisbee</p>
<p class='product_description'>A whirling disk of pure goodness! From <a href="http://www.wham-o.com/">Wham-O</a>.</p>
</td>
<td class='product_price'>10.95</td>
</tr>
<tr>
<td>
<img class='product_image' src='./product-images/rope.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Giant chew rope</p>
<p class='product_description'>Nom nom nom, and nom again. Chew this, not human shoes. You know how touchy they are.</p>
</td>
<td class='product_price'>12.95</td>
</tr>
Figure 5 (again). HTML created by PHP
Lines 1 to 10 are for the first product. They were created from the first row in the record set:

Figure 7 (again). A record set from MySQL
The second row in the table was created from the second row in the record set.
Here’s the entire loop again:
while( $row = $record_set->fetch_assoc() ) {
//Get fields for a product.
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output
print "
<tr>
<td>
<img class='product_image' src='$path_to_root/product-images/$image_file_name'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>$name</p>
<p class='product_description'>$description</p>
</td>
<td class='product_price'>$price</td>
</tr>";
} //End while.
Part of Figure 6 (again). PHP to show the product catalog
This from line 24:
$row = $record_set->fetch_assoc()
puts a row from the record set into $row.
Remember that $record_set doesn’t have a single value, like 7 or bark. It’s an object, with a bunch of data.
@$row$ doesn’t contain a single value, either. But it isn’t a record set object. It’s an array. We don’t care right now about arrays. Just know that an array contains a bunch of values that you can access.
Remember the SQL query used to create the record set:
select name, description, image_file_name, price from products
So record (row) in the record set has the fields name, description, image_file_name, and price. When:
$row = $record_set->fetch_assoc()
puts the current record in $row, it puts four values into $row.
Here are the next few lines of code:
//Get fields for a product.
$name = $row[‘name’];
$description = $row[‘description’];
$image_file_name = $row[‘image_file_name’];
$price = $row[‘price’];
You can access array elements like this:
$array_name['element name']
So…
$name = $row['name'];
... gets the name element from $row and puts it into the variable $name. $name is not a complex object. It’s just a variable with a simple string in it.
We end up with four variables containing the name, description, image file name, and price for the current record.
Then we can output the variables:
//Output
print "
<tr>
<td>
<img class='product_image' src='$path_to_root/product-images/$image_file_name'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>$name</p>
<p class='product_description'>$description</p>
</td>
<td class='product_price'>$price</td>
</tr>";
Part of Figure 6 (again). PHP to show the product catalog
This PHP statement outputs the fields, inserting them into some HTML, to create a table row.
Here’s all the code again:
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "
select product_id, name, description, image_file_name, price
from products";
//Run the query.
$record_set = $db->query($query);
//Start the output table.
?>
<table class="product_table">
<thead>
<tr>
<th> </th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for a product.
$product_id = $row['product_id'];
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output
print "
<tr>
<td>
<img class='product_image' src='$path_to_root/product-images/$image_file_name'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>$name</p>
<p class='product_description'>$description</p>
</td>
<td class='product_price'>$price</td>
</tr>";
} //End while.
?>
</tbody>
</table>
Figure 6 (again). PHP to show the product catalog
You should be able to follow it now.
Lines 3 and 4 connect to the database.
Lines 6 to 10 run a SELECT query, fetching a record set from MySQL.
Lines 13 to 21 output the table header.
Lines 24 to 43 loop across the record set, showing a table row for each product.
Lines 45 and 46 close the table’s HTML.
W00f!
That’s a lot of new stuff we’ve covered. If you need a brain break, take one.
Let’s take a look at the second sample application, the DogRock CMS.
We want to make something like this:

Figure 15. Article list
Here’s part of the HTML that shows the table:
<tr> <td>A new howl on the prowl</td> <td>February 5, 2010</td> <td>Lumis</td> </tr> <tr> <td>Somebody let them out!</td> <td>February 3, 2010</td> <td>Bounder</td> </tr>
Figure 16. HTML for the article list
I’ve omitted some stuff for simplicity. It’ll come back later.
Here’s the PHP that creates the HTML that makes the page.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "
select title, author, when_published
from articles";
//Run the query.
$record_set = $db->query($query);
//Start the output table.
?>
<table cellpadding="10" cellspacing="10" border="0">
<thead>
<tr>
<th>Title</th>
<th>Date</th>
<th>Author</th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$title = $row['title'];
$when_published = $row['when_published'];
$author = $row['author'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
//Output
print "
<tr>
<td>$title</td>
<td>$when_published</td>
<td>$author</td>
</tr>";
} //end while
?>
</tbody>
</table>
Figure 17. Article list code
Lines 3 and 4 connect to the database.
Lines 6 to 8 create an SQL query:
select title, author, when_published from articles
This says:
From the
articlestable, fetch the fieldstitle,author, andwhen_publishedfor every record.
Why “every record?” Because there’s no WHERE clause. Like this:
select title, author, when_published from articles where author='Bounder'
This would fetch data only for the rows that have Bounder in the author field.
But we don’t have a WHERE clause, so all rows are returned.
Lines 13 to 21 output the table header and such.
The fun starts here:
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$title = $row['title'];
$when_published = $row['when_published'];
$author = $row['author'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
//Output
print "
<tr>
<td>$title</td>
<td>$when_published</td>
<td>$author</td>
</tr>";
} //end while
Part of Figure 15. Article list code
Line 24 fetches a row from the record set. Lines 26 to 28 fetch fields from the row into variables.
Line 30 formats the date in $when_published. The format in the database is YYYY-MM-DD, like 1960-01-03 (my birthday – yes, I’m old). This is the standard international format, but most people aren’t used to it.
This code…
date('F j, Y', strtotime($when_published))
... uses the date() function to format the date in the common “Month Day, Year” format, such as “January 3, 1960.” date() expects the date you give it to be in a certain format – a Unix timestamp. strtotime() converts a YYYY-MM-DD date (from the database) into a Unix timestamp (that the date() function needs).
The output from the date() function is put back into $when_published:
$when_published = date('F j, Y', strtotime($when_published));
The last few lines show the record:
//Output
print "
<tr>
<td>$title</td>
<td>$when_published</td>
<td>$author</td>
</tr>";
So that’s it! We now have a list of articles. W00f!
There’s something missing here, though. Here’s the output we want:

Figure 15 (again). Article list
We want the name of the article to be a clickable link. We’ll look at that later.
Write jokes.php, a page that will show the jokes in your database. You already have a version of the page; it was in the download. You need to add the database logic.
Include error checking code. Check that:
Don’t worry about sorting the records. We’ll talk about that later.
Don’t include the links to the individual jokes. That comes later, too.
Once you have it running, you can check my solution. But don’t look at it now!
Jokes also puts a list of jokes on the home page, like DogRock does. Do that too. Don’t worry about sorting, links, or limiting the number of records. That comes later.
You can check my solution once you have it running.
(Log in to enter your solution to this exercise.)
SELECT statement returns a record set. A record set has one or more rows of data.Let’s see how you can let users sort the data.
You know how to show users the data in a database. What if they want to sort the data? Let’s see how you do that.
In this lesson, you will learn:
ORDER BY clause of the SELECT statement tells MySQL to sort records in the record set.ORDER BY, depending on the parameter.LIMIT clause of the SELECT statement sets the maximum number of records MySQL will add to a record set.Recall that the SQL SELECT command returns a bunch of rows:
select name, description, image_file_name, price from products;
You can tell MySQL how to sort the returned rows. Add ORDER BY and then the field to sort by.
For example, this…
select name, description, image_file_name, price from products order by name;
... returns:

Figure 1. Sorted by name
This…
select name, description, image_file_name, price from products order by image_file_name;
... returns:

Figure 2. Sorted by image_file_name
This…
select name, description, image_file_name, price from products order by price;
... returns:

Figure 3. Sorted by price
By default, the records are sorted in ascending order, from smaller to larger. But you can change this. For example, if you want to sort by price from high to low:
select name, description, image_file_name, price from products order by price desc;
I added desc for “descending” after the sort field name. This returns:

Figure 4. Sorted by price, descending
Sorting is not difficult. But it’s trickier to…
We want to let the user choose how to sort the data. Here’s the interface for the product list:

Figure 5. Sort interface for products
The user can click on the product name to sort by name, or click on an arrow to sort by price, from low to high, or high to low.
You can try it.
How does this work? The URLs give a clue. Here’s how the URL changes when the user clicks on the “Price” link.

Figure 6. User sorting by price
The URL of the page is normally products.php. This gives a default sort order, by name. Then the user clicks the sort arrow. The HTML for the arrow is:
<a href="products.php?order=price_asc">
<img src="./library/arrow_down.png"
alt="Sort from low to high">
</a>
Figure 7. HTML for a sort arrow in products.php
Look at the href in line 1 of Figure 7:
products.php?order=price_asc
So the page points to itself, but with a parameter order.
When this renders (Figure 6), the user sees the data sorted ascending by price.
For this to work, we need:
Let’s look at the code for DogToys.
Here’s products.php:
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "
select product_id, name, description, image_file_name, price
from products
order by ";
//Append the sort order.
$order = $_GET['order'];
if ( $order == 'price_asc' ) {
$query .= 'price asc';
}
else if ( $order == 'price_desc' ) {
$query .= 'price desc';
}
else {
//Default sort order.
$query .= 'name';
}
//Run the query.
$record_set = $db->query($query);
//Start the output table.
?>
<table cellpadding="5" cellspacing="0" border="0">
<thead>
<tr>
<th> </th>
<th><a href="products.php?order=name">Name</a></th>
<th>Price<br>
<a href="products.php?order=price_asc">
<img src="<?php print $path_to_root; ?>/library/arrow_down.png"
alt="Sort from low to high">
</a>
<a href="products.php?order=price_desc">
<img src="<?php print $path_to_root; ?>/library/arrow_up.png"
alt="Sort from high to low">
</a>
</th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
...
Figure 8. products.php
I’ve omitted some code for simplicity.
We need to add the ORDER BY to the SQL. That’s what lines 9 to 21 do.
Line 9 adds “order by” to the SQL statement. But the statement is not complete. Order by what? That’s what the next few lines figure out.
Line 11 …
$order = $_GET['order'];
... fetches the value of the GET parameter order, if there is one. Recall that GET passes data to a Web page through the URL. The value of order is put in the PHP variable $order.
Here are the next few lines (lines 12 to 14):
if ( $order == 'price_asc' ) {
$query .= 'price asc';
}
Recall that .= means “append” or “concatenate.” In normal words, “add to the end of.” So id the variable $order contained price_asc, then price asc would get appended to the SQL statement. It would end like this:
...
order by price asc
Now the order by clause is complete. When the SQL statement is run, the records will be sorted by price in ascending order.
Lines 15 to 17 append price desc to the SQL statement, if $order contains price_desc:
if ( $order == 'price_desc' ) {
$query .= 'price desc';
}
If $order contains anything else, or nothing at all, then line 20 sets the default sort order to name:
$query .= 'name';
The SQL query now has a complete ORDER BY clause, so MySQL will sort the data before returning it.
The last thing we need is the interface, that is, a way for the user to select the sort order. That’s what lines 31, 33, and 37 do:
<a href="products.php?order=name">
<a href="products.php?order=price_asc">
<a href="products.php?order=price_desc">
Clicking these links will cause products.php to reload itself, but with a different sort order.
Here’s the code for articles.php:
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "select article_id, title, author, when_published
from articles
order by ";
//Append the sort order.
$order = $_GET['order'];
if ( $order == 'date' ) {
$query .= 'when_published desc';
}
else if ( $order == 'author' ) {
$query .= 'author';
}
else {
//Default sort order.
$query .= 'title';
}
//Run the query.
$record_set = $db->query($query);
//Start the output table.
?>
<table cellpadding="10" cellspacing="10" border="0">
<thead>
<tr>
<th><a href="articles.php?order=title">Title</a></th>
<th><a href="articles.php?order=date">Date</a></th>
<th><a href="articles.php?order=author">Author</a></th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
...
Figure 9. articles.php
It works the same way as products.php. Clicking a link (lines 28, 29, and 30) causes articles.php to reload itself, passing a sort order.
Line 10 gets the order from the URL.
Lines 11 to 20 complete the SQL statement, depending on which sort order the user selected.
The DogRock site also shows articles on its home page. It shows the three most recent:

Figure 10. Most recent articles
They are sorted descending by date, so the most recent article is first.
Here’s the code for the page, with some things omitted.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch the three most recently published articles.
$query = "select article_id, title, when_published
from articles
order by when_published desc
limit 3";
$record_set = $db->query($query);
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$article_id = $row['article_id'];
$title = $row['title'];
$when_published = $row['when_published'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
print "<p>$title ($when_published)</p>";
} //End while.
?>
Figure 11. index.php for DogRock
Line 8 adds the sort order:
order by when_published desc
The desc means the the highest (most recent) articles are first.
Line 9 shows something new:
limit 3
MySQL will limit the record set it returns to the first three records.
The rest is as before.
Change jokes.php so that users can sort by title, funniness, and date. Don’t worry about linking a joke’s title to its content; that comes later.
You can check my solution. But do it yourself first!
Now change the home page so that the user sees the three funniest jokes.
You can check my solution. But, as before, do it yourself first!
(Log in to enter your solution to this exercise.)
ORDER BY clause of the SELECT statement tells MySQL to sort records in the record set.ORDER BY, depending on the parameter.LIMIT clause of the SELECT statement sets the maximum number of records MySQL will add to a record set.Let’s see how you let users “drill down,” that is, go from summary data (e.g., just the title of an article) to details (e.g., all of the data about the article).
You know how to create a listing of data, like the DogRocks’ article list. But each listing only shows the title of the article, not the article itself.
In this lesson, you will learn that:
“Drilling down” means to get more detailed information, to go from a summary to detail.
Here’s the article list from DogRock:

Figure 1. Article list, from articles.php
The reader gets a little information on what each article is about: the title. If the reader wants more, s/he can click on the title of the article. For example, clicking on the first one shows:

Figure 2. An article
How does this work? Here’s some of the HTML that creates Figure 1, the article list:
<tr> <td><a href='show-article.php?id=2'>A new howl on the prowl</a></td> <td>February 5, 2010</td> <td>Lumis</td> </tr> <tr> <td><a href='show-article.php?id=1'>Somebody let them out!</a></td> <td>February 3, 2010</td> <td>Bounder</td> </tr>
Figure 3. HTML generated by articles.php
Lines 1 to 5 show a table row for an article. You can see the link to the article on line 2:
<a href='show-article.php?id=2'>
show-article.php will show an article, if you send it the article’s id. If you want a different article, send it a different id.
Here’s a picture of what’s happening:

Figure 4. Link to article
You can see the HTML that makes the link. The user clicks on the link. The browser jumps to show-article.php, passing id of the article to show.
What are the ids? They’re from the database:

Figure 5. articles table
The article ids are the primary key values.
So we need to do two things to get this working:
articles.php, to create links to the articles, links like <a href='show-article.php?id=2'>.show-article.php, so it will take an id, and show the article with that id.Let’s look at each piece.
Here’s code from articles.php. I’ve omitted error control and sorting.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "select article_id, title, author, when_published
from articles";
//Run the query.
$record_set = $db->query($query);
//Start the output table.
...
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$article_id = $row['article_id'];
$title = $row['title'];
$when_published = $row['when_published'];
$author = $row['author'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
//Output
print "
<tr>
<td><a href='show-article.php?id=$article_id'>$title</a></td>
<td>$when_published</td>
<td>$author</td>
</tr>";
} //end while
?>
Figure 6. articles.php
Lines 3 and 4 connect to the MySQL database.
Lines 6 to 9 create and run the MySQL query. Here it is:
select article_id, title, author, when_published from articles
The query retrieves article_id, because we’ll need it to make the link to show-article.php. show-article.php needs to know which article to show.
Here’s the data the query might fetch:

Figure 7. Article data
The while() loop (lines 13 t0 28) works as before. It grabs the fields from each row, including each record’s article_id and title. They’re put in the variables $article_id and $title (lines 15 and 16).
The program outputs the link to each article with line 24:
<a href='show-article.php?id=$article_id'>$title</a>
If $article_id is 2 and $title is A new howl on the prowl (from the first record in Figure 7), then we get:
<td><a href='show-article.php?id=2'>A new howl on the prowl</a></td>
W00f! We have the article list.
How do we show the article? Here’s show-article.php, with error checking omitted.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch article data.
$article_id = $_GET['id'];
$query = "select title, body, when_published, author
from articles
where article_id = " . $article_id;
$record_set = $db->query($query);
$row = $record_set->fetch_assoc();
//Get fields.
$title = $row['title'];
$body = $row['body'];
$when_published = $row['when_published'];
$author = $row['author'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
//For the body, change new lines into <br> tags.
$body = str_replace("\n", "<br>", $body);
//Output.
print "
<h2>$title</h2>
<p>By $author</p>
<p>$when_published</p>
<p>$body</p>
";
?>
Figure 8. show-article.php
Line 6 gets the id of the article from the URL.
Lines 7 to 9 use the id to create an SQL statement to fetch the article with that id. It will fetch just one article. Remember that article_id is the primary key of the article table, so each row in the table will have a different value for article_id.
Line 10 runs the query, getting a record set back from MySQL. It puts the record set into the variable $record_set.
Line 11 takes the row from the record set:
$row = $record_set->fetch_assoc();
There is only one row, so no loop is needed.
Lines 13 to 16 extract the fields from the $row. Line 18 formats the date.
I don’t see the article id there. Why don’t you have something like this?
$article_id = $row['article_id'];
article_id was passed into the page. The page got it on line 6:
$article_id = $_GET['id'];
So there was no need to fetch it again from the database.
Lines 20 is something new. The body field of the articles table contains the main text of the article. When a writer types it in, s/he might do something like this:

Figure 9. Typing in an article
The writer added a blank line between the two paragraphs. It gets stored this way my MySQL:

Figure 10. Article body shown by phpMyAdmin
But, as you know, browsers don’t render whitespace in HTML. And an empty line is whitespace.
Take this:
<blockquote> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet. </p> </blockquote>
Figure 11. Empty line in body
It will render as:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet.
Figure 12. Empty line in body, rendered
The two paragraphs are smooshed together. There isn’t any whitespace between them, because browsers don’t render whitespace in HTML.
How can we make sure that the empty lines in the body are rendered by the browser?
Here’s one way, from line 20 of the code:
$body = str_replace("\n", "<br>", $body);
\n stands for the “new line” character. It’s the character that your keyboard makes when you press the Enter key. The str_replace() function goes through $body, and replaces all of the \ns with <br>s. As you, the <br> renders as a line break.
By the way, it’s important to use double quotes – "\n" – rather than single quotes – '\n'. PHP will only interpret the \n as the new line character when it’s in double quotes.
Here’s what $body looks like before the str_replace():
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet.
It contains two \ns, one at the end of line 1, and other at the end of line 2. They’re might have been one at the end of line 3, but the writer didn’t type it.
Here’s what $body looks like after the substitution:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet. <br> <br>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet.
The two <br>s make the browser show the empty line. W00f!
Here’s the rest of the code from show-article.php:
//Output. print " <h2>$title</h2> <p>By $author</p> <p>$when_published</p> <p>$body</p> ";
Figure 13. Part of show-article.php
It outputs the article data.
Change jokes.php so that each joke title is a link to the page show-joke.php. This page shows all of the details of a joke. You can see the page in action on my sample site.
Do the same for the home page.
You can see my code for:
But don’t look at them now! Do it yourself first.
(Log in to enter your solution to this exercise.)
You learned:
You know how to add records, and show them. What about maintaining the data? How do we let people edit and delete records?
That’s what we look at next. But we start with the administration menu. It lets users select the records they want to edit or delete.
Learn that:
Both DogToys and DogRock have two parts to their sites:
The pages for the administration part of the site are all in the admin directory. Here is the layout of the DogToys site:

Figure 1. DogToys directory layout
The administration menu (admin/index.php) gives users access to the all of the administrative functions. This is what it looks like:

Figure 2. DogToys administration menu
At the top is a link to add a new record. At the bottom is a list of existing records, with Edit and Delete links. This is much like the list of products on the product catalog, but with some extra stuff.
Let’s see how this works.
Here’s the code. I’ve included all the sorting and error checking.
<p>What do you want to do?</p>
<blockquote>
<p><a href="add-product.php">Add a new product</a></p>
</blockquote>
<h2>Current products</h2>
<?php
//List the current products, along with edit/delete action for each one.
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
//Fetch product data.
$query = "select product_id, name, description,
image_file_name, price
from products
order by ";
//Get the product list order, if given.
$order = $_GET['order'];
if ( $order == 'price_asc' ) {
$query .= 'price asc';
}
else if ( $order == 'price_desc' ) {
$query .= 'price desc';
}
else {
//Default sort order.
$query .= 'name';
}
$record_set = $db->query($query);
if ( $db->error != '' ) {
print '<p>SQL error! Message: ' . $db->error . '</p>';
print "<p>Query:</p>
<blockquote>
$query
</blockquote>";
exit();
}
//Start the product table.
?>
<table cellpadding="5" cellspacing="0" border="0">
<thead>
<tr>
<th> </th>
<th><a href="index.php?order=name">Name</a></th>
<th>Description</th>
<th>Price<br>
<a href="index.php?order=price_asc">
<img src="<?php print $path_to_root; ?>/library/arrow_down.png"
alt="Sort from low to high">
</a>
<a href="index.php?order=price_desc">
<img src="<?php print $path_to_root; ?>/library/arrow_up.png"
alt="Sort from high to low">
</a>
</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an product.
$product_id = $row['product_id'];
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output
print "
<tr>
<td>
<img src='$path_to_root/product-images/$image_file_name'>
</td>
<td>$name</td>
<td>$description</td>
<td>$price</td>
<td>
<a href='edit-product.php?id=$product_id'>Edit</a><br>
<a href='confirm-delete-product.php?id=$product_id'>Delete</a>
</td>
</tr>";
} //End while.
?>
</tbody>
</table>
Figure 3. DogToys administration menu code
Line 3 makes a link to the add-product.php page.
Lines 9 and 10 connect to the database. Lines 11 to 15 check that the connection was successful.
Lines 17 to 32 create the SQL query. The statement has an ORDER BY clause attached.
Line 33 runs the query. Lines 34 to 41 check for error reports from the database.
Lines 66 to 87 output a table row for each product. Here are the lines that make the Edit and Delete links:
<a href='edit-product.php?id=$product_id'>Edit</a><br>
<a href='confirm-delete-product.php?id=$product_id'>Delete</a>
They use the “drill-down” method we used in the previous lesson. The product id is attached to the URL for the edit and delete pages.
We’ll look at how editing and deletion is done in future lessons. For now, just notice how the links are created to edit-product.php and confirm-delete-product.php.
The DogRock administration menu is much the same:

Figure 4. DogRock administration menu
Here are excerpts from the code that makes the menu:
<p>What do you want to do?</p>
<blockquote>
<p><a href="add-article.php">Add a new article</a></p>
</blockquote>
<h2>Current articles</h2>
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
...
//Fetch article data.
$query = "select article_id, title, author, when_published
from articles
order by ";
...
$record_set = $db->query($query);
...
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$article_id = $row['article_id'];
$title = $row['title'];
$when_published = $row['when_published'];
$author = $row['author'];
...
//Output
print "
<tr>
<td><a href='../show-article.php?id=$article_id'>$title</a></td>
<td>$when_published</td>
<td>$author</td>
<td>
<a href='edit-article.php?id=$article_id'>Edit</a><br>
<a href='confirm-delete-article.php?id=$article_id'>Delete</a>
</td>
</tr>";
}
?>
Figure 5. DogRock administration menu code
It’s much the same as the other administration menu. Lines 33 and 34 create the Edit and Delete links.
Add an administration menu to your Jokes application. It should have a link to the add form, plus edit and delete links for each joke.
The links should refer to edit-joke.php and confirm-delete-joke.php. They don’t exist yet; you’ll add them later.
You can check my solution. But don’t look at it now! Do it yourself, first.
(Log in to enter your solution to this exercise.)
Let’s see how record deletion works.
You know how to create an admin menu, with a delete link for each record. But what happens when the user clicks on one? Let’s see.
In this lesson, learn:
DELETE statement.The SQL DELETE statement deletes records from a table. Here’s a typical example:
delete from dogs where weight < 25;
This will delete all records from the dogs table that have a weight less than 25.
Typically, you use the primary key value to delete a single record. Like this:
delete from articles where article_id = 17;
The administration menu looks like this:

Figure 1. DogToys administration menu
There’s a Delete link for each record. Here’s the PHP that makes it:
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an product.
$product_id = $row['product_id'];
...
print "
...
<a href='confirm-delete-product.php?id=$product_id'>Delete</a>
?>
</tbody>
</table>
Figure 2. DogToys administration menu code
The code generates HTML like this:
<a href='confirm-delete-product.php?id=2'>Delete</a>
Figure 3. HTML generated by PHP
When the user clicks on a Delete link, we could delete the record immediately. But that’s not a good idea. It would be easy to accidentally click the Delete link, maybe when aiming for the Edit link.
So let’s ask the user to confirm the deletion. That’s why the link in Figure 3 calls the page confirm-delete-product.php. It renders like this:

Figure 4. Confirm deletion
The page shows the record that the user has chosen to delete. The user has to click the button before the record will be deleted.
Here’s code for this page.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch product data.
$product_id = $_GET['id'];
$query = "select name, image_file_name, price
from products
where product_id = " . $product_id;
$record_set = $db->query($query);
$row = $record_set->fetch_assoc();
//Get fields.
$name = $row['name'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output confirmation.
print "
<p>You have chosen to delete this product:</p>
<blockquote>
<p><img src='$path_to_root/product-images/$image_file_name'> $name</p>
<p>$$price</p>
</blockquote>
";
?>
<form method="post" action="delete-product.php">
<p>Are you sure you want to do this?</p>
<p>The action cannot be undone.</p>
<p>
<input type="hidden" name="id" value="<?php print $product_id; ?>">
<button type="submit">Confirm</button>
</p>
</form>
<p><a href="index.php">< Back</a></p>
Figure 5. confirm-delete-product.php
Lines 3 and 4 connect to the database.
Line 6 gets the id of the record from the URL.
Lines 7 to 9 creates an SQL SELECT statement with the id. Line 10 runs the query.
Line 11 gets a row from the record set (there will be only one) and puts it into the variable $row. Lines 13 to 15 get the fields from $row, putting them into variables.
The next few lines show the field values:
print "
<p>You have chosen to delete this product:</p>
<blockquote>
<p><img src='$path_to_root/product-images/$image_file_name'> $name</p>
<p>$$price</p>
</blockquote>
";
Part of Figure 5. confirm-delete-product.php
Line 21 looks a little odd:
<p>$$price</p>
Why two dollar signs? The second dollar sign is part of the variable name. The first one is shown on the page. So if $price is 9.95, then the following shows:
<p>$9.95</p>
Let’s look at the screen shot again:

Figure 4 (again). Confirm deletion
We’ve done everything except for the button. Here’s the code:
<form method="post" action="delete-product.php">
<p>Are you sure you want to do this?</p>
<p>The action cannot be undone.</p>
<p>
<input type="hidden" name="id" value="<?php print $product_id; ?>">
<button type="submit">Confirm</button>
</p>
</form>
Another part of Figure 5. confirm-delete-product.php
The program that will delete the product is delete-product.php. It’s the action property of the form:
<form method="post" action="delete-product.php">
We only need to send one piece of data to the page: the id of the product to delete. How to add it to the form? With a hidden field. It’s in line 29:
<input type="hidden" name="id" value="<?php print $product_id; ?>">
If the product id is, say, 17, the HTML will be:
<input type="hidden" name="id" value="17">
A hidden field is like a regular <input> field, but it isn’t shown on the page. Its value, however, is passed to the destination page, like any other field on a form.
What happens when the user clicks the Confirm button? The id is sent to delete-product.php, which actually does the deletion.
Here’s the code for delete-product.php. I left out the error checking code.
<?php
//Delete a product.
//Input:
// id: id number of the product. POST.
$path_to_root = '..';
$product_id = $_POST['id'];
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Delete the product.
$query = "delete from products where product_id = $product_id";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 6. delete-product.php
Line 6 gets the id passed to the page.
Lines 8 and 9 connect to the database.
Line 11 creates the SQL statement that will delete the product:
$query = "delete from products where product_id = $product_id";
Line 12 runs the query.
Finally…
header('location:index.php');
...back to the administration menu.
The administration menu for DogRock looks like this:

Figure 7. DogRock administration menu
There’s a Delete link for each record. Here’s the PHP that makes it:
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$article_id = $row['article_id'];
...
print "
...
<a href='confirm-delete-article.php?id=$article_id'>Delete</a>
?>
</tbody>
</table>
Figure 8. DogRock administration menu code
Clicking on the Delete link loads a confirmation page:

Figure 9. Confirm deletion
Here’s the code for this page.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch product data.
$product_id = $_GET['id'];
$query = "select name, image_file_name, price
from products
where product_id = " . $product_id;
$record_set = $db->query($query);
$row = $record_set->fetch_assoc();
//Get fields.
$name = $row['name'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output confirmation.
print "
<p>You have chosen to delete this product:</p>
<blockquote>
<p><img src='$path_to_root/product-images/$image_file_name'> $name</p>
<p>$$price</p>
</blockquote>
";
?>
<form method="post" action="delete-product.php">
<p>Are you sure you want to do this?</p>
<p>The action cannot be undone.</p>
<p>
<input type="hidden" name="id" value="<?php print $product_id; ?>">
<button type="submit">Confirm</button>
</p>
</form>
<p><a href="index.php">< Back</a></p>
Figure 10. confirm-delete-article.php
As before, it shows the record to be deleted, then a <form> with:
hidden field with the id of the record.Here’s the code for delete-article.php.
<?php
//Delete an article.
//Input:
// id: id number of the article. POST.
$path_to_root = '..';
$article_id = $_POST['id'];
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Delete the article.
$query = "delete from articles where article_id = $article_id";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 6. delete-article.php
This is almost identical to delete-product.php.
Add confirm-delete-joke.php and delete-joke.php to your Jokes application. They should act like their DogToys and DogRock counterparts.
You can see my solutions for the confirmation and deletion pages. But write them yourself first!
(Log in to enter your solution to this exercise.)
DELETE statement.We’re almost done. Time to let users edit existing records.
You’ve created an administration menu, that lets users delete and edit records. You know how to program the delete part. Let’s talk about the edit part.
In this lesson, learn:
UPDATE statement changes data in an existing record. Usually, it has a single primary key value in its WHERE clause.Here’s a screenshot of someone editing a product record:

Figure 1. Editing a product record
The user changes the values and clicks the button.
Here’s the form for adding a product:

Figure 2. Adding a product
They’re almost the same. They have the same form fields, and the same client-side validation.
The main differences between add and edit are:
product_id. New records don’t have a product id. MySQL will choose an id value when it adds a record to the database. (Recall that we made product_id an auto_increment field.)UPDATE. To add a new record, we use the INSERT statement.UPDATE statementLet’s look at the dogs table again. Each row has data about one dog. The table has the fields:
dog_id: the dog’s id number (integer, primary key).name: name of the dog (character).breed: breed of the dog (character).weight: weight of the dog in pounds (integer).Here’s some sample data:

Figure 3. Dog data
Suppose Brian puts on two pounds. Here’s an SQL statement to show the change:
update dogs
set weight = 53
where dog_id = 5;
The statement tells MySQL three things:
dogs. dog_id of 5.weight to 53.Most update statements just change one record, using a primary key value.
You can change text values, like this:
update dogs
set name = 'Fido'
where dog_id = 5;
Don’t forget the quotes around the text.
You can change more than one field at a time. For example:
update dogs
set name = 'Fido',
weight = 56
where dog_id = 5;
When we changed the weight to 53. Could we do this?
update dogs
set weight = 53
where name = 'Brian';
Yes, we could. You can use text fields in the where clause. It would work in this case, but…
Could there be more than one dog named Brian?
Hmm, I suppose there could be.
Right! And your SQL statement would change the weight of all those dogs to 53.
So, usually, when you use the update statement, you use the primary key. This identifies a single record. Here it is again:
update dogs
set weight = 53
where dog_id = 5;
There is only one dog with an id of 5. Even if we had eight dogs called Brian, only one of them would have an id of 5.
Let’s see how we can use the update statement.
Suppose a user wants to edit product data on the DogToys site. Let’s look at the workflow.
The user starts at the admin menu, and selects a product to edit:

Figure 4. Admin menu
Here’s a sample Edit link:
<a href='edit-product.php?id=2'>Edit</a>
Clicking the link jumps to the page edit-product.php, passing an id of 2. This is the product_id of the product the user wants to edit.
edit-product.php shows a form like this:

Figure 1 (again). Editing a product record
The user changes the data and clicks the Save button. If all the validation checks are passed, the new data is saved into the database, and the user is taken back to the main menu.
Here’s a picture of the workflow:

Figure 5. Workflow for editing a product record
Let’s look at the code for edit-product.php, the edit form. Here’s what the code has to produce.

Figure 1 (again). Editing a product record
Each of the form fields shows the current data.
Here’s how edit-product.php works:
Here’s the page. Some of the code has been removed for simplicity.
<?php
...
//Connect to the database.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch product data.
$product_id = $_GET['id'];
$query = "select name, description,
image_file_name, price
from products
where product_id = " . $product_id;
$record_set = $db->query($query);
$row = $record_set->fetch_assoc();
//Extract fields.
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
?>
...
<form id="edit_product_form" method="post" action="save-edited-product.php">
<p>
Name<br>
<input type="text" name="name" id="name" size="30"
value="<?php print $name; ?>">
</p>
<p>
Description<br>
<textarea name="description" id="description" rows="5" cols="30"><?php print $description; ?></textarea>
</p>
<p>
Image file name<br>
<input type="text" name="image_file_name" id="image_file_name" size="30"
value="<?php print $image_file_name; ?>">
</p>
<p>
Price<br>
<input type="text" name="price" id="price" size="10"
value="<?php print $price; ?>">
</p>
<p>
<input type="hidden" name="product_id" value="<?php print $product_id; ?>">
<button type="submit">Save</button>
</p>
</form>
Figure 6. edit-product.php
Line 4 to 5 connect to the database.
Line 7 gets the product id from the URL. Recall that the URLs are like this:
edit-product.php?id=2
Lines 8 to 11 create an SQL statement that looks up the data for the product. For example, if id was 2, the query would be:
select name, description, image_file_name, price
from products
where product_id = 2
Line 12 runs the query. The query only returns one row, because the where clause tests the primary key.
Line 13 fetches the row. Lines 15 to 18 get the individual fields, and put them into variables. For example:
$name = $row['name'];
Here’s how that data is used:
<input type="text" name="name" id="name" size="30"
value="<?php print $name; ?>">
If $name contained Frisbee, this line would become:
<input type="text" name="name" id="name" size="30"
value="Frisbee">
When the browser renders the field, it will put the value Frisbee into it:

Figure 7. Name field rendered
This page has to send the new data to save-product.php. It needs to include the product_id. But the id is not actually shown in the form. The user can’t change it, so there’s no point in showing it.
So how do you put some data into a form so that it can be sent, but not have it visible to the user?
The solution: use a hidden field. Like this:
<input type="hidden" name="product_id" value="<?php print $product_id; ?>">
The product_id will travel along with the rest of the form fields. But the user won’t see it.
W00f!
Here’s the workflow again:

Figure 5 (again). Workflow for editing a product record
When the user clicks the Save button, the browser sends the data to save-edited-product.php. This is the action property of the form in line 21 in Figure 6:
<form id="edit_product_form" method="post" action="save-edited-product.php">
Here’s what save-edited-product.php has to do:
update statement.Here’s the code. Error checking and some other code has been omitted.
<?php
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Get the form fields.
$product_id = stripslashes($_POST['product_id']);
$name = stripslashes($_POST['name']);
$description = stripslashes($_POST['description']);
$image_file_name = stripslashes($_POST['image_file_name']);
$price = stripslashes($_POST['price']);
//Make the fields safe.
$product_id = $db->escape_string($product_id);
$name = $db->escape_string($name);
$description = $db->escape_string($description);
$image_file_name = $db->escape_string($image_file_name);
$price = $db->escape_string($price);
//Create and run the SQL.
$query = "update products
set name = '$name',
description = '$description',
image_file_name = '$image_file_name',
price = $price
where product_id = $product_id";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 8. save-edited-product.php
Lines 4 and 5 connect to the database.
Lines 7 to 11 get the form data, including the product_id passed as a hidden field. Note that stripslashes() was used to undo PHP’s “helpful” insertion of backslashes.
Lines 13 to 17 make the data safe from Evil Doers. It defuses SQL injection attacks.
Lines 19 to 24 creates the SQL query. It includes quotes (’) for text fields, and a where clause to select the right product.
Line 25 runs the query.
Line 27 jumps back to the admin menu.
That’s it for DogToys. Let’s move on.
The workflow for editing an article is the same as the workflow for editing a product:
Here’s what the form looks like:

Figure 9. Editing an article
Here’s the code, with some stuff omitted:
<?php
//Connect to the database.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch article data.
$article_id = $_GET['id'];
$query = "select title, body, when_published, author
from articles
where article_id = " . $article_id;
$record_set = $db->query($query);
$row = $record_set->fetch_assoc();
//Extract fields.
$title = $row['title'];
$body = $row['body'];
$when_published = $row['when_published'];
$author = $row['author'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
?>
<form id="new_article_form" method="post" action="save-edited-article.php">
<p>
Title<br>
<input type="text" name="title" id="title" size="40"
value="<?php print $title; ?>">
</p>
<p>
Author<br>
<input type="text" name="author" id="author" size="40"
value="<?php print $author; ?>">
</p>
<p>
When published<br>
<input type="text" name="when_published" id="when_published" size="40"
value="<?php print $when_published; ?>">
</p>
<p>
Body<br>
<textarea name="body" id="body" rows="8" cols="40"><?php print $body; ?></textarea>
</p>
<p>
<input type="hidden" name="article_id" value="<?php print $article_id; ?>">
<button type="submit">Save</button>
</p>
</form>
Figure 10. edit-article.php
Lines 3 and 4 connect to the database.
Lines 6 gets the id of the article wants to edit. Lines 7 to 9 create the SQL query, which includes the article’s primary key. Line 10 runs the query, and line 11 gets the row fetched by MySQL.
Lines 13 to 16 extract the individual fields from the row. Line 18 formats the date to a familiar format.
The form is then shown. Each field’s value property puts the current value into the field. Line 41 adds the article-id as a hidden field, so the id will get passed to the page that saves the data.
Here’s save-edited-article.php, the program that saves the new article data.
<?php
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Get the form fields.
$article_id = stripslashes($_POST['article_id']);
$title = stripslashes($_POST['title']);
$body = stripslashes($_POST['body']);
$author = stripslashes($_POST['author']);
$when_published = stripslashes($_POST['when_published']);
//Make the fields safe.
$article_id = $db->escape_string($article_id);
$title = $db->escape_string($title);
$body = $db->escape_string($body);
$author = $db->escape_string($author);
$when_published = $db->escape_string($when_published);
//Format the date.
$when_published = date('Y-m-d', strtotime($when_published));
//Create and run the SQL.
$query = "update articles
set title = '$title',
author = '$author',
body = '$body',
when_published = '$when_published'
where article_id = $article_id";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 11. save-edited-article.php
Lines 4 and 5 connect to the database. Lines 7 to 11 get the data passed into the form, and strips the excess backslashes with stripslashes().
Lines 13 to 17 sanitize the data. Line 19 converts the publication date into the format MySQL prefers.
Lines 21 to 26 create the SQL update statement. Line 27 runs the query.
Line 29 jumps back to the admin menu.
W00f!
Here’s the pattern for the edit page itself.
[node:pattern/updating-database-record noterms]
Give users the ability to edit existing jokes. Model your code on DogToys and DogRock.
You can see my code for edit-joke.php and save-edited-joke.php. But do it yourself first!
(Log in to enter your solution to this exercise.)
UPDATE statement changes data in an existing record. Usually, it has a single primary key value in its WHERE clause.W00f!
Time for some more exercises.
On your local machine, create a database called dogmovies. It will have data about movies that have dogs in them.
Make a user who can access the database.
Add a table to the database. Call it movies. Add fields for:
Add some movie data from this page.
Write a PHP test page to connect to the database.
Now duplicate the database and the test page on your hosting account.
Put the URL of the test page below.
(Log in to enter your solution to this exercise.)
On your local machine, create a database called dogbooks. It will have data about books about dogs.
Make a user who can access the database.
Add a table to the database. Call it books. Add fields for:
The last field is a paragraph or two about the book.
Add some book data from Dogwise, or another site. There’s lots of good stuff there.
Write a PHP test page to connect to the database.
Now duplicate the database and the test page on your hosting account.
Put the URL of the test page below.
(Log in to enter your solution to this exercise.)
On your local machine, create a database called dogsites. It will have data on Web sites about dogs.
Make a user who can access the database.
Add a table to the database. Call it sites. Add fields for:
Add some data about dog sites.
Write a PHP test page to connect to the database.
Now duplicate the database and the test page on your hosting account.
Put the URL of the test page below.
(Log in to enter your solution to this exercise.)
Create a version of the DogMovies site with a home page and a movies list. The home page should look something like this, but with your own graphics and colors:

Figure 1. Home page
Here’s the movie list page.

Figure 2. Movies
Clicking on the column headings sorts the data.
Add at least five movies to your database.
You can try my solution.
Upload your application to your hosting account. Put the URL below.
(Log in to enter your solution to this exercise.)
Create a version of the DogBooks site with a home page, a book list, and a page describing each book. The home page should look something like this, but with your own graphics and colors:

Figure 1. Home page
Here’s the book list page:

Figure 2. Book list
Clicking on a column header sorts the pages.
Here’s part of a book description page:

Figure 3. Book description
You can try my solution.
Upload your application to your hosting account. Put the URL below.
(Log in to enter your solution to this exercise.)
Create a version of the DogSites site with a home page, and a site list. The home page should look something like this, but with your own graphics and colors:

Figure 1. Home page
Here’s the site list page:

Figure 2. Site list
You can try my solution.
Upload your application to your hosting account. Put the URL below.
(Log in to enter your solution to this exercise.)
Add an administration section to the DogMovies Web site.
You can see my solution.
Enter the URL of your solution below.
(Log in to enter your solution to this exercise.)
Add an administration section to the DogBooks Web site.
You can see my solution.
Enter the URL of your solution below.
(Log in to enter your solution to this exercise.)
Add an administration section to the DogSites Web site.
You can see my solution.
Enter the URL of your solution below.
(Log in to enter your solution to this exercise.)
Recall that DogToys has two parts to it:

Figure 1. DogToys
One part of DogToys is for customers. That’s the blue area. It has pages for tasks that customers need to do. Like look at a list of products.
The other part of DogToys is for employees. That’s the green area. It has pages for tasks that employees need to do. Like add products and change prices.
But we don’t want just anyone messing with prices. This chapter shows you how to let different people access different pages, depending on what they are allowed to do.
You can try the secure version of the DogToys application. You have to look at the admin menu to see the changes (user names and passwords are here – both are case-sensitive). As before, this version of DogToys won’t update product data.
You can download the files for the application. If you want to run the application on your own computer, you’ll need to change the database connection information.
We want to restrict who can see what pages on a site. Let’s make sure we understand what that means.
Learn:
Here’s the situation:

Figure 1. Louise wants to change a price
Louise wants to change a product’s price. edit-product.php is the page that lets her do that.
We’ll add some code to DogToys, including edit-product.php. The new stuff needs to handle two things:
We’ll give every person in the company a user name and a password. They’ll have to log in before they can use any of the administrative functions of DogToys (like edit prices, add products, and delete products).
The log in page will look like this:

Figure 2. Log in page
The person types in his/her user name and password:

Figure 3. Louise logs in
All of the admin pages are in a separate part of the site. They’re in the admin/ directory. There’s an admin menu, that shows all of the tasks authorized users are allowed to do.
Here is part of the admin menu, shown after the user logs in:

Figure 4. Admin menu
It shows the name of the logged in user. It has a link to log out. Click it, and the browser will jump back to the log in page.
Every admin page, like edit-product.php, will have some new PHP at the very beginning. It will make sure that someone is logged in:
if ( nobody is logged in ) Jump to the log in page. ...
Figure 5. Log in check
If nobody is logged in, the browser will be sent to the log in page.
So now we know who is logged in. But what is that user allowed to do?
Let’s add a table to the DogToys database.

Figure 6. users table
We’ll actually use different field names later.
For each user, there’s a set of permissions. There’s a y if the user is allowed to do a task, like edit. If the user isn’t allowed, there’s an n.
Each page will check the permissions before running.
Let’s add to the security code in edit-product.php:
if ( nobody is logged in ) Jump to log in page. if ( edit permission is 'n' ) Show "Permission denied" message. Stop. ...
Figure 7. Log in and permissions check
We need to figure out how to:
You already know all the PHP you need, except for one thing: remembering who a user is once s/he has logged in. We’ll look at that in the next lesson.
Let’s talk about PHP sessions. DogToys will use sessions to remember who has logged in.
This chapter is about restricting access to pages on a Web site. We just talked about the goal: a workflow with log in, actions, and log out.
This page talks about PHP sessions. We’ll use use to create the workflow we want.
Learn that:
Louise works for DogToys, keeping product information up-to-date. Here’s one of Louise’s work sessions.

Figure 1. Louise’s work session
Louise logs in. Then she does her work: adding products, editing products, whatever she needs to do. At the end of the session, she logs out.
Once she has logged in, all of the pages – edit-product.php, add-product.php, and so on – know who she is, and what permissions she has. She doesn’t have to log in again for each change.
This is the workflow we want. But…
Each PHP page is a program running on the server. When the program starts running, it asks the server’s operating system for some memory, where it can store variables.

Figure 2. Page memory
Each time the code makes a new variable, a place is created for the variable in the page’s memory.
Here’s an important thing: when the page is finished, the page’s memory is erased. The server’s operating system grabs the memory back. So there’s nothing left of the variables. They’re all gone.
Every page has its own memory space:

Figure 3. Each page has some memory
All of the page memories are independent. Nothing is shared.
Remember we want to have a log in page, log-in.php. How does this page let the other pages know that Louise is allowed to use the system? log-in.php has to leave something behind to say, “Louise is OK.” But how?
That’s where PHP sessions come in. Sessions do a number of things, but the most important is that they create some memory on the server that exists separately from any one page:

Figure 4. Session memory
log-in.php can put variables into the session memory. When the server erases log-in.php’s memory, the session memory will still be there.
Other pages can access the session memory. add-product.php can look in it, and see what log-in.php left behind.
This is how we can make authentication work. When a user logs in, we can put some data into the session memory. Other pages can check the session memory, to see what the user is allowed to do.
Each person who connects to DogToys gets their own session memory:

Figure 5. Separate sessions
Actually, the server doesn’t create a separate session for each person, but for each browser that connects. Each browser will be running on a machine with a different IP address:

Figure 6. Sessions for IP addresses
There’s a little more to it, but it’s close enough.
But we’ll continue to talk about user sessions, not IP sessions. It’s simpler.
Suppose each user in Figure 5 – Louise, Jim, Clara, and Joe – connects to log-in.php. When log-in.php runs, it uses separate session data for each user:

Figure 7. log-in.php uses separate sessions
This means that each person’s log in data is kept separate.
Different users will run the same page, like log-in.php. But log-in.php will see different data for each user, because it is pulling data from each user’s own session memory.
Here’s an example. It isn’t accurate about how session data is stored; we’ll see that in a minute.

Figure 8. log-in.php uses different values for each user
log-in.php outputs $p. What will it show? Because the data comes from session memory, each user would see something different.
So the same code – print $p; – could show something different when it is run by different users, if it is using data from session memory.
Let’s look at the PHP statements that mess with sessions. We’ll only look at the core statements.
You need to start the session mechanism before you can use it.
session_start();
This statement has to appear before the page outputs anything, or sessions won’t work.
Once a session has been started, you can store data into it using the $_SESSION array. For example:
$_SESSION['user name'] = 'jimmy';
The string between the quotes names the session data. Other examples:
$_SESSION['favorite number'] = 96;
$_SESSION['favorite color'] = 'green';
$_SESSION['database server'] = 'localhost';
You can retrieve session data as well. Again, you have to start the session first.
$user name = $_SESSION['user name'];
$num = $_SESSION['favorite number'];
$best_color = $_SESSION['favorite color'];
$db_server_host = $_SESSION['database server'];
Destroy a session like this:
session_start();
$_SESSION = array();
session_destroy();
The second line erases the session data. The third one wipes out the session itself.
Let’s look at a sample application that uses session. The user enters his/her name:

Figure 9. User enters name
The name is stored in the session.
The user then sees a menu, like this:

Figure 10. Menu
The user’s name is inserted into captions of photos. Here’s what clicking on the Renata link shows:

Figure 11. Renata
You can try the application, and download its files.
Here are the pages in the application:

Figure 12. Pages in the application
Here are the pieces:
ask-user-name.html shows the form that gets the user’s name. remember-name.php stores the user’s name in the session. menu.php shows the application’s menu. cc.php, kieran.php, and renata.php show photos with the user’s name in the captions.new-name.php erases the user’s name from the session, and jumps back to the user name form.Let’s look at the code.
ask-user-name.html shows the form:

Figure 9 (again). User enters name
The form passes the data to remember-name.php, which stores the data into the session.
Here’s the code for remember-name.php:
<?php
//Get the name the user typed.
$user_name = $_POST['user_name'];
//Validate.
if ( $user_name == '' ) {
header('location:ask-user-name.html');
exit();
}
//Start session.
session_start();
//Store data in session.
$_SESSION['user name'] = $user_name;
//Go to the main menu.
header('location:menu.php');
?>
Figure 13. remember-name.php
remember-name.php doesn’t output anything for the user to see. It puts data into the session, for other pages to use.
Line 3 gets the value the user typed into the form. Lines 5 to 8 check whether the value is empty. If it is, the browser is told to jump back to ask-user-name.html.
Now to store the name into the session. Line 10 starts the session:
session_start();
PHP pages need to do this before they can use sessions.
Line 12 stores the user name into the session:
$_SESSION['user name'] = $user_name;
Line 14 jumps to the menu:
header('location:menu.php');
Here’s the code for menu.php.
<?php
//Do we know the user's name?
session_start();
$user_name = $_SESSION['user name'];
if ( $user_name == '' ) {
header('location:ask-user-name.html');
exit();
}
?><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Menu | Sessions Sample App</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>Menu</h1>
<p>Name: <?php print $user_name; ?></p>
<ul>
<li><a href="cc.php">CC photo</a></li>
<li><a href="kieran.php">Kieran photo</a></li>
<li><a href="renata.php">Renata photo</a></li>
<li><a href="new-name.php">Use a different name</a></li>
</ul>
</body>
</html>
Figure 14. menu.php
Line 3 starts the session.
Line 4 gets some data from the session:
$user_name = $_SESSION['user name'];
Lines 5 to 8 checks that there is a name. If not, the browser is sent to ask-user-name.html.
How could $_SESSION['user name'] be empty? It was just set in remember-name.php, wasn’t it?
You’re right, remember-name.php sets
$_SESSION['user name'], and then it jumps to menu.php.
But that’s only one way the user can get to menu.php. There are other ways. For example, the user can type the page’s URL directly into the browser:

Figure 15. User types menu.php into the browser
This skips remember-name.php, so the session data isn’t set.
It’s best not to assume that the session data is there.
The menu shows the user name:

Figure 10 (again). Menu
Here’s line 17 from menu.php:
<p>Name: <?php print $user_name; ?></p>
Here’s renata.php, that shows the user’s name in a photo caption:
<?php
//Do we know the user's name?
session_start();
$user_name = $_SESSION['user name'];
if ( $user_name == '' ) {
header('location:ask-user-name.html');
exit();
}
?><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Renata Photo | Sessions Sample App</title>
</head>
<body>
<div id="container">
<p><img src="renata.jpg" alt="CC"></p>
<h1 class="loves">Renata loves <?php print $user_name; ?></h1>
</div>
<p><a href="menu.php">< Menu</a></p>
</body>
</html>
Figure 16. renata.php
Line 3 starts the session. Line 4 gets the user’s name from the session:
$user_name = $_SESSION['user name'];
Lines 5 to 8 check whether there is a user name. If not, jump to ask-user-name.html.
Line 18 puts the user name into the photo caption:
<h1 class="loves">Renata loves <?php print $user_name; ?></h1>
What if the user wants to change the name that shows in the captions? There’s a link for that in the main menu.

Figure 10 (again). Menu
The last link in the menu sends the browser to new-name.php. Here is the code:
<?php
session_start();
//Erase the session variables.
$_SESSION = array();
//Kill the session.
session_destroy();
header('location:ask-user-name.html');
?>
Figure 17. new-name.php
new-name.php erases the session data (line 4) and kills the session itself (line 6). Then it sends to browser back to the user name form.
Again, new-name.php doesn’t output anything itself. It does something to the session, and jumps to another page.
You might be asking yourself:
Self, do sessions last forever?
Sessions are destroyed when:
session_destroy();Sessions expire if the browser does not access a page in the application within a certain amount of time. You can set the amount of time sessions last. The default is 20 minutes.
The Dog Center is a meeting center that holds lectures, demonstrations, workshops, and other events. About dogs, of course.
The Center’s management want to put information kiosks at the doors. They’ll be PCs with touch screens, running a Web browser. People can use the kiosks to learn about the events at the Center that day.
Here’s what the kiosk’s screen will look like for a typical day:

Figure 1. Event list
Clicking on a link shows an event:

Figure 2. Event
There’s a joke at the bottom of every page. When a kiosk is started for the day, it asks the user for a joke:

Figure 3. Asking for a joke
The joke is stored in the session. It’s then shown on every page.
Management wants to be able to change the joke if need be. Add a link to the menu to do that. But the link should be hidden on the lower right of the page. The mouse cursor should change when passing over the link, but the link itself should not show:

Figure 4. Secret link
When the link is clicked, the browser asks for a joke again, as in Figure 3.
You can try my solution. You can also download the files, but do it yourself first!
Hint: what happens when color and background-color are the same?
Upload your solution to your hosting account. Put the URL below.
(Log in to enter your solution to this exercise.)
This chapter is about restricting access to pages on a Web site. You just saw how sessions let PHP pages share information. Now let’s talk about storing data about users and their permissions.
You’ve seen the workflow we want in DogToys. You’ve seen how sessions work.
We’ll need some way to remember user names and passwords, and the permissions that each user has. Let’s adding user data to the DogToys database.
Learn:
users table in the database. It will have user names, passwords, and permission flags.users tableLet’s add a table called users to the DogToys database. It will have the data we need for authentication and permissions. Here are the fields:

Figure 1. users table
user_id is the primary key, an unsigned integer. auto_increment means that MySQL will automatically supply values when new records are added to the users table.
user_name is, er, the user name. It can be up to 20 characters long.
password is the user’s password. Like user_name, it can be up to 20 characters long.
We’re storing the password in clear text. You wouldn’t do that in a secure application.
permission_add is a one-character field that is either y or n. If it’s y, the user has permission to add products.
permission_edit is a one-character field as well. If it’s y, the user can change data about existing products.
permission_delete controls whether users are allowed to delete product records.
Here’s some data. I broke the image into pieces so it would fit on this page.

Figure 2. Users data
You can see that Kieran and Louise are allowed to do anything to the data. But Renata can only edit data. She can’t add products or delete them.
Look at the passwords. Renata’s password is terrible. If a hacker learned Renata’s user name, one of the first things s/he would try is using the same thing for the password.
Loiuse’s password isn’t very good either. The characters are all the same.
CC’s password is bad as well. It’s a single word. It’s vulnerable to “dictionary” attacks, where a program tries to log in using English words as passwords. Eventually, it would hit “sheltie.” The word is in dictionary.com.
Kieran’s password is the only good one. It uses four different character types:
It’s not vulnerable to dictionary attacks, and is hard to guess.
We need to be able to:
In a real Web application, we’d write Web pages for these tasks. We’d assign someone as an administrator. We’d add a permissions field that would let an administrator access those the pages that change user data.
To keep things simple, we don’t do that in this chapter. It’s not core.
You can change the user data with phpMyAdmin.
Add a users table to the DogJokes database you created in the previous chapter. Use the same fields I used for the DogToys users table. Add some records.
users table in the database. It will have user names, passwords, and permission flags.Now let’s see how you use the table in the log in process.
This chapter is about restricting access to pages on a Web site.
You know how sessions let PHP pages share information. You’ve seen how we can store data about users and their permissions in a database table.
Now let’s create the log in system that uses that data.
You will learn that:
users.We want the user to see a log in form, like this:

Figure 1. Louise logs in
If the user name and password match a record in the database, then the user would see the admin menu.

Figure 2. Admin menu
Here are the pages we’ll create:

Figure 3. Pages involved in log in
log-in.php shows the form. The user completes it, and clicks the submit button. log-in.php passes the data to check-log-in.php. check-log-in.php checks the database for a matching record in the users table. If it finds one, it stores some data in the session, and sends the browser to the admin menu page, admin/index.php. If there is no matching record, the browser is sent back to the log in form.
Let’s look at the code.
log-in.phpHere’s the log in form again:

Figure 1 (again). Louise logs in
The form does some error checking. There are client-side checks for empty fields:

Figure 4. Empty field errors
But what if the user fills in both fields with an unknown user name and password? This check cannot be done on the client-side. A server-side program has to look up the user name and password before finding that the user name/password combination is unknown.
Here’s the error reporting for a unknown user name/password combination:

Figure 5. Unknown user name/password error
So the page needs to:
The page will also have to:
That’s quite a lot of stuff for log-in.php to do!
Here’s the complete code. We’ll go over all the pieces.
<?php
//Show the admin menu
//Input: order(optional). Sort order for article list. GET.
//Path from this page to the site root.
$path_to_root = '..';
//Already logged in?
session_start();
if ( $_SESSION['logged in'] == 'y' ) {
//Yes - jump to admin menu.
header("location:$path_to_root/admin/index.php");
exit();
}
//Title of this page.
$page_title = 'DogToys Log In';
require $path_to_root . '/library/start_page.inc';
require $path_to_root . '/library/header.inc';
require $path_to_root . '/library/left_nav.inc';
?>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="<?php print $path_to_root;?>/library/show-error-messages.js"></script>
<script type="text/javascript">
$(document).ready(function() {
//Initialization.
$("#global_error_message_container").hide();
$("#user_name").focus();
<?php
if ( $_GET['err'] == 'auth' ) {
print("show_global_error_message('Sorry, user name or password not found.');");
}
?>
//Form submit event.
$("#log_in_form").submit(function() {
//Set flag showing everything is OK.
var data_ok = true;
//Check the user name field.
var user_name = $("#user_name").val();
if ( user_name == '' ) {
data_ok = false;
show_field_error_message('Sorry, you must enter a user name.', 'user_name');
show_global_error_message("Sorry, I can't log you in.");
}
else {
hide_error_message('user_name');
}
//Check the password field.
var user_password = $("#user_password").val();
if ( user_password == '' ) {
data_ok = false;
show_field_error_message('Sorry, you must enter a password.', 'user_password');
show_global_error_message("Sorry, I can't log you in.");
}
else {
hide_error_message('user_password');
}
return data_ok;
});
});
</script>
<div id="center_region">
<h1><?php print $page_title; ?></h1>
<p id="global_error_message_container" class="message_container">
<img src="<?php print $path_to_root; ?>/library/error.png" alt="Error">
<span id="global_error_message"/>
</p>
<form id="log_in_form" method="post" action="check-log-in.php">
<p>
User name<br>
<input type="text" id="user_name" name="user_name" size="20"><br>
<span id="user_name_message_container" class="message_container">
<img src="<?php print $path_to_root; ?>/library/error.png" alt="Error">
<span id="user_name_message"/>
</span>
</p>
<p>
Password<br>
<input type="password" id="user_password" name="user_password" size="20"><br>
<span id="user_password_message_container" class="message_container">
<img src="<?php print $path_to_root; ?>/library/error.png" alt="Error">
<span id="user_password_message"/>
</span>
</p>
<p>
<button type="submit">Log in</button>
</p>
</form>
</div>
<?php
require $path_to_root . '/library/footer.inc';
require $path_to_root . '/library/end_page.inc';
?>
Figure 6. log-in.php
Line 8 starts the session.
Lines 9 to 13 check whether the user has already logged in. This…
$_SESSION['logged in']
...looks for the session variable logged in. If it has the value y, the user is already logged in. The browser is sent to the admin menu immediately, skipping the log in process.
Lines 14 to 18 are part of the templating system.
Lines 20 to 62 mostly implement client-side error checking. But lines 27 to 31 are new. They show errors from the server.
Here are the pages again:

Figure 3 (again). Pages involved in log in
check-log-in.php is the page that finds whether the user name and password are valid. If they aren’t, is sends something to log-in.php, to tell it to show an error message. But what should it send, and how?
Here’s one way to do it. When check-log-in.php finds an error, it tells the browser to jump to log-in.php. It attaches something to the URL. Here is some code from check-log-in.php :
header("location:$path_to_root/admin/log-in.php?err=auth");
See the err=auth bit? This tells log-in.php to show an authentication error. Here’s how the URL looks in a browser’s address bar:

Figure 7. Error flag in URL
When log-in.php receives this, it can show the error message.
We already have some JavaScript code in log-in.php for showing errors. It uses colors, shows an icon, and other Good Stuff. Let’s use that existing JavaScript code to report the server error.
Here’s some code from the JavaScript part of log-in.php.
$(document).ready(function() {
//Initialization.
$("#global_error_message_container").hide();
$("#user_name").focus();
<?php
if ( $_GET['err'] == 'auth' ) {
print("show_global_error_message('Sorry, user name or password not found.');");
}
?>
Figure 8. Part of log-in.php
The JavaScript function show_global_error_message() shows an error message at the top of the page. Line 29 outputs JavaScript that would call that function:
show_global_error_message('Sorry, user name or password not found.');
The PHP in line 28 adds this JavaScript if log-in.php has err passed to it, with the value auth. Recall that GET attaches stuff to the URL.
So, if the server program check-log-in.php can’t find the user name and password in the users database table, it jumps back to log-in.php, with err set to auth.
If log-in.php gets err set to auth, it calls some JavaScript to show an error message. This is PHP writing JavaScript. Again.
The rest of log-in.php should be familiar. But there’s a new thing in line 81:
<input type="password" id="user_password" name="user_password" size="20">
An <input> field with a type of password is almost the same as one with a type of text. The only difference is that the browser doesn’t show what the user types into a password field:

Figure 9. Password field
check-log-in.phpHere are the pages again:

Figure 3 (again). Pages involved in log in
check-log-in.php gets a user name and password from log-in.php. It looks them up in the database. If they’re found, it sets some session data, and jumps to the admin menu. If the user name and password are not found, check-log-in.php jumps back to log-in.php.
Here is the code:
<?php
//Path from this page to the site root.
$path_to_root = '..';
//Get the values the user typed.
$user_name = $_POST['user_name'];
$user_password = $_POST['user_password'];
//Validate
if ( $user_name == '' || $user_password == '') {
header("location:$path_to_root/admin/log-in.php");
exit();
}
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($db_host, $db_user_name, $db_password, $db_name);
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
//Fetch article data.
$query = "select permission_add, permission_edit, permission_delete
from users
where binary user_name = '$user_name'
and binary user_password = '$user_password'";
$record_set = $db->query($query);
if ( $record_set->num_rows == 1 ) {
session_start();
$_SESSION['logged in'] = 'y';
$_SESSION['user name'] = $user_name;
//Store permissions in session.
$row = $record_set->fetch_assoc();
$_SESSION['permission add'] = $row['permission_add'];
$_SESSION['permission edit'] = $row['permission_edit'];
$_SESSION['permission delete'] = $row['permission_delete'];
//Jump to admin menu.
header("location:$path_to_root/admin/index.php");
exit();
}
//Authentication problem.
header("location:$path_to_root/admin/log-in.php?err=auth");
exit();
?>
Figure 10. check-log-in.php
Lines 5 and 6 get the data sent to the page:
$user_name = $_POST['user_name'];
$user_password = $_POST['user_password'];
Lines 8 to 11 make sure that the data is there:
if ( $user_name '' || $user_password '') {
header("location:$path_to_root/admin/log-in.php");
exit();
}
If it isn’t, the browser is sent back to log-in.php. This should never happen, of course, because of the client-side validation in log-in.php. These lines are an extra security check.
Now we need to check the database, and see if the user name and password are valid. Lines 13 to 19 connect to the database.
Lines 21 to 24 create the SQL query that will look up the user name and password:
select permission_add, permission_edit, permission_delete
from users
where binary user_name = '$user_name'
and binary user_password = '$user_password'
Two things to notice. The first is the and. The select will only return records where both conditions are true.
The second thing is the binary keyword. MySQL tests are usually not case-sensitive. So louise, Louise, and lOUise are all the same.
But we don’t want that here. Applications are more secure if user names and passwords as case-sensitive, so louise and Louise are not the same. Adding the binary keyword makes the tests case-sensitive.
Line 25 is:
$record_set = $db->query($query);
It tells MySQL to run the query, returning a record set.
If the user name and password are valid, the record set should return exactly one record. That gets tested in the next piece of code.
if ( $record_set->num_rows == 1 ) {
session_start();
$_SESSION['logged in'] = 'y';
$_SESSION['user name'] = $user_name;
//Store permissions in session.
$row = $record_set->fetch_assoc();
$_SESSION['permission add'] = $row['permission_add'];
$_SESSION['permission edit'] = $row['permission_edit'];
$_SESSION['permission delete'] = $row['permission_delete'];
//Jump to admin menu.
header("location:$path_to_root/admin/index.php");
exit();
}
Figure 11. Part of check-log-in.php
The code…
$record_set->num_rows
...returns the number of rows in the record set. If one record was returned, the user name and password is valid. In that case, we want to store stuff in the session.
This line…
$_SESSION['logged in'] = 'y';
...sets logged in to y. This lets other pages easily test whether there is a logged in user. We’ll see that later.
This line…
$_SESSION['user name'] = $user_name;
...stores the user name passed into check-log-in.php in the session.
Line 31 fetches the row in the record set. Remember that we already know there is only one row.
Lines 32 to 34 get the values of the three permissions fields. They are stored in the session.
Line 36 jumps to the admin menu.
Here’s what happens if the user name/password combination is not found:
if ( $record_set->num_rows == 1 ) {
...
exit();
}
//Authentication problem.
header("location:$path_to_root/admin/log-in.php?err=auth");
Figure 12. Another part of check-log-in.php
The browser is sent back to log-in.php, with a flag showing that log-in.php should show an error message.
users.Now we have a log in system that leaves data in the session. Let’s see how other pages use that data for security.
We want to restrict access to the administration section of a Web site. We have some log in code that checks for a valid user name and password. If it finds one, it puts data into the session.
Let’s see how we can use that session data to restrict access to pages.
Learn:
require statement to insert it.Here’s Louise’s workflow again:

Figure 1. Louise’s workflow
The figure omits pages Louise doesn’t see, like check-log-in.php.
We saw the log in process in the previous lesson. It stores session data about the user, like this:
$_SESSION['logged in'] = 'y'; $_SESSION['user name'] = $user_name; ... $_SESSION['permission add'] = $row['permission_add']; $_SESSION['permission edit'] = $row['permission_edit']; $_SESSION['permission delete'] = $row['permission_delete'];
Figure 2. Storing log in data into the session
Pages like index.php, add-product.php, and edit-product.php will use the session data to check:
Let’s see how that works.
During log in, check-log-in.php does this if the user gives a valid user name and password:
$_SESSION['logged in'] = 'y';
Other pages, like add-product.php, can use this to check whether the user is logged in. Here’s some code.
<?php
//Start session mechanism.
session_start();
//User logged in?
if ( $_SESSION['logged in'] != 'y' ) {
//No - jump to log in page.
header("location:$path_to_root/admin/log-in.php");
exit();
}
?>
Figure 3. Log in check
Line 3 starts the session. Line 5 checks the variable $_SESSION['logged in']. Recall that != means “not equal to.” So if $_SESSION['logged in'] has anything other than y, the browser is sent back to the log in page.
W00f!
This check needs to be done on every page of the site’s admin section. We could copy-and-paste the code everywhere, but there’s a better way. We’ll put the code in Figure 3 in the file library/restrict.php. Then we’ll use the require statement to insert it into all the admin pages. Here’s how add-product.php starts:
<?php //Get data from user for a new product. //Input: // None. //Path from this page to the site root. $path_to_root = '..'; //Security check require $path_to_root . '/library/restrict.php';
Figure 4. Code from add-product.php
Line 9 loads the file. That’s all add-product.php needs to do.
We can make an admin page check whether a user is logged in. But what about checking individual permissions?
There are two parts to this:
Remember this, from check-log-in.php:
$_SESSION['permission add'] = $row['permission_add'];
Admin pages can check $_SESSION['permission add'], to see whether the user is allowed to add products.
Here’s code from add-products.php:
<?php
...
//Security check
require $path_to_root . '/library/restrict.php';
if ( $_SESSION['permission add'] != 'y' ) {
//Exit if user doesn't have add permission.
header("location:$path_to_root/admin/index.php");
exit();
}
Figure 5. More code from add-product.php
Line 4 checks whether the user is logged in, as we saw above.
Line 5 checks whether the user has permission to add products. If not, the browser is sent to the admin menu.
Here’s code from delete-product.php.
<?php
...
//Security check
require $path_to_root . '/library/restrict.php';
if ( $_SESSION['permission delete'] != 'y' ) {
//Exit if user doesn't have delete permission.
header("location:$path_to_root/admin/index.php");
exit();
}
Figure 6. Code from delete-product.php
It’s the same, except that it checks a different permission.
The admin interface should change, depending on what permissions the user has. Here’s Kieran’s admin menu:

Figure 7. Kieran’s admin menu
Because Kieran has permission to add, edit, and delete products, links for all of those tasks appear in the menu.
Here’s Renata’s admin menu:

Figure 8. Renata’s admin menu
Renata does not have permission to add or delete products. Only to edit products. So only edit links show up in the interface.
How to make this happen? Let’s start with the “Add a new product” link. We want that to appear only for users who have permission to add new products. Here’s code from the admin menu:
<p>User: <?php print $_SESSION['user name']; ?></p>
<p>What do you want to do?</p>
<ul>
<li><a href="log-out.php">Log out</a></li>
<?php
if ( $_SESSION['permission add'] == 'y' ) {
?>
<li><a href="add-product.php">Add a new product</a></li>
<?php
}
?>
Figure 9. Code from admin/index.php
Line 1 shows the user name. You can see it on the page:

Figure 7 (again). Kieran’s admin menu
Line 4 shows a link to the log out page. We’ll look at that in the next lesson.
The next lines are:
if ( $_SESSION['permission add'] == 'y' ) {
?>
<li><a href="add-product.php">Add a new product</a></li>
<?php
}
The link to add-product.php is output to the page only if the user has permission to add pages.
What about the edit and delete links?

Figure 7 (again). Kieran’s admin menu
They’re done the same way.
print "
...
<td>
";
if ( $_SESSION['permission edit'] == 'y' ) {
print "<a href='edit-product.php?id=$product_id'>Edit</a><br>";
}
if ( $_SESSION['permission delete'] == 'y' ) {
print "<a href='confirm-delete-product.php?id=$product_id'>Delete</a>";
}
print "
</td>
...";
Figure 10. More code from admin/index.php
The code that outputs the links is wrapped in if statements that check permissions.
require statement to insert it.You know how to make log in pages store security data in the session. You know how to use that data to restrict what users can do.
Now let’s see how users can log out.
This chapter is about restricting access to the admin section of a Web site.
You know how to add a log in page, store log in data in sessions, and check it in every admin page.
The only thing left to do is let the user log out.
Learn:
The admin menu (admin/index.php) has a log out link. The user clicks it to log out:

Figure 1. Log out link
Here’s the HTML that makes the link:
<a href="log-out.php">Log out</a>
Just a simple link. But that does the log out page do?
The code we wrote to restrict access uses session data. The log out code gets rid of it.
Here’s the code for log-out.php.
<?php
//Log out
//Path from this page to the site root.
$path_to_root = '..';
session_start();
//Kill all the the session variables.
$_SESSION = array();
//Kill the session itself.
session_destroy();
//Back to the log in page.
header("location:$path_to_root/admin/log-in.php");
exit();
?>
Figure 2. log-out.php
Line 7 erases the session data, including the log in flag and all permissions information. Line 9 destroys the session itself. Line 11 jumps back to the log in page.
That’s it!
W00f!
The erases all of the session data, right?
Yes.
Is there session data you might not want to erase?
Ooo, good question!
You can use the session to store data about anything. Like the winner of the Tokyo dog show.
In the code we’ve been looking at, we only used the session to store information about log in and permission. So erasing it all makes sense.
But if you’re using the session to store other information as well, you might want to just erase the log in and permission stuff, like this:
$_SESSION['logged in'] = '';
$_SESSION['permission add'] = '';
$_SESSION['permission edit'] = '';
$_SESSION['permission delete'] = '';
Now for some exercises.