PHP Database Access
Using DBM Databases
Problem
You have data that can be easily represented as key/value pairs, want to store it safely, and have very fast lookups based on those keys.
Solution
Example Using a DBM database
$dbh = dba_open(__DIR__ . '/fish.db','c','db4') or die($php_errormsg);
// retrieve and change values
if (dba_exists('flounder',$dbh)) {
$flounder_count = dba_fetch('flounder',$dbh);
$flounder_count++;
dba_replace('flounder',$flounder_count, $dbh);
print "Updated the flounder count.";
} else {
dba_insert('flounder',1, $dbh);
print "Started the flounder count.";
}
// no more tilapia
dba_delete('tilapia',$dbh);
// what fish do we have?
for ($key = dba_firstkey($dbh); $key !== false; $key = dba_nextkey($dbh)) {
$value = dba_fetch($key, $dbh);
print "$key: $value\n";
}
dba_close($dbh);
Discussion
PHP can support many DBM backends, such as GDBM, NDBM, QDBM, DB2, DB3, DB4, DBM, and CDB. The DBA abstraction layer lets you use the same functions on any DBM backend. All these backends store key/value pairs. You can iterate through all the keys in a database, retrieve the value associated with a particular key, and find if a particular key exists. Both the keys and the values are strings.
The program maintains a list of usernames and passwords in a DBM database. The username is the first command-line argument, and the password is the second argument. If the given username already exists in the database, the password is changed to the given password; otherwise, the user and password combination are added to the database.
Example Tracking users and passwords with a DBM database
$user = $argv[1];
$password = $argv[2];
$data_file = '/tmp/users.db';
$dbh = dba_open($data_file,'c','db4') or die("Can't open db $data_file");
if (dba_exists($user,$dbh)) {
print "User $user exists. Changing password.";
} else {
print "Adding user $user.";
}
dba_replace($user,$password,$dbh) or die("Can't write to database $data_file");
dba_close($dbh);
The dba_open() function returns a handle to a DBM file (or false on error). It takes three arguments. The first is the filename of the DBM file. The second argument is the mode for opening the file. A mode of r opens an existing database for read-only access, and w opens an existing database for read-write access. The c mode opens a database for read-write access and creates the database if it doesn’t already exist. Last, n does the same thing as c, but if the database already exists, n empties it. The third argument to dba_open() is which DBM handler to use; this example uses db4.
To find what DBM handlers are compiled into your PHP installation, use the dba_handlers() function. It returns an array of the supported handlers.
To find if a key has been set in a DBM database, use dba_exists(). It takes two arguments: a string key and a DBM file handle. It looks for the key in the DBM file and returns true if it finds the key (or false if it doesn’t). The dba_replace() function takes three arguments: a string key, a string value, and a DBM file handle. It puts the key/ value pair into the DBM file. If an entry already exists with the given key, it overwrites that entry with the new value.
To close a database, call dba_close(). A DBM file opened with dba_open() is automatically closed at the end of a request, but you need to call dba_close() explicitly to close persistent connections created with dba_popen().
You can use dba_firstkey() and dba_nextkey() to iterate through all the keys in a DBM file and dba_fetch() to retrieve the values associated with each key. The program calculates the total length of all passwords in a DBM file.
Example Calculating password length with DBM
$data_file = '/tmp/users.db';
$total_length = 0;
$dbh = dba_open($data_file,'r','db4');
$dbh or die("Can't open database $data_file");
$k = dba_firstkey($dbh);
while ($k) {
$total_length += strlen(dba_fetch($k,$dbh));
$k = dba_nextkey($dbh);
}
print "Total length of all passwords is $total_length characters.";
dba_close($dbh);
The dba_firstkey() function initializes $k to the first key in the DBM file. Each time through the while loop, dba_fetch() retrieves the value associated with key $k and $total_length is incremented by the length of the value (calculated with strlen()). With dba_nextkey(), $k is set to the next key in the file.
One way to store complex data in a DBM database is with serialize(). Stores structured user information in a DBM database by serializing the structure beforestoring it and unserializing when retrieving it.
Example Storing structured data in a DBM database
$dbh = dba_open('users.db','c','db4') or die($php_errormsg);
// read in and unserialize the data
$exists = dba_exists($_POST['username'], $dbh);
if ($exists) {
$serialized_data = dba_fetch($_POST['username'], $dbh) or die($php_errormsg);
$data = unserialize($serialized_data);
} else {
$data = array();
}
// update values
if ($_POST['new_password']) {
$data['password'] = $_POST['new_password'];
}
$data['last_access'] = time();
// write data back to file
if ($exists) {
dba_replace($_POST['username'],serialize($data), $dbh);
} else {
dba_insert($_POST['username'],serialize($data), $dbh);
}
dba_close($dbh);
Though can store multiple users’ data in the same file, you can’t search for, for example, a user’s last access time, without looping through each key in the file. If you need to do those kinds of searches, put your data in an SQL database.
Using a DBM database is a step up from a plain-text file but it lacks most features of an SQL database. Your data structure is limited to key/value pairs, and locking robustness varies greatly depending on the DBM handler. Still, DBM handlers can be a good choice for heavily accessed read-only data.
No comments:
Post a Comment