In the last column we introduced the use of embedded databases within PHP via the Database Abstract (DBA) Layer. Now, we will re-implement Address Book 1 (AB1) using this superior technology.
Python vs. PHP: Choosing your next project's language
Accepting New Data
In AB1, ab1.php built a string, $data, consisting of the uploaded data. Each item of data was on its own line with a leading identifier tag, incorporated so that the parser knew how to read the file. For example:
FGavin
LSherry
tells the parser that the first line is a first name and the second line is a second name. While we could simply use the same $data string, it does not make much sense to separate each attribute of data by a new line. Instead, we will take a lesson from the early Common Gateway Interface (CGI) engineers and use the name=value pairing. This also means that we can escape our delimiter using standard PHP URL functions. For example:
F=Gavin&L=Sherry
The script begins with the declaration of a variable $DB, where the name of the database is stored. This is followed by a function, mkdb(), which compensates for a shortcoming in PHP's DBA layer implementation. It checks if the database already exists, and if it does not, it creates it and returns the identifier, $id. If the database already exists, mkdb() returns false.
Following this, the code tests if it has been called to add data to the address database (see the December 2001 issue for a more in-depth explanation of this). The script then executes the mkdb() function with the argument $DB, our database. It tests the return value and, if it is false, opens $DB for writing without creating it.
The data string $data is then built in the form of name=value pairs. Since this mimics the CGI query protocol used by PHP and most other Web application development systems, the script can make use of a standard URL encoding function, rawurlencode(), to make sure the data is not corrupted. For example, if one of the uploaded values contained an ampersand (&), this would affect our $data string in the following way:
F=Gavin & Co&L=Sherry
The parser, if it follows the name=value rules, will think that the value of F is 'Gavin'. Moreover, the next name, 'Co' will have no value. By calling rawurlencode(), & will be converted to the value '%26'. Our parser will decode these values later.
Finally, the data is inserted into the database, using $ln, the last name, as a key. If dba_insert() returns true, "Data successfully updated" is sent to the user; otherwise, an error "Could not store data" is raised. Note that the database is closed in both instances to preserve data integrity.
Search Address Book 2
Searching in AB2 is much simpler than AB1, because the parser does not need to parse the whole file, just the result. See the implementation below:
/* search for the last name stored in $query */
if(isset($submit) && (strcmp($submit,"Search") == 0)) {
if(!file_exists($DB)) { /*
database has not been created yet */
exit("No entries to search");
}
if(!($id = dba_open($DB,"r","db3"))) {
exit("Could not open $db\n");
}
if(($str = dba_fetch($query,$id))) {
/* found the query */
parse_str($str);
?>
NAME: <? echo rawurldecode($F); ?> <? echo rawurldecode($L);
?>
<?
/* etc */
} else {
echo "No entry with that last name";
} dba_close($id);
}
Like the data upload script, this second section of AB2 tests to see if it has been called by comparing $submit to "Search". To start with, the script checks to see if $DB exists - since if it does not, it hasn't been created with mkdb() in the first section of AB2.
After this, $DB is opened and dba_fetch() is called in order to retrieve the entry pointed to by $query, where $query is a last name. If dba_fetch() returns true, the key has been located. The resulting entry is put in $str which is parsed by parse_str(). This function extracts the names out of the name=value pairs and inlines them as variables with the corresponding value. That is, given the string:
F=Gavin&L=Sherry
The corresponding data can be accessed via $F and $L.
The address book data is the output to the user. Note that the data is processed with rawurldecode(), to unescape any work done by rawurlencode() earlier in the script.