Article

Build Your Own Database Driven Web Site Using PHP & MySQL, Part 4: Publishing MySQL Data on the Web

Page: 1 2 3 4 Next

Inserting Data into the Database

In this section, I’ll demonstrate how to use the tools at your disposal to enable site visitors to add their own jokes to the database.

If you want to let visitors to your site type in new jokes, you’ll obviously need a form. Here’s a template for a form that will fit the bill:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">  
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">  
 <head>  
   <title>Add Joke</title>  
   <meta http-equiv="content-type"  
       content="text/html; charset=utf-8"/>  
   <style type="text/css">  
   textarea {  
     display: block;  
     width: 100%;  
   }  
   </style>  
 </head>  
 <body>  
   <form action="?" method="post">  
     <div>  
       <label for="joketext">Type your joke here:</label>  
       <textarea id="joketext" name="joketext" rows="3" cols="40"></textarea>  
     </div>  
     <div><input type="submit" value="Add"/></div>  
   </form>  
 </body>  
</html>

As we’ve seen before, when submitted this form will request the same PHP script that generated the form—the controller script (index.php). You’ll notice, however, that instead of leaving the action attribute empty (""), we set its value to ?. As we’ll see in a moment, the URL used to display the form in this example will feature a query string, and setting the action to ? strips that query string off the URL when submitting the form.

The figure below shows what this form looks like in a browser.

Another nugget of comic genius is added to the database

When this form is submitted, the request will include a variable, joketext, that contains the text of the joke as typed into the text area. This variable will then appear in the $_POST and $_REQUEST arrays created by PHP.

Let’s tie this form into the preceding example, which displayed the list of jokes in the database. Add a link to the top of the list that invites the user to add a joke:

<body>  
 <p><a href="?addjoke">Add your own joke</a></p>  
 <p>Here are all the jokes in the database:</p>

Like the form, this link points back to the very same PHP script used to generate this page, but this time it adds a query string (?addjoke), indicating the user’s intention to add a new joke. Our controller can detect this query string and use it as a signal to display the “Add Joke” form instead of the list of jokes.

Let’s make the necessary changes to the controller now:

if (isset($_GET['addjoke']))  
{  
 include 'form.html.php';  
 exit();  
}

This opening if statement checks if the query string contains a variable named addjoke. This is how we detect that the user clicked the new link. Even though there is no value specified by the query string (?addjoke) for the addjoke variable, it does create it, which we can detect with isset($_GET['addjoke']).

When we detect this variable, we display the form by including form.html.php, and then exit.

Once the user fills out the form and submits it, that form submission results in another request to this controller. This we detect by checking if $_POST['joketext'] is set:

