Sei sulla pagina 1di 2

CD Home < Web Techniques < 2000 < October

A Database-Independent API for PHP


By Sterling Hughes
PHP is a valuable item in any Web developer's toolbox. The server-side technology allows for rapid development of dynamic,
database-driven applications. One of PHP's major strengths is its ability to connect with many different databases, including Sybase,
PostgreSQL, Oracle, or MySQL.
Database connectivity, however, is also one of PHP's major weaknesses. Specifically, PHP's lack of a standard database API makes
switching databases difficult and costly once an application has been written. For example, if you write a program that uses a MySQL
database on the back end, and you want to modify that program so that it uses a Sybase database, you'll most likely spend hours
modifying the code.
Highest Form of Flattery
Fortunately, there are several workarounds to this problem, including a couple that rely on new features in PHP4. A lot can be
learned from examining (and borrowing) a solution from Perl. For the particular solution presented here, I've used Perl's DBI as a
model. Before Perl 5, many Perl programmers faced the same problem that PHP programmers face today. They had to use different
modules and subroutines to access different databases. For example, the sybperl module worked with Sybase, while oraperl
interfaced with Oracle. Switching databases meant going through the code and changing all references from sybperl to oraperl. Perl
programmers got frustrated and began looking for a better solution. The result was the DBI module.
DBI lets you access many different databases with the same set of methods. For example, the code in Listing 1 checks a MySQL
database to verify a username and password pair. If you wanted to change the database from MySQL to Sybase all you'd need to do
is change the value of the $dbtype variable from mysql to sybase.
Listing 2 illustrates the same script written in PHP4. Notice that you need to make function calls that are specific to the MySQL
database, such as the mysql_connect function. If you want to change the database you have to change all function calls (that is,
mysql_connect to sybase_connect). While this might not be too bad for a small project, it creates a problem for large programs
with many database calls.
Notice that the calls are very similar though. When you're working with a Sybase database you use sybase_connect. When you're
working with an Interbase database, you use ibase_connect. A MySQL database uses mysql_connect. An mSQL database uses
msql_connect, and so on. And if you look at all the different functions for every database, they may vary a little but the basic idea is
the same: Each database has its own version of the connect, query, fetch, free, and close functions. Because of these similarities, it's
possible to write a wrapper around these functions and create a database-independent API.
The Plan
There are several ways to incorporate a database-independent API into your applications. One is to use the PHPLIB library. The
trouble with PHPLIB is that it's not a good solution for writing portable applications. While PHPLIB is popular on many systems, for
every person who uses PHPLIB there are many who don't (or can't). If you write an application that requires PHPLIB, you have to be
sure the library is available on the target system. Often, you won't have that luxury.
Another option is to use an object-oriented approach, much like Perl's DBI does. In this case, you'd create a class that you could
instantiate (probably named something like DB). After you create the instance of the class, you'd supply, as a parameter to a
function, the name of the specific database you wanted to use. The DB class would then call another class that handles the specific
access to that type of database.
An example of database independence through classes is available from phpclasses.upperdesign.com, via a class named Metabase.
However, I'm not going to cover the object oriented approach because, as Zeev Suraski, CTO of Zend Technologies says, "PHP is not
an object oriented language." While PHP version 4 does support the main object-oriented concepts, it wasn't meant to be a
full-blown object-oriented programming language.
Instead, let's look at two other approaches. The first is to use variables that are set at the beginning of the code, as in Listing 3. The
$dbtype variable contains the name of the database you're using. Then, other variables and basic function calls refer to the $dbtype
variable repeatedly. Building dynamic function calls in this way is a new feature found in PHP4. Previously, this method wasn't
possible.
The variable approach will work pretty well for several databases and is easy to implement. The main problem with the variable
approach is that it's difficult to extend to complex examples and doesn't work with certain database connections (such as ODBC).
Therefore, you may want to consider a function-based approach.
I like the function-based approach best because it's fast, extensible, and easy to use. You use generic wrapper functions for the
specific database calls. When you call these wrapper functions they call the appropriate database functions on your behalf. Listing 4
demonstrates the use of the function-based API. This program reproduces the authentication script that I previously wrote using the
mysql_* function calls (in Listing 2). Instead of calling the database-specific mysql_connect function, I now call the generic
db_connect function.
If you want to change the active database from MySQL to Sybase, simply change line 6 of Listing 4 to:
include_once ("DB/sybase.php");
With that single modification, you can successfully swap the database from MySQL to Sybase.
Creating Functions
Designing an API is one thinghowever, implementing it is a completely different ball game. For the database swap in Listing 4 to
work, you have to implement all the appropriate database functions in a file called sybase.php. This means putting together all the
generic db_* functions you'll ever need, which is less difficult than it sounds. Consider the db_connect function call. It's simply a
wrapper for the sybase_connect function. That's all you need to create the different functions: You simply have to delegate the
generic functions like db_connect to the appropriate native call. Listing 5 shows some of the generic functions for a mysql.php file.
Take a look at the full code.
Because all the functions follow the same skeleton, I've created the dummy function in Example 1 for teaching purposes. Line 1
declares the function, the $args array allows for the passing of optional parameters. All of the generic functions begin with db_. The
generic functions can take between zero and two parameters, and the switch statements in lines 3 through 11 account for that. If the
caller supplies the maximum number of parameters, the switch statement falls into the default clause (instead of a specific case
clause). This allows the function to cope with other databases that may require more than two arguments.
Also, note that the code doesn't use a break inside the switch statement. Each case returns a value, causing the function to
terminate (without a break statement).
Missing Features
One of the problems with this API is that you may lose some functionality. Some databases don't support transactions. For example,
if you go from Oracle to MySQL, you'll lose transaction support and may have to modify your code in more places than one. In some
cases you might be forced to use the proprietary database function calls to implement the missing features.
The solution will depend on how complex your needs are. For something simple like preparing a query and executing it, you can
write generic code that emulates the preparation of a query. The db_prepare and db_execute functions in Listing 5 do that.
For more advanced features (like transactions) it may be too much effort to roll your own emulation. Therefore, I usually have my
code return an error if the caller requests an unsupported function. For example, consider the db_commit function. If the database
supports transactions, this call commits any pending operations. For a database that doesn't support transactions, I'd write a function
similar to that in Example 2, where ERROR_NOT_CAPABLE is a predefined constant.
In Practice
The good thing about keeping your generic functions together in one file (per database) is that you can reuse the code in many
projects. As you write more wrappers, you increase the number of databases you can use in any project. Soon you'll have a library
of database wrappers stored in files like mysql.php, sybase.php, oracle.php.
If you're like me, you'll usually want to separate the files from your application. That means that while your application might reside
in the /www/htdocs directory, your included files might be in a directory like /usr/local/include. It's annoying always to have to
specify the full path in your code as follows:
include_once ("/usr/local/include/DB/mysql.php");
Fortunately, the php.ini file has a parameter called include_path just for this situation. This parameter tells PHP where to look for
include files. If you change its value to /usr/local/include, you don't have to type the entire path in your code.
Do note that local files have priority. If you have a file named SomeClass.php in both a directory specified by the include_path and
also in your local directory, PHP will use the file in your local directory.
In Conclusion
When it comes to interfacing with a single database, PHP is an excellent tool. However, when you're writing a program that must run
on many different database systems, PHP doesn't offer much aid. Libraries like PHPLIB can help in certain situations, but aren't
completely portable solutions. Many people predict that PHPLIB will fade away because PHP4 incorporates many of its popular
features, like sessions. One thing to look out for is the DBI class for PHP, which is under development and will be ready for prime
time next year. Until then, using the variable- and function-based approaches is your best bet for writing programs that require
database independence.
(Get the source code for this article here.)
Sterling is the author of PHP Developer's Encyclopedia (MacMillan). A Unix programmer, he's been a software developer for five
years.
Copyright 2003 CMP Media LLC

Potrebbero piacerti anche