Structured PHP Programming - PHP & MySQL: Novice to Ninja, 5th Edition (2012)

PHP & MySQL: Novice to Ninja, 5th Edition (2012)

Chapter 6. Structured PHP Programming

Before we plow headlong into the next enhancements of our joke database, let’s spend a little time honing your “PHP-fu.” Specifically, I want to show you a few techniques to better structure your code. Structured coding techniques are useful in all but the simplest of PHP projects. Already in Chapter 3, you’ve learned how to split up your PHP code into multiple files: a controller and a set of associated templates. This lets you keep the server-side logic of your site separate from the HTML code used to display the dynamic content generated by that logic. In order to do this, you learned how to use the PHP include command. The PHP language offers many such facilities to help you add structure to your code. The most powerful of these is undoubtedly its support for object oriented programming (OOP), which we touched on briefly in Chapter 4. But there’s no need to learn all the complexities of OOP to build complex (and well-structured) applications with PHP;[37] thankfully, there are also opportunities for structuring your code through the more basic features of PHP. In this chapter, I’ll explore some simple ways to keep your code manageable and maintainable without requiring you to become a total programming wizard (though you might still like to become one later on!).

Include Files

Even very simple PHP-based websites often need the same piece of code in several places. You already learned to use the PHP include command to load a PHP template from inside your controller; it turns out you can use the same feature to save yourself from having to write the same code again and again. Include files (also known as just includes) contain snippets of PHP code that you can then load into your other PHP scripts instead of having to retype them.

Including HTML Content

The concept of include files came long before PHP. If you’re an old codger like me (which, in the Web world, means you’re over 25), you may have experimented with Server-side Includes (SSIs) . A feature of just about every web server out there, SSIs let you put commonly used snippets of HTML (and JavaScript, and CSS) into include files that you can then use in multiple pages. In PHP, include files most commonly contain either pure PHP code or, in the case of PHP templates, a mixture of HTML and PHP code. But you don’t have to put PHP code in your include files. If you like, an include file can contain strictly static HTML. This is most useful for sharing common design elements across your site, such as a copyright notice to appear at the bottom of every page:

chapter6/static-footer/footer.inc.html.php

<div id="footer">

The contents of this web page are copyright © 1998–2012

Example Pty. Ltd. All Rights Reserved.

</div>

This file is a template fragment —an include file to be used by PHP templates. To distinguish this type of file from others in your project, I recommend giving it a name ending with .inc.html.php. You can then use this fragment in any of your PHP templates:

chapter6/static-footer/samplepage.html.php

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>A Sample Page</title>

</head>

<body>

<p id="main">

This page uses a static include to display a standard

copyright notice below.

</p>

<?php include 'footer.inc.html.php'; ?>

</body>

</html>

Finally, here’s the controller that loads this template:

chapter6/static-footer/index.php

<?php

include 'samplepage.html.php';

?>

Figure 6.1 shows what the page looks like in the browser.

A static include displays the site’s copyright notice

Figure 6.1. A static include displays the site’s copyright notice

Now all you need to do to update your copyright notice is to edit footer.inc.html.php. No more time-consuming, error-prone find-and-replace operations! Of course, if you really want to make your life easier, you can just let PHP do the work for you:

chapter6/dynamic-footer/footer.inc.html.php

<p id="footer">

The contents of this web page are copyright ©

1998–<?php echo date('Y'); ?> Example Pty. Ltd.

All Rights Reserved.

</p>

Including PHP Code

On database driven websites, almost every controller script must establish a database connection as its first order of business. As we’ve already seen, the code for doing this is fairly substantial:

try

{

$pdo = new PDO('mysql:host=localhost;dbname=ijdb', 'ijdbuser',

'mypassword');

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$pdo->exec('SET NAMES "utf8"');

}

catch (PDOException $e)

{

$error = 'Unable to connect to the database server.';

include 'error.html.php';

exit();

}

At some 12 lines long, it’s only a slightly cumbersome chunk of code, but having to type it at the top of every controller script can quickly become annoying. Many new PHP developers will often omit essential error checking to save typing (for example, by leaving out the try-catchstatement in this code), which can result in a lot of lost time looking for the cause when an error does occur. Others will make heavy use of the clipboard to copy pieces of code like this from existing scripts for use in new ones. Some even use features of their text editor software to store useful pieces of code as snippets for frequent use. But what happens when the database password or some other detail of the code changes? Suddenly you’re on a treasure hunt to find every occurrence of the code in your site to make the necessary change—a task that can be especially frustrating if you’ve used several variations of the code that you need to track down and update. Figure 6.2 illustrates how include files can help in this situation. Instead of repeating the code fragment in every file that needs it, write it just once in a separate file—known as the include file. That file can then be included in any other PHP files that need to use it.

Include files allow several scripts to share common code

Figure 6.2. Include files allow several scripts to share common code

Let’s apply this technique to create the database connection in our joke list example to see how it works in detail. First, create a file called db.inc.php [38] and place the database connection code inside it:

chapter6/jokes/db.inc.php

<?php

try

{

$pdo = new PDO('mysql:host=localhost;dbname=ijdb', 'ijdbuser',

'mypassword');

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$pdo->exec('SET NAMES "utf8"');

}

catch (PDOException $e)

{

$error = 'Unable to connect to the database server.';

include 'error.html.php';

exit();

}

As you can see, include files are just like normal PHP files, but typically they contain snippets of code that are only useful within the context of a larger script. Now you can put this db.inc.php file to use in your controller:

chapter6/jokes/index.php

<?php

if (get_magic_quotes_gpc())

{

$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);

while (list($key, $val) = each($process))

{

foreach ($val as $k => $v)

{

unset($process[$key][$k]);

if (is_array($v))

{

$process[$key][stripslashes($k)] = $v;

$process[] = &$process[$key][stripslashes($k)];

}

else

{

$process[$key][stripslashes($k)] = stripslashes($v);

}

}

}

unset($process);

}

if (isset($_GET['addjoke']))

{

include 'form.html.php';

exit();

}

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

{

include 'db.inc.php';

try

{

$sql = 'INSERT INTO joke SET

joketext = :joketext,

jokedate = CURDATE()';

$s = $pdo->prepare($sql);

$s->bindValue(':joketext', $_POST['joketext']);

$s->execute();

}

catch (PDOException $e)

{

$error = 'Error adding submitted joke: ' . $e->getMessage();

include 'error.html.php';

exit();

}

header('Location: .');

exit();

}

if (isset($_GET['deletejoke']))

{

include 'db.inc.php';

try

{

$sql = 'DELETE FROM joke WHERE id = :id';

$s = $pdo->prepare($sql);

$s->bindValue(':id', $_POST['id']);

$s->execute();

}

catch (PDOException $e)

{

$error = 'Error deleting joke: ' . $e->getMessage();

include 'error.html.php';

exit();

}

header('Location: .');

exit();

}

include 'db.inc.php';

try

{

$sql = 'SELECT joke.id, joketext, name, email

FROM joke INNER JOIN author

ON authorid = author.id';

$result = $pdo->query($sql);

}

catch (PDOException $e)

{

$error = 'Error fetching jokes: ' . $e->getMessage();

include 'error.html.php';

exit();

}

foreach ($result as $row)

{

$jokes[] = array(

'id' => $row['id'],

'text' => $row['joketext'],

'name' => $row['name'],

'email' => $row['email']

);

}

include 'jokes.html.php';

As you can see, wherever our controller needs a database connection, we can obtain it simply by including the db.inc.php file with an include statement. And because the code to do this is a simple one-liner, we can make our code more readable by using a separate include statement just before each SQL query in our controller. Previously, we established a database connection at the top of the controller, regardless of whether the code that followed would end up needing one or not. When PHP encounters an include statement, it puts the current script on hold and runs the specified PHP script. When it’s finished, it returns to the original script and picks up where it left off. Include files are the simplest way to structure PHP code. Because of their simplicity, they’re also the most widely used method. Even very simple web applications can benefit greatly from using include files.

Types of Includes

The include statement we’ve used so far is actually only one of four statements that can be used to include another PHP file in a currently running script:

· include

· require

· include_once

· require_once

include and require are almost identical. The only difference between them is what happens when the specified file is unable to be included (that is, if it doesn’t exist, or if the web server doesn’t have permission to read it). With include, a warning is displayed and the script continues to run. With require, an error is displayed and the script stops.[39] In general, you should use require whenever the main script is unable to work without the included script. I do recommend using include whenever possible, however. Even if the db.inc.php file for your site is unable to load, for example, you might still want to let the script for your front page continue to load. None of the content from the database will display, but the user might be able to use the Contact Us link at the bottom of the page to let you know about the problem! include_once and require_once work just like include and require, respectively—but if the specified file has already been included at least once for the current page request (using any of the four statements described here), the statement will be ignored. This is handy for include files performing a task that only needs to be done once, like connecting to the database. Figure 6.3 shows include_once in action. In the figure, index.php includes two files: categories.inc.php and top10.inc.php. Both files use include_once to include db.inc.php, as they both need a database connection in order to do their job. As shown, PHP will ignore the attempt to include db.inc.php in top10.inc.php because the file was already included in categories.inc.php. As a result, only one database connection is created.

Use include_once to avoid opening a second database connection

Figure 6.3. Use include_once to avoid opening a second database connection

include_once and require_once are also useful for loading function libraries, as we’ll see in the section called “Custom Functions and Function Libraries”.

Shared Include Files

In all the examples I’ve shown you so far, I’ve assumed that the include file is located in the same directory on your web server as the file(s) that use it. Often, this is an invalid assumption! On many sites, you’ll want to share include files among scripts that span potentially complex directory structures. A solid candidate for a shared include file would be the database connection include, db.inc.php. So the question is, when the include file is in a different directory, how does a PHP script find it? The most obvious method is to specify the location of the include file as an absolute path . Here’s how this would look on a Windows server:[40]

<?php include 'C:/Program Files/Apache Software Foundation/Apache2.2

↵/htdocs/includes/db.inc.php'; ?>

And here’s the code on a Linux server:

<?php include '/usr/local/apache2/htdocs/includes/db.inc.php'; ?>

While this method will work, it’s undesirable because it ties your site’s code to your web server configuration. Ideally, you should be able to drop your PHP-based website onto any PHP-enabled web server and just watch it run. This is particularly important because many developers will build a site on one server, then deploy it publicly on a different server. That’s impractical if your code refers to drives and directories that are specific to one particular server. And, even if you do have the luxury of working on a single server, you’ll be kicking yourself if you ever need to move your website to another drive/directory on that server. A better method is to let PHP keep track of the document root of your web server, then specify the path from that location. The document root is the directory on your server that corresponds to the root directory of your website. For example, to make index.php available at http://www.example.com/index.php, you’d have to place it in the document root directory on the www.example.com web server. In any PHP script, you can obtain the document root of your web server using $_SERVER ['DOCUMENT_ROOT']. As I briefly explained in Chapter 4, $_SERVER is an array variable that’s automatically created by PHP, just like $_GET, $_POST, and $_REQUEST. $_SERVER contains a whole bunch of information supplied by your web server, including $_SERVER['DOCUMENT_ROOT']. Here’s an example:

<?php include $_SERVER['DOCUMENT_ROOT'] . '/includes/db.inc.php'; ?>

This will work on Windows, Mac, and Linux servers with either Apache or Internet Information Services (IIS) installed.[41] Another excellent candidate for a shared include file is the snippet of code that we used to reverse the changes to submitted values made by PHP’s misguided magic quotes feature, which we looked at in Chapter 4. Simply drop this code into its own file:

chapter6/includes/magicquotes.inc.php

<?php

if (get_magic_quotes_gpc())

{

$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);

while (list($key, $val) = each($process))

{

foreach ($val as $k => $v)

{

unset($process[$key][$k]);

if (is_array($v))

{

$process[$key][stripslashes($k)] = $v;

$process[] = &$process[$key][stripslashes($k)];

}

else

{

$process[$key][stripslashes($k)] = stripslashes($v);

}

}

}

unset($process);

}

From this point on, you can use this include file to remove the effects of magic quotes with a single line at the top of your controller scripts:

<?php

include $_SERVER['DOCUMENT_ROOT'] . '/includes/magicquotes.inc.php';

I’ll use the two shared include files discussed in this section—the database connection script and the magic quotes removal script—in many of the examples from this point forward in the book. You’ll be able to follow along too, as long as the two files in question (db.inc.php andmagicquotes.inc.php) can be found in a directory called includes situated in the document root directory of your web server.

Custom Functions and Function Libraries

By this point, you’re probably quite comfortable with the idea of functions. A function in PHP that you can invoke at will, where you’d usually provide one or more arguments for it to use, and often receiving a return value as a result. You can use PHP’s vast library of functions to do just about anything a PHP script could ever be asked to do, from retrieving the current date (date) to generating graphics on the fly ( imagecreatetruecolor ). But what you may be unaware of is that you can create functions of your own! Custom functions, once defined, work just like PHP’s built-in functions, and they can do anything a normal PHP script can do. Let’s start with a really simple example. Say you had a PHP script that needed to calculate the area of a rectangle given its width (3) and height (5). Thinking back to your basic geometry classes in school, you should recall that the area of a rectangle is its width multiplied by its height:

$area = 3 * 5;

But it would be nicer to have a function called area that simply calculated the area of a rectangle given its dimensions:

chapter6/calculate-area/index.php (excerpt)

$area = area(3, 5);

As it happens, PHP has no built-in area function, but clever PHP programmers like you and me can just roll up our sleeves and write the function ourselves:

chapter6/calculate-area/area-function.inc.php

<?php

function area($width, $height)

{

return $width * $height;

}

This include file defines a single custom function: area. The <?php marker is probably the only line that looks familiar to you in this code. What we have here is a function declaration ; let me break it down for you one line at a time:

function area($width, $height)

The keyword function tells PHP that we wish to declare a new function for use in the current script. Then, we supply the function with a name (in this case, area). Function names operate under the same rules as variable names—they are case-sensitive, must start with a letter or an underscore (_), and may contain letters, numbers, and underscores—except, of course, that there’s no dollar sign prefix. Instead, function names are always followed by a set of parentheses ((…)), which may or may not be empty. The parentheses that follow a function name enclose the list of arguments that the function will accept. You should already be familiar with this from your experience with PHP’s built-in functions. For example, when you use date to retrieve the current date as a PHP string, you provide a string describing the format you want the date to be written in within the parentheses. When declaring a custom function, instead of giving a list of values for the arguments, you give a list of variable names. In this example, we list two variables: $width and $height. When the function is called, it will therefore expect to be given two arguments. The value of the first argument will be assigned to $width, while the value of the second will be assigned to $height. Those variables can then be used to perform the calculation within the function.

{

Speaking of calculations, the rest of the function declaration is the code that performs the calculation, or does whatever else the function is supposed to do. That code must be enclosed in a set of braces ({…}), so here’s the opening brace.

return $width * $height;

You can think of the code within those braces as a miniature PHP script. This function is a simple one, because it contains just a single statement: a return statement. A return statement can be used in the code of a function to jump back into the main script immediately. When the PHP interpreter hits a return statement, it immediately stops running the code of this function and goes back to where the function was called. It’s sort of an ejection seat for functions! In addition to breaking out of the function, the return statement lets you specify a value for the function toreturn to the code that called it. In this case, the value we’re returning is $width * $height—the result of multiplying the first parameter by the second.

}

The closing brace marks the end of the function declaration.

In order to use this function, we must first include the file containing the function declaration:

chapter6/calculate-area/index.php

<?php

include_once 'area-function.inc.php';

$area = area(3, 5);

include 'output.html.php';

Technically, you could write the function declaration within the controller script itself, but by putting it in an include file you can reuse the function in other scripts much more easily. It’s tidier, too. To use the function in the include file, a PHP script need only include it with include_once (orrequire_once if the function is critical to the script). Avoid using include or require to load include files that contain functions; as explained in the section called “Types of Includes”, that would risk defining the functions in the library more than once and covering the user’s screen with PHP warnings. It’s standard practice (but not required) to include your function libraries at the top of the script, so that you can quickly see which include files containing functions are used by any particular script. What we have here are the beginnings of a function library—an include file that contains declarations for a group of related functions. If you wanted to, you could rename the include file to geometry.inc.php and add to it a whole bunch of functions to perform various geometrical calculations.

Variable Scope and Global Access

One big difference between custom functions and include files is the concept of variable scope. Any variable that exists in the main script will also be available and can be changed in the include file. While this is useful sometimes, more often it’s a pain in the neck. Unintentionally overwriting one of the main script’s variables in an include file is a common cause of error—and one that can take a long time to track down and fix! To avoid such problems, you need to remember the variable names in the script that you’re working on, as well as any that exist in the include files your script uses. Functions protect you from such problems. Variables created inside a function (including any argument variables) exist only within that function, and disappear when the function has run its course. In programmer-speak, the scope of these variables is the function; they’re said to have function scope . In contrast, variables created in the main script outside of any function are unavailable inside functions. The scope of these variables is the main script, and they’re said to have global scope . Okay, but beyond the fancy names, what does this really meanfor us? It means that you can have a variable called, say, $width in your main script, and another variable called $width in your function, and PHP will treat these as two entirely separate variables! Perhaps more usefully, you can have two different functions each using the same variable names, and they’ll have no effect on each other because their variables are kept separate by their scope. On some occasions, you may actually want to use a global-scope variable (global variable for short) inside one of your functions. For example, the db.inc.php file creates a database connection for use by your script and stores it in the global variable $pdo. You might then want to use this variable in a function that needed to access the database. Disregarding variable scope, here’s how you may write such a function:

<?php

include_once $_SERVER['DOCUMENT_ROOT'] . '/includes/db.inc.php';

function totalJokes()

{

try

{

$result = $pdo->query('SELECT COUNT(*) FROM joke');

}

catch (PDOException $e)

{

$error = 'Database error counting jokes!';

include 'error.html.php';

exit();

}

$row = $result->fetch();

return $row[0];

}

Note: Shared Database Include in Use!

Note that the first line of this controller script uses a shared copy of the db.inc.php file in the includes directory, as discussed earlier in the section called “Shared Include Files”. Make sure you’ve placed a copy of this file (and the associated error.html.php file that it uses to display errors) in the includes directory in your server’s document root; otherwise, PHP will complain that it’s unable to find the db.inc.php file.

The problem here is that the global variable $pdo (shown in bold) is unavailable within the scope of the function. If you attempt to call this function as it is, you’ll receive the errors shown in Figure 6.4.

The totaljokes function cannot access $pdo

Figure 6.4. The totaljokes function cannot access $pdo

Now, of course, you could just add an argument to the totaljokes function and send it the value of $pdo that way, but having to pass this value to every function that needs database access would become quite tedious. Instead, let’s use the global variable directly within our function. There are two ways to do this. The first is to import the global variable into the function’s scope:

chapter6/totaljokes-global1/totaljokes-function.inc.php

<?php

include_once $_SERVER['DOCUMENT_ROOT'] . '/includes/db.inc.php';

function totalJokes()

{

global $pdo;

try

{

$result = $pdo->query('SELECT COUNT(*) FROM joke');

}

catch (PDOException $e)

{

$error = 'Database error counting jokes!';

include 'error.html.php';

exit();

}

$row = $result->fetch();

return $row[0];

}

The global statement, shown here in bold, lets you give a list of global variables (separated by commas, if you want to import more than one) that you want to make available within the function. Programmers call this importing a variable. This is different from passing the variable as an argument, because if you modify an imported variable inside the function, the value of the variable changes outside the function, too. The alternative to importing the variable is to use the $GLOBALS array:

chapter6/totaljokes-global2/totaljokes-function.inc.php

<?php

include_once $_SERVER['DOCUMENT_ROOT'] . '/includes/db.inc.php';

function totalJokes()

{

try

{

$result = $GLOBALS['pdo']->query('SELECT COUNT(*) FROM joke');

}

catch (PDOException $e)

{

$error = 'Database error counting jokes!';

include 'error.html.php';

exit();

}

$row = $result->fetch();

return $row[0];

}

As you can see, all we’ve done here is replace $pdo with $GLOBALS['pdo']. The special PHP array $GLOBALS is available across all scopes (for this reason, it’s known as a superglobal ), and contains an entry for every variable in the global scope. You can therefore access any global variable within a function as $GLOBALS['name'], where name is the name of the global variable (without a dollar sign). The advantage of using $GLOBALS is that you can still create a separate function-scope variable called $pdo if you want. Other special PHP arrays that are superglobal, and are therefore accessible inside functions, include $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES, $_ENV, $_REQUEST, and $_SESSION. See the page on superglobals in the PHP Manual for full details.

Structure in Practice: Template Helpers

To cap this chapter off, let’s make a start on a function library you can actually use. There are few functions more tedious to call in the PHP language than htmlspecialchars. As I explained in Chapter 3, every time you wish to output some piece of text that was submitted by a user, you need to use htmlspecialchars to prevent hackers from inserting malicious code into your page. For example, this is the code we’ve used to output user-submitted jokes in our joke list examples so far:

chapter6/jokes/jokes.html.php (excerpt)

<?php echo htmlspecialchars($joke['text'], ENT_QUOTES, 'UTF-8'); ?>

As well as htmlspecialchars being an inordinately long function name, it takes three arguments—two of which are always the same! Because outputting text as HTML is such a common task in PHP template code, let’s write a much shorter function that does this for us:

chapter6/includes/helpers.inc.php (excerpt)

<?php

function html($text)

{

return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');

}

With this custom html function, we can call htmlspecialchars with a lot less typing!

<?php echo html($joke['text']); ?>

We can take this even further by writing a second custom function, htmlout, that takes the value generated by the first and outputs it:

chapter6/includes/helpers.inc.php

<?php

function html($text)

{

return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');

}

function htmlout($text)

{

echo html($text);

}

I like to name these little convenience functions that make writing templates easier template helpers . Here’s what our joke listing template looks like when we use these helpers:

chapter6/jokes-helpers/jokes.html.php

<?php include_once $_SERVER['DOCUMENT_ROOT'] .

'/includes/helpers.inc.php'; ?>

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>List of Jokes</title>

</head>

<body>

<p><a href="?addjoke">Add your own joke</a></p>

<p>Here are all the jokes in the database:</p>

<?php foreach ($jokes as $joke): ?>

<form action="?deletejoke" method="post">

<blockquote>

<p>

<?php htmlout($joke['text']); ?>

<input type="hidden" name="id" value="<?php echo

$joke['id']; ?>">

<input type="submit" value="Delete">

(by <a href="mailto:<?php htmlout($joke['email']); ?>">

<?php htmlout($joke['name']); ?></a>)

</p>

</blockquote>

</form>

<?php endforeach; ?>

</body>

</html>

Important: Helpers Belong in the Shared includes Directory

Like db.inc.php and magicquotes.inc.php, the helpers.inc.php file belongs in the shared includes directory under your server’s document root, as described in the section called “Shared Include Files”.

As you write templates with more and more user-submitted content in them, these little gems will come in very handy indeed! While you’re at it, update the controller script to use the shared includes db.inc.php and magicquotes.inc.php:

chapter6/jokes-helpers/index.php

<?php

include_once $_SERVER['DOCUMENT_ROOT'] .

'/includes/magicquotes.inc.php';

if (isset($_GET['addjoke']))

{

include 'form.html.php';

exit();

}

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

{

include $_SERVER['DOCUMENT_ROOT'] . '/includes/db.inc.php';

try

{

$sql = 'INSERT INTO joke SET

joketext = :joketext,

jokedate = CURDATE()';

$s = $pdo->prepare($sql);

$s->bindValue(':joketext', $_POST['joketext']);

$s->execute();

}

catch (PDOException $e)

{

$error = 'Error adding submitted joke: ' . $e->getMessage();

include 'error.html.php';

exit();

}

header('Location: .');

exit();

}