if (isset($_POST['joketext']))  
{

To insert the submitted joke into the database, we must run an INSERT query using the value stored in $_POST['joketext'] to fill in the joketext column of the joke table. This might lead you to write some code like this:

$sql = 'INSERT INTO joke SET  
   joketext="' . $_POST['joketext'] . '",  
   jokedate="today’s date"';

There is a serious problem with this code, however: the contents of $_POST['joketext'] are entirely under the control of the user who submitted the form. If a malicious user were to type just the right sort of SQL code into the form, this script would feed it to your MySQL server without question. This type of attack is called an SQL injection attack, and in the early days of PHP it was one of the most common security holes that hackers found and exploited in PHP-based web sites.

These attacks were so feared, in fact, that the team behind PHP added some built-in protection against SQL injections to the language that remains enabled by default in many PHP installations today. Called magic quotes, this protective feature of PHP automatically analyzes all values submitted by the browser and inserts backslashes (\) in front of any dangerous characters, like apostrophes—which can cause problems if they’re included in an SQL query inadvertently.

The problem with the magic quotes feature is that it causes as many problems as it prevents. Firstly, the characters that it detects and the method it uses to sanitize them (prefixing them with a backslash) are only valid in some circumstances. Depending on the character encoding of your site, and the database server you’re using, these measures may be completely ineffective.

Secondly, when a submitted value is used for some purpose other than creating an SQL query, those backslashes can be really bothersome. I mentioned this briefly in Chapter 2: Introducing MySQL when, in the welcome message example, the magic quotes feature would insert a spurious backslash into the user’s last name if it contained an apostrophe.

In short, magic quotes was a bad idea, so much so that it’s scheduled to be removed from PHP in version 6. In the meantime, however, you have to deal with the problems it creates in your code. The easiest way to do this is to detect if magic quotes is enabled on your web server and, if it is, to undo the modifications it has made to the submitted values. (You can disable magic quotes—and save your web server a lot of work—by setting the magic_quotes_gpc option in your php.ini file to Off. To make sure your code still functions if this setting is changed, however, you should still deal with magic quotes in your code when it’s enabled.) Thankfully, the PHP Manual provides a snippet of code that will do exactly this:

if (get_magic_quotes_gpc())  
{  
 function stripslashes_deep($value)  
 {  
   $value = is_array($value) ?  
       array_map('stripslashes_deep', $value) :  
       stripslashes($value);  
 
   return $value;  
 }  
 
 $_POST = array_map('stripslashes_deep', $_POST);  
 $_GET = array_map('stripslashes_deep', $_GET);  
 $_COOKIE = array_map('stripslashes_deep', $_COOKIE);  
 $_REQUEST = array_map('stripslashes_deep', $_REQUEST);  
}  

Avoid wasting time trying to understand the inner workings of this code; to keep the code short, it uses several advanced PHP features that we’ve yet to see—and one or two others that are beyond the scope of this book. Rather, just drop this code into the top of your controller—and indeed any other PHP script that will receive user input in the form of query variables or a form submission (or, as we’ll learn in Chapter 9, Cookies, Sessions, and Access Control, browser cookies). And be assured; from this point forward, I’ll remind you whenever this code is required by an example. (In Chapter 6: Structured PHP Programming I’ll show you how to manage the burden of repeatedly including this code snippet in your controller code.)

With the damage done by magic quotes reversed, you must now prepare those values that you do intend to use in your SQL query. Just as it provides htmlspecialchars for outputting user-submitted values into HTML code, PHP provides a function that prepares a user-submitted value so that you can use it safely in your SQL query: mysqli_real_escape_string. Not the most elegant name, but it does the trick. Here’s how you use it:

$joketext = mysqli_real_escape_string($link, $_POST['joketext']);  
$sql = 'INSERT INTO joke SET  
   joketext="' . $joketext . '",  
   jokedate="today's date"';

This code first uses mysqli_real_escape_string to store a “query safe” version of the contents of $_POST['joketext'] in the new variable $joketext. It then uses this variable to insert the submitted value into the INSERT query as the value of the joketext column.

The lingering question in this code is how to assign today’s date to the jokedate field. We could write some fancy PHP code to generate today’s date in the YYYY-MM-DD form that MySQL requires, but it turns out MySQL itself has a function to do this: CURDATE:

$joketext = mysqli_real_escape_string($link, $_POST['joketext']);  
$sql = 'INSERT INTO joke SET  
   joketext="' . $joketext . '",  
   jokedate=CURDATE()';

The MySQL function CURDATE is used here to assign the current date as the value of the jokedate column. MySQL actually has dozens of these functions, but we’ll introduce them only as required. Appendix B, MySQL Functions provides a reference that describes all commonly used MySQL functions.

Now that we have our query, we can complete the if statement we started above to handle submissions of the “Add Joke” form. We can execute our INSERT query by using the mysqli_query function:

if (isset($_POST['joketext']))  
{  
 $joketext = mysqli_real_escape_string($link, $_POST['joketext']);  
 $sql = 'INSERT INTO joke SET  
     joketext="' . $joketext . '",  
     jokedate=CURDATE()';  
 if (!mysqli_query($link, $sql))  
 {  
   $error = 'Error adding submitted joke: ' . mysqli_error($link);  
   include 'error.html.php';  
   exit();  
 }  
 
 header('Location: .');  
 exit();  
}

But wait! This if statement has one more new trick up its sleeve. Once we’ve added the new joke to the database, instead of displaying the PHP template as previously, we want to redirect the user’s browser back to the list of jokes. That way they are able to see the newly added joke among them. That’s what the two lines highlighted in bold at the end of the if statement above do.

Your first instinct in order to achieve the desired result might be to allow the controller, after adding the new joke to the database, simply to fetch the list of jokes from the database and display the list using the jokes.html.php template as usual. The problem with doing this is that the resulting page, from the browser’s perspective, would be the effect of having submitted the “Add Joke” form. If the user were then to refresh the page, the browser would resubmit that form, causing another copy of the new joke to be added to the database! This is rarely the desired behaviour.

Instead, we want the browser to treat the updated list of jokes as a normal web page, able to be reloaded without resubmitting the form. The way to do this is to answer the browser’s form submission with an HTTP redirect (HTTP stands for HyperText Transfer Protocol, and is the language that describes the request/response communications that are exchanged between the visitor’s web browser and your web server.)—a special response that tells the browser “the page you’re looking for is over here.”

The PHP header function provides the means of sending special server responses like this one, by letting you insert special headers into the response sent to the server. In order to signal a redirect, you must send a Location header with the URL of the page to which you wish to direct the browser:

header('Location: URL');

In this case, we want to send the browser back to the very same page—our controller. We’re asking the browser to submit another request—this time, without a form submission attached to it—rather than sending the browser to another location. Since we want to point the browser at our controller (index.php) using the URL of the parent directory, we can simply tell the browser to reload the current directory, which is expressed as a period (.).

Thus, the two lines that redirect the browser back to our controller after adding the new joke to the database:

 header('Location: .');  
 exit();  
}

$_SERVER['PHP_SELF'] is the URL of the current page

Another common means of obtaining the URL of the current page in PHP is with $_SERVER['PHP_SELF'].

Like $_GET, $_POST, and $_REQUEST, $_SERVER is an array variable that is automatically created by PHP. $_SERVER contains a whole bunch of information supplied by your web server. In particular, $_SERVER['PHP_SELF'] will always be set to the URL of the PHP script that your web server used to generate the current page.

Unfortunately, because the web server automatically translates a request for http://localhost/addjoke/ to a request for http://localhost/addjoke/index.php, $_SERVER['PHP_SELF'] will contain the latter URL. Redirecting the browser to . lets us preserve the shorter, more memorable form of the URL.

For this reason, I have avoided using $_SERVER['PHP_SELF'] in this book. Since it’s so commonly used in basic PHP examples around the Web, however, I thought you might like to know what it does.

The rest of the controller is responsible for displaying the list of jokes as before. Here’s the complete code of the controller:

<?php  
if (get_magic_quotes_gpc())  
{  
 function stripslashes_deep($value)  
 {  
   $value = is_array($value) ?  
       array_map('stripslashes_deep', $value) :  
       stripslashes($value);  
 
   return $value;  
 }  
 
 $_POST = array_map('stripslashes_deep', $_POST);  
 $_GET = array_map('stripslashes_deep', $_GET);  
 $_COOKIE = array_map('stripslashes_deep', $_COOKIE);  
 $_REQUEST = array_map('stripslashes_deep', $_REQUEST);  
}  
 
if (isset($_GET['addjoke']))  
{  
 include 'form.html.php';  
 exit();  
}  
 
$link = mysqli_connect('localhost', 'root', 'password');  
if (!$link)  
{  
 $error = 'Unable to connect to the database server.';  
 include 'error.html.php';  
 exit();  
}  
 
if (!mysqli_set_charset($link, 'utf8'))  
{  
 $output = 'Unable to set database connection encoding.';  
 include 'output.html.php';  
 exit();  
}  
 
if (!mysqli_select_db($link, 'ijdb'))  
{  
 $error = 'Unable to locate the joke database.';  
 include 'error.html.php';  
 exit();  
}  
 
if (isset($_POST['joketext']))  
{  
 $joketext = mysqli_real_escape_string($link, $_POST['joketext']);  
 $sql = 'INSERT INTO joke SET  
     joketext="' . $joketext . '",  
     jokedate=CURDATE()';  
 if (!mysqli_query($link, $sql))  
 {  
   $error = 'Error adding submitted joke: ' . mysqli_error($link);  
   include 'error.html.php';  
   exit();  
 }  
 
 header('Location: .');  
 exit();  
}  
 
$result = mysqli_query($link, 'SELECT joketext FROM joke');  
if (!$result)  
{  
 $error = 'Error fetching jokes: ' . mysqli_error($link);  
 include 'error.html.php';  
 exit();  
}  
 
while ($row = mysqli_fetch_array($result))  
{  
 $jokes[] = $row['joketext'];  
}  
 
include 'jokes.html.php';  
?>

As you review this code to make sure it all makes sense to you, note that the calls to mysqli_connect and mysqli_select_db must come before any of the code that runs database queries. A database connection is unnecessary to display the “Add Joke” form, however, so that code can come at the very top of the controller script.

Load this up and add a new joke or two to the database via your browser. The resulting page should look like the figure below.

Look, Ma! No SQL!

There you have it! With a single controller (index.php) pulling the strings, you’re able to view existing jokes in, and add new jokes to, your MySQL database.

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links