There are currently a lot of libraries available for PHP that provide 'database abstraction'. Some of them are good, some of them aren't, and they run the range from lightweight/efficient classes like the database classes in Eclipse, to feature-rich but bloated code like the classes in PEAR. What the majority of the classes promise is database independence. The idea is that if your code is originally written using database X as a backend, but two months down the line you discover you need to use database Y, you should be able to make the switch without having to edit every file in your application.
IMO, this is a very noble yet entirely useless endeavor in its present incarnation. The problem is that, although these libraries abstract the calls to the built-in PHP functions for databases, they do not abstract the SQL itself. Unfortunately, only the very basic SQL syntax is similar (not identical) across different database systems. So if I use database abstracion and write my application for MySQL and then later want to switch to PostgreSQL or Oracle, although I won't have to change most of the method calls in my scripts, I will still have to change almost all of the SQL queries! So you are basicly touching most of the files in your application anyway.
This isn't to say that creating classes to access your database is completely pointless--doing so still gives you the advantages of encapsulating all of the DB functionality into OO structure and makes your code easier to understand. However, I would like to describe an approach that works pretty well for me (although I'm still ironing it out).
One thing I realized is that if I have to rewrite my queries anyway, the code needed to achieve the so-called 'database independence' is just wasted overhead. So instead of writing a class that could switch functionality between several database sytems, I write my database classes to be specific to one system. I generally end up with two or three classes depending on the database being used: DBConnection, DBQuery, and sometimes DBLargeObject (useful with PostgreSQL, for instance).
DBConnection defines methods for things related to the connection itself as well as one-off commands such as INSERT, UPDATE, and DELETE that do not return a result set. It handles the opening and closing of the connection, and it also handles transaction support for a database that uses transactions.
DBQuery defines methods for executing a query and returning the result set, and also methods for doing common things with the result set such as returning each row as an array or object, etc.
DBLargeObject handles BLOBS for databases that support a seperate namespace for these datatypes. It lets you import and export files into a BLOB, return the results directly to a browser, etc.
Because these classes contain code only for a specific database system, they do not need to spend one microsecond of time abstracting. For commands that are similar between all SQL databases, the method names will be the same (so you can switch between databases without having to learn a whole new set of semantics for the new class), but the functionality may be quite different internally.
So now I have my database functions contained in a class. What does that gain me besides encapsulation at this point? Not much. I would still have to change every part of my code to switch databases at this point.
The solution is to make better use of OOP. In my scripts, instead of calling database methods all over the place, I use objects for everything that deals with persistant data.
For instance, lets say my program tracks information about user accounts. I would create two classes: User and UserCollection. User's methods and properties will deal entirely with the data stored for a specific user, and UserCollection's methods and properties deal with things such as returning a list of users and manipulating that list in some way.
If I ever need to access or manipulate information about a user in my program, I make sure that I never talk to the database directly. If I can't do what I want with the User and UserCollection classes (possibly in combination with other data object classes) then I will enhance those classes to provide the needed functionality.
What you gain through this approach in addition to better OOP structure is true database independence. If I switch from MySQL to PostgreSQL, I will not have to edit every file in my application. I would simply use DB* classes that are for PostgreSQL and then rewrite the methods in my data object classes (like User and UserCollection) so that they would work with the new database. Not only does this mean not editing queries throughout your application, but it also takes care of problems that come up due to changes in table structure that are needed due to differences in database systems.