227 Checking and Filtering Input Data Figure 10.2 The results of searching the database for books about Java are presented in a Web page using the results.php script. The Basic Steps in Querying a Database from the Web In any script used to access a database from the Web, you will follow some basic steps: 1. Check and filter data coming from the user. 2. Set up a connection to the appropriate database. 3. Query the database. 4. Retrieve the results. 5. Present the results back to the user. These are the steps we have followed in the script results.php, and we will go through each of them in turn. Checking and Filtering Input Data We begin our script by stripping any whitespace that the user might have inadvertently entered at the beginning or end of his search term.We do this by applying the function trim() to $searchterm. $searchterm=trim($searchterm); Our next step is to verify that the user has entered a search term and search type. Note that we check he entered a search term after trimming whitespace from the ends of $searchterm.Had we arranged these lines in the opposite order, we could get 13 525x ch10 1/24/03 3:37 PM Page 227 228 Chapter 10 Accessing Your MySQL Database from the Web with PHP situations where a user’s search term was not empty, so it did not create an error mes- sage, but it was all whitespace, so it was deleted by trim(): if (!$searchtype || !$searchterm) { echo 'You have not entered search details. Please go back and try again.'; exit; } Yo u will notice that we’ve checked the $searchtype variable even though in this case it’s coming from an HTML SELECT.You might ask why we bother checking data that has to be filled in. It’s important to remember that there might be more than one inter- face to your database. For example,Amazon has many affiliates who use their search interface. Also, it’s sensible to screen data in case of any security problems that can arise because of users coming from different points of entry. Also, when you are going to use any data input by a user, it is important to filter it appropriately for any control characters. As you might remember, in Chapter 4,“String Manipulation and Regular Expressions,” we talked about the functions addslashes() and stripslashes().You need to use addslashes() when submitting any user input to a database such as MySQL and stripslashes() when returning output to the user who has had control characters slashed out. In this case we have used addslashes() on the search terms: $searchterm = addslashes($searchterm); We have also used stripslashes() on the data coming back from the database. None of the data we have entered by hand into the database has any slashes in it—however, it also doesn’t have any control characters in it.The call to stripslashes() will have no effect. As we build a Web interface for the database, chances are we will want to enter new books in it, and some of the details entered by a user might contain these charac- ters.When we put them into the database, we will call addslashes(), which means that we must call stripslashes() when taking the data back out.This is a sensible habit to get into. We are using the function htmlspecialchars() to encode characters that have spe- cial meanings in HTML. Our current test data does not include any ampersands (&), less than (<), greater than (>), or double quote (“) symbols, but many fine book titles con- tain an ampersand. By using this function, we can eliminate future errors. Setting Up a Connection We use this line in our script to connect to the MySQL server: @ $db = mysql_pconnect('localhost', 'bookorama', 'bookorama123'); We have used the mysql_pconnect() function to connect to the database.This function has the following prototype: 13 525x ch10 1/24/03 3:37 PM Page 228 229 Setting Up a Connection resource mysql_pconnect( [string host [:port] [:/socketpath] [, string user [, string password]]] ); Generally speaking, you will pass it the name of the host on which the MySQL server is running, the username to login as, and the password of that user.All of these are option- al, and if you don’t specify them, the function uses some sensible defaults—localhost for the host, the username that the PHP process runs as, and a blank password. The function returns a link identifier to your MySQL database on success (which you ought to store for further use) or false on failure.The result is worth checking as none of the rest of code will work without a valid database connection.We have done this using the following code: if (!$db) { echo 'Error: Could not connect to database. Please try again later.'; exit; } An alternative function that does almost the same thing as mysql_pconnect() is mysql_connect().The difference is that mysql_pconnect() returns a persistent connec- tion to the database. A normal connection to the database will be closed when a script finishes execution, or when the script calls the mysql_close() function. A persistent connection remains open after the script finishes execution and cannot be closed with the mysql_close() function. You might wonder why we would want to do this.The answer is that making a con- nection to a database involves a certain amount of overhead and therefore takes some time.When mysql_pconnect() is called, before it tries to connect to the database, it will automatically check if there is a persistent connection already open. If so, it will use this one rather than opening a new one.This saves time and server overhead. It is also worth noting that persistent connections don’t persist if you are running PHP as a CGI. (Each call to a PHP script starts a new instance of PHP and closes it when the script finishes execution.This also closes any persistent connections.) Bear in mind that there is a limit to the number of MySQL connections that can exist at the same time.The MySQL parameter max_connections determines what this limit is.The purpose of this parameter and the related Apache parameter MaxClients is to tell the server to reject new connection requests rather than allowing machine resources to be all used at busy times or when software has crashed. You can alter both of these parameters from their default values by editing the con- figuration files.To set MaxClients in Apache, edit the httpd.conf file on your system.To set max_connections for MySQL, edit the file my.conf. If you use persistent connections and nearly every page in your site involves database access, you are likely to have a persistent connection open for each Apache process.This can cause a problem if you leave these parameters set to their default values. By default, Apache allows 150 connections, but MySQL only allows 100.At busy times, there might 13 525x ch10 1/24/03 3:37 PM Page 229 230 Chapter 10 Accessing Your MySQL Database from the Web with PHP not be enough connections to go around. Depending on the capabilities of your hard- ware,you should adjust these so that each Web server process can have a connection. Choosing a Database to Use Yo uwill remember that when we are using MySQL from a command line interface, we need to tell it which database we plan to use with a command such as use books; We also need to do this when connecting from the Web.We perform this from PHP with a call to the mysql_select_db() function, which we have done in this case as fol- lows: mysql_select_db('books'); The mysql_select_db() function has the following prototype: bool mysql_select_db(string database, [resource database_connection] ); It will try to use the database called database. You can also optionally include the database link you would like to perform this operation on (in this case $db), but if you don’t specify it, the last opened link will be used. If you don’t have a link open, the default one will be opened as if you had called mysql_connect(). Querying the Database To actually perform the query, we can use the mysql_query() function. Before doing this, however, it’s a good idea to set up the query you want to run: $query = "select * from books where ".$searchtype." like '%".$searchterm."%'"; In this case, we are searching for the user-input value ($searchterm) in the field the user specified ($searchtype).You will notice that we have used like for matching rather than equal—it’s usually a good idea to be more tolerant in a database search. Tip It’s important to realize that the query you send to MySQL does not need a semicolon on the end of it, unlike a query you type into the MySQL monitor. We can now run the query: $result = mysql_query($query); The mysql_query() function has the following prototype: resource mysql_query(string query, [resource database_connection] ); You pass it the query you want to run, and optionally, the database link (again, in this case $db). If not specified, the function will use the last opened link. If there isn’t one, the function will open the default one as if you had called mysql_connect(). 13 525x ch10 1/24/03 3:37 PM Page 230 231 Retrieving the Query Results This function returns a result identifier (that allows you to retrieve the query results) on success and false on failure.You should store this (as we have in this case in $result) so that you can do something useful with it. Retrieving the Query Results Avariety of functions are available to break the results out of the result identifier in dif- ferent ways.The result identifier is the key to accessing the zero, one, or more rows returned by the query. In our example, we have used two of these: mysql_num_rows() and mysql_fetch_array(). The function mysql_num_rows() gives you the number of rows returned by the query.You should pass it the result identifier, like this: $num_results = mysql_num_rows($result); It’s useful to know this—if we plan to process or display the results, we know how many there are and can now loop through them: for ($i=0; $i <$num_results; $i++) { // process results } In each iteration of this loop, we are calling mysql_fetch_array().The loop will not execute if no rows are returned.This is a function that takes each row from the resultset and returns the row as an associative array, with each key an attribute name and each value the corresponding value in the array: $row = mysql_fetch_array($result); Given the associative array $row,we can go through each field and display them appro- priately, for example: echo '<br />ISBN: '; echo stripslashes($row['isbn']); As previously mentioned, we have called stripslashes() to tidy up the value before displaying it. There are several variations on getting results from a result identifier. Instead of an associative array, we can retrieve the results in an enumerated array with mysql_fetch_row(), as follows: $row = mysql_fetch_row($result); The attribute values will be listed in each of the array values $row[0], $row[1], and so on. You could also fetch a row into an object with the mysql_fetch_object() function: $row = mysql_fetch_object($result); 13 525x ch10 1/24/03 3:37 PM Page 231 . saves time and server overhead. It is also worth noting that persistent connections don’t persist if you are running PHP as a CGI. (Each call to a PHP script starts a new instance of PHP and closes. command such as use books; We also need to do this when connecting from the Web. We perform this from PHP with a call to the mysql_ select_db() function, which we have done in this case as fol- lows: mysql_ select_db('books'); The. pass it the name of the host on which the MySQL server is running, the username to login as, and the password of that user.All of these are option- al, and if you don’t specify them, the function