if (isset($_GET['deletejoke']))

{

include $_SERVER['DOCUMENT_ROOT'] . '/includes/db.inc.php';

try

{

$sql = 'DELETE FROM joke WHERE id = :id';

$s = $pdo->prepare($sql);

$s->bindValue(':id', $_POST['id']);

$s->execute();

}

catch (PDOException $e)

{

$error = 'Error deleting joke: ' . $e->getMessage();

include 'error.html.php';

exit();

}

header('Location: .');

exit();

}

include $_SERVER['DOCUMENT_ROOT'] . '/includes/db.inc.php';

try

{

$sql = 'SELECT joke.id, joketext, name, email

FROM joke INNER JOIN author

ON authorid = author.id';

$result = $pdo->query($sql);

}

catch (PDOException $e)

{

$error = 'Error fetching jokes: ' . $e->getMessage();

include 'error.html.php';

exit();

}

foreach ($result as $row)

{

$jokes[] = array(

'id' => $row['id'],

'text' => $row['joketext'],

'name' => $row['name'],

'email' => $row['email']

);

}

include 'jokes.html.php';

The Best Way

In this chapter, I have helped you to rise above the basic questions of what PHP can do for you, and begin to look for the best way to code a solution. Sure, you can approach many simple scripts as lists of actions you want PHP to do for you, but when you tackle site-wide issues such as database connections, shared navigation elements, visitor statistics, and access control systems, it really pays off to structure your code carefully. We’ve now explored a couple of simple but effective devices for writing structured PHP code. Include files let you reuse a single piece of code across multiple pages of your site, greatly reducing the burden when you need to make changes. Writing your own functions to put in these include files lets you build powerful libraries of functions that can perform tasks as needed and return values to the scripts that call them. These new techniques will pay off in a big way in the rest of this book. If you want to take the next step into structuring your PHP code, you’ll want to explore PHP’s object oriented programming (OOP) features. The section on Classes and Objects in The PHP Manual has some useful information on the subject, but for a more complete guide you’ll want to check out PHP Master: Write Cutting-edge Code. In Chapter 7, you’ll use all the knowledge you’ve gained so far, plus a few new tricks, to build a content management system in PHP. The aim of such a system is to provide a customized, secure, web-based interface that enables you to manage the contents of your site’s database, instead of requiring you to type everything into phpMyAdmin by hand.


[37] Indeed, possibly the most-used PHP application today, WordPress, is not written in the OOP style.

[38] The current convention of naming include files with a .inc.php extension allows you to easily identify them among ordinary PHP scripts, while at the same time ensuring that they’re identified and processed as PHP scripts by the web server and the development tools you use. In practice, though, you can name include files however you like. Previously, it was common to simply give include files an .inc extension, but unless the web server was specifically configured to process such files as PHP scripts or protect them from being downloaded, there was a security risk: users who guessed the names of your include files could download them as plain text and gain access to sensitive information (such as database passwords) that appeared in the source code.

[39] In production environments, warnings and errors are usually disabled in php.ini. In such environments, a failed include has no visible effect (aside from the lack of content that would normally have been generated by the include file), while a failed require causes the page to stop at the point of failure. When a failed require occurs before any content is sent to the browser, the unlucky user will see nothing but a blank page!

[40] I recommend always using forward slashes in your paths, even when you’re working with a Windows server. PHP is smart enough to do the conversion for you, and using forward slashes saves you from having to type double-backslashes (\\) to represent single backslashes in PHP strings.

[41] The one place where you’re unable to count on $_SERVER['DOCUMENT_ROOT'] is on a server running the Common Gateway Interface (CGI) version of PHP. The CGI specification does not require the web server to inform PHP of the document root directory for the site, so this value will usually be absent on such configurations. Thankfully, CGI installations of PHP are increasingly rare, and should certainly be avoided in production environments. If you followed the installation instructions for PHP in this book, you can rest assured that $_SERVER['DOCUMENT_ROOT'] will work.