Databases are central to many web applications. A database can hold almost any collection of information you may want to search and update, such as a user list, a product catalog, or recent headlines. One reason why PHP is such a great web programming language is its extensive database support. PHP can interact with (at last count) more than 20 different databases, some relational and some not. The relational databases it can talk to are Apache Derby, DB++, FrontBase, IBM Cloudscape, IBM DB2, Informix, Interbase, Ingres II, Microsoft SQL Server, mSQL, MySQL, MySQL MaxDB, Oracle, Ovrimos SQL Server, PostgreSQL, SQLite, and Sybase. The nonrelational databases it can talk to are dBase, filePro, HyperWave, Paradox, and the DBM family of flat-file databases. It also has ODBC support, so even if your favorite database isn't in the list, as long as it supports ODBC, you can use it with PHP.
DBM databases, discussed in Recipe 10.1, are simple, robust, and efficient flat files but limit the structure of your data to key/value pairs. If your data can be organized as a mapping of keys to values, DBM databases are a great choice.
PHP really shines, though, when paired with an SQL database. This combination is used for most of the recipes in this chapter. SQL databases can be complicated, but they are extremely powerful. To use PHP with a particular SQL database, PHP must be explicitly told to include support for that database when it is compiled. If PHP is built to support dynamic module loading, the database support can also be built as a dynamic module.
The SQL database examples in this chapter use PHP 5's PDO database access layer. With PDO, you use the same PHP functions no matter what database engine you're talking to. Although the syntax of the SQL may differ from database to database, the PHP code remains similar. In this regard, PDO offers data access abstraction, not total database abstraction. Other PHP libraries, such as PEAR DB (http://pear.php.net/package/db), ADODb (http://adodb.sourceforge.net/), and MDB2 (http://pear.php.net/package/MDB2) attempt to solve the total database abstraction problem'they hide different databases' implementation details such as date handling and column types behind a layer of code. While this sort of abstraction can save you some work if you're writing software that is intended to be used with lots of different types of databases, but it can cause other problems. When you write SQL focused on a particular type of database, you can take advantage of that database's features for maximum performance.
PHP 5 comes bundled with SQLite, a powerful database that doesn't require a separate server. It's a great choice when you have a moderate amount of traffic and don't want to deal with the hassles of running a database server. Recipe 10.2 discusses some of the ins and outs of SQLite. With PHP 4, you can use SQLite via the PECL SQLite extension (http://pecl.php.net/package/SQLite).
Many SQL examples in this chapter use a table of information about Zodiac signs. The table's structure is shown in Example 10-1. The data in the table is shown in Example 10-2.
Sample table structure
Sample table data
Recipes 10.3 through 10.8 cover the basics of connecting to a database server, sending queries and getting the results back, as well as using queries that change the data in the database.
Typical PHP programs capture information from HTML form fields and store that information in the database. Some characters, such as the apostrophe and backslash, have special meaning in SQL, so you have to be careful if your form data contains those characters. PHP has a feature called "magic quotes" that attempts to make this easier. When the configuration setting magic_quotes_gpc is on, variables coming from get requests, post requests, and cookies have single quotes, double quotes, backslashes, and nulls escaped with a backslash. You can also turn on magic_quotes_runtime to automatically escape quotes, backslashes, and nulls from external sources such as database queries or text files. For example, if magic_quotes_runtime is on and you read a file into an array with file( ), the special characters in that array are backslash-escaped.
Unfortunately, "magic quotes" usually turns out to be more like "annoying quotes." If you want to use submitted form data in any other context than just an SQL query (for example, displaying it in a page), then you need to undo the escaping so the page looks right. If you've run into a PHP web site in which backslashes seem to accumulate before single quotes in text fields, the culprit is almost certainly magic quotes. Recipe 10.7 explains PDO's bound parameters support, which is a better way to make sure that special characters in user input are properly escaped when the user input is incorporated into SQL queries. Recipe 10.9 discusses escaping special characters in queries in more detail. General debugging techniques you can use to handle errors resulting from database queries are covered in Recipe 10.10.
The remaining recipes cover database tasks that are more involved than just simple queries. Recipe 10.11 shows how to automatically generate unique ID values you can use as record identifiers. Recipe 10.12 covers building queries at runtime from a list of fields. This makes it easier to manage INSERT and UPDATE queries that involve a lot of columns. Recipe 10.13 demonstrates how to display links that let you page through a result set, displaying a few records on each page. To speed up your database access, you can cache queries and their results, as explained in Recipe 10.14.
Recipe 10.15 shows some techniques for managing access to a single database connection from various places in a large program. Last, Recipe 10.16 ties together some of the topics discussed in the chapter in a complete program that stores a threaded message board in a database.