Skip to page content or skip to Accesskey List.
Search evolt.org
evolt.org login: or register

Work

Main Page Content

Abstract PHP's database code with PEAR::DB

Rated 4.03 (Ratings: 9) (Add your rating)

Log in to add a comment
(14 comments so far)

Want more?

 
Picture of andrewpander

Andrew Forsberg

Member info | Full bio

User since: January 09, 2001

Last login: January 09, 2001

Articles written: 1

With the PEAR::DB classes you can avoid: re-writing frequently used routines to suit different vendors' databases, or the time-consuming process of building your own wrapper functions.

Why abstract your database code?

Because often there's no reason why that useful section of code can't be reused on other database servers. Whether it's a ten line algorithm you spent half a day on, or a ten thousand line monster you just know you will need to migrate someday from MySQL to Oracle or PostgreSQL. You might also develop on one database server while targeting several production servers from different vendors. It makes good sense to avoid replacing all the database specific functions and routines by using an abstraction layer between your code and database vendor specific functions.

Does that sound like a good idea? I thought so. One way to achieve this is to build wrapper scripts to hide the real access functions from your programs. Building a library of these wrappers to support all the functions of supported databases in the PHP API is a really big nasty project. Fortunately most of it has already been done, and is available via the PEAR repository, included in recent PHP releases by default.

By using PEAR::DB and a simple configuration script, you can avoid much of the database function related hassle associated with migrating scripts. You also get access to a comprehensive, standardized error handling system, and several functions not yet available in some of PHP's individual database APIs (like prepare() and execute()).

So, what do I need?

  • First, you need to be familiar with at least one of PHP's sets of database access functions, it doesn't matter which group (MySQL, Oracle, PostgreSQL, …).
  • Second, you will need access to a PHP build which includes PEAR. If you can execute <?php require_once 'DB.php'; ?> without an error then you are good to go (Windows users may need to add the PEAR path to their php.ini file, the PEAR FAQ explains this).

    If, after the security announcement (2002 / 2 / 27) your host still hasn't, and doesn't intend to, upgrade, then IMO you need to find a new host. Version 4.1.2 of PHP builds PEAR by default.
  • Finally, you will need a database to play with. Doh!

Caveat: the SQL statements will need to be generic enough to work on all of your target databases, otherwise they should be built separately from each other (via a switch block, or the prepare() strategy described at the end of this introduction). PEAR::DB only helps abstract access to the database, it cannot abstract SQL statements. For accurate information about what each supported database is capable of, implemented in PEAR, and tested for, read the document at your pear_base_dir/DB/STATUS.

The config file

PEAR::DB needs to know about the usual suspects for a database connection (the server hostname, username, password, and database name), just like mysql_connect() or pg_connect() do. By using an external config file instead of listing this information inline you:

  • save time and increase portability by only needing to change a parameter in one place;
  • gain some security because you can move the file with password information outside the web accessible directory on the server; and
  • avoid having to type that stuff out again and again, or waste time hunting down the inevitable typo.
// /home/foo/incs/db_config.php
// put this file somewhere safe, preferably outside the document root

$DB_host    = "bigBoxInTheCorner";  // the hostname of the database server
$DB_user    = "thatIsMe";           // the username to connect with
$DB_pass    = "notTellingYou";      // the user's password
$DB_dbName  = "myDatabase";         // the name of the database to connect to
$DB_dbType  = "seeBelow";           // the type of database server.

At the time of writing, the supported database types for the $DB_dbType variable are:

Database TypeDatabase Type String
MySQLmysql
PostgreSQLpgsql
InterBaseibase
Mini SQLmsql
Microsoft SQL Servermssql
Oracle 7/8/8ioci8
ODBC (Open Database Connectivity)odbc
SyBasesybase
Informixifx
FrontBasefbsql
Use the database type string in the config file so PEAR::DB can create the right connection type.

The PEAR::DB object

Once you've saved the config file somewhere creating a connection with a PEAR::DB object is really easy:

require_once 'DB.php';          // You know you're gonna need this
                                // Imports all the PEAR::DB classes, static
                                // methods, etc...

                                // Get the connection variables:
require_once '/home/foo/incs/db_config.php';

$dsn = $DB_dbType . "://"       // Build a DSN string (Data Source Name)
        . $DB_user . ":"        // Required by DB::connect()
        . $DB_pass . "@" 
        . $DB_host . "/" 
        . $DB_dbName;

$db = DB::connect($dsn, TRUE);  // Creates a database connection object in $db
                                // or, a database error object if it went wrong.
                                // The boolean specifies this is a persistent
                                // connection like mysql_pconnect(), it
                                // defaults to FALSE.

if (DB::isError($db)) {         // Check whether the object is a connection or
                                // an error.
    die($db->getMessage());     // Print out a message and exit if it's 
                                // an error object.
}

// Do all sorts of stuff with your database connection here.

$db->disconnect();              // Close the connection.

So, creating database connections with PEAR::DB objects doesn't take that much more effort than a regular PHP database whatever_connect() function. Building, executing, and processing SQL queries is also a piece of cake:

// Get a connection as above

$sql = "SELECT first_name
        , last_name
        , pet_name
        FROM person
        WHERE person_id > 0";   // Build your SQL query
    
$res = $db->query($sql);        // $db->query processes the query in the
                                // string $sql, using the database
                                // connection we built before in $db.
                                //
                                // $res will be a result resource object
                                // or an error object

if (DB::isError($res)) {        // Check the result object in case there
    die($res->getMessage());    // was an error, and handle it here.
}

while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) {
    // Fetch a row from the result resource object $res.
    //
    // DB_FETCHMODE_OBJECT returns a row with column names as keys.
    // Other possibilities are listed here:
    // http://pear.php.net/manual/en/core.db.tut_fetch.php

    if (DB::isError($row)) {
        die($row->getMessage());  // fetchRow can return an error object
                                  // like most PEAR::DB methods.
    }
    
    print ("<p>Hey, it's:<br />" 
            . $row->first_name . " " . $row->last_name 
            . " ... and their freaky pet: " . $row->pet_name 
            . "</p>\n");
}

$res->free();                   // Release the result resource object.
                                // Similar to: mysql_free_result($result)

// Disconnect as above

Other handy PEAR::DB features

Just get*() the data already

The fun doesn't stop there folks. There are also some neat time-saving methods in PEAR::DB. The get*() group (getOne(), getRow(), getCol(), getAssoc(), and getAll()) takes an SQL query string as an argument, queries the database, fetches the results, then frees the result resource, all in one go. Explanations and examples are available at the bottom of the fetch() tutorial.

prepare() to execute()!

Some database servers allow you to save an SQL query with place holders, then use it later by filling in the blanks. For example: say you want to insert a variable number of rows into a table, but would like to avoid putting the INSERT query inside a loop. PEAR::DB emulates this functionality for those databases that don't support it natively. (As at the release of PHP v. 4.1.2, a fully tested, native implementation of this feature is only available for Oracle, and an untested implementation for InterBase; it is emulated on all the other supported databases. See the pear_base_dir/DB/STATUS document for full details of the implemented / emulated, tested / untested, features of your target databases.)

Here's an example of how the executeMultiple()function could be used on a table like the one in our previous SELECT example:

// Get a connection as above

    // First we translate a normal INSERT query into a generic 
    // one we can re-use. So, if this is a valid INSERT query:
    //
    // INSERT INTO person (first_name, last_name, pet_name) 
    //        VALUES ("jonnie", "walker", "tipples");
    // 
    // Replace the quoted values with ? and your good to go:

$sth = $db->prepare("INSERT INTO person"
            . " (first_name, last_name, pet_name)"
            . " VALUES (?, ?, ?)");    

    // You can also use ! instead of ? to insert data as is (not quoted),
    // or & to insert the data from a filename.


    // Next we build a two dimensional array that contains the
    // values we want to insert. 
    // executeMultiple() will process all of these in a single step.
    // execute() will process only one SQL command at a time.

$data = array (
            array ("johnnie", "walker", "tipples"),
            array ("mandy", "morrison", "mumpy"),
            array ("jumpy", "jones", "jiggles"),
            array ("willy", "the menace", "groutch"));


    // executeMultiple() takes the above prepared statement handler ($sth)
    // and the array of data ($data), and processes it. 

$res = $db->executeMultiple($sth, $data);

    // If everything went ok executeMultiple returns a Result object 
    // (in the case of SELECT statements).
    // If anything went wrong then a DB Error object will return, just
    // like nearly everywhere else in the PEAR project.


if (DB::isError($res)) {         // Check the result object in case there
    die($res->getMessage());  // was an error, and handle it here.
}

// Disconnect as above

More details can be found in PEAR's execute() tutorial. They also suggest a nifty solution for handling different database's SQL syntax with an array of prepared statements where the key is the database type:

// Get a connection as above

    // Create an array where the database type string is the key
    // to the appropriate SQL statement.
    // MySQL supports either syntax, but you get the general idea.

$sql['oci8']['INSERT_PERSON'] = "INSERT INTO person"
    . " (first_name, last_name, pet_name)"
    . " VALUES ( ?, ?, ? )" ;
$sql['mysql']['INSERT_PERSON'] = "INSERT INTO person"
    . " SET first_name=?, last_name=?, pet_name=?" ;
    
    // And so on with all the types of syntax you need to support.

    // Next, prepare the query, using the $DB_dbType variable
    // to access the correct SQL syntax:

$sth = $db->prepare($sql[$DB_dbType]['INSERT_PERSON']);    

    // The rest as in the previous executeMultiple() example.

Summary

PEAR::DB supplies us with a standard, fairly easy-to-use, API for database connections. By using the methods it supplies we can more easily write portable code than is possible using PHP's vendor-specific database functions. And, the icing on the cake, we get new functionality previously not available for all databases.

References

The core documentation for PEAR, the PEAR::DB docs, and the downloadable docs will be very handy. The PEAR FAQ is primarily targeted at PEAR developers.

PHP lists, including the PEAR Project list archives can be found at Zend's Mailing List page.

Tutorials, reviews, and information about PEAR classes are listed on this PHP Weblog.

PHP Builder have a more detailed tutorial by Allan Kent.

Give him a manhattan or two and he'll mumble for hours about how he shaved 10 milliseconds off a recursive php routine, the necessity of the fibonacci series in well typeset printed documents, or perdita the cat.

He lives in Auckland, New Zealand, and listens to way too much Wu-Tang Clan and Philip Glass for Vanessa's taste. Andrew is a part owner, and runs the technical side, of uber.net.nz.

Dealing with a customer...

Submitted by chozsun on March 10, 2002 - 02:04.

... wanting to run SquirrelMail which uses PEAR::DB. This article's timing is second to none and has helped me a great deal. -- ChozSun chozsun.com

login or register to post comments

wow

Submitted by Martin Tsachev on March 30, 2002 - 13:50.

A great article I was coding only for MySQL but now I'm gonna start the way forward and use PEAR::DB.

Thanks for pointing this out to me.

login or register to post comments

yay

Submitted by andrewpander on April 2, 2002 - 01:01.

Thanks shaggy!

PEAR, IMO, is a big step in the right direction for the entire PHP community. ColdFusion has fusebox, Perl has CPAN, now we have PEAR -- and the name is cooler than any of the others. :)

The net methods in the repository are worth a look, as are the authentication methods. While most reasonably experienced PHP developers can do authentication, etc, without much trouble, it's nice to be able to leave it out of the equation and get on with coding the app in question. And you get upgrades without even touching the keyboard. Heh!

login or register to post comments

PEAR is growing

Submitted by Martin Tsachev on May 15, 2002 - 15:01.

New stable versions of packages are added every week. One that I've thought of writing myself is the form generator HTML_QuickForm, it's really a great extension package.

login or register to post comments

Less code

Submitted by jonaskb on July 4, 2002 - 02:19.

I agree with the author of this article - it's about time that PHP programmers started using the same common API (PEAR DB, that is) for database access instead of the native PHP SQL functions or home-made database abstraction classes.

A few comments: First of all, the name of PHP's common database API is "PEAR DB", not "PEAR::DB".

Secondly, looking at the examples above, one might get the impression that using PEAR DB requires a huge amount of code: Each time you do something, you have to check for errors and treat them. However, there are ways to avoid writing "if (DB::isError(..." each time you do something: Set up an error handler at the beginning of the script. There are some examples of that at the bottom of this page: http://pear.php.net/manual/en/class.pear.php. The one that I usually use is the following:

PEAR::setErrorHandling(PEAR_ERROR_TRIGGER);

Once you've written that line, you don't need to check for PEAR DB errors and treat them. Nice, ain't it? : ) You need to write the line above before you start connecting to the database - so the first few lines of my PHP scripts look something like this:

<?php
require_once("DB.php");

PEAR::setErrorHandling(PEAR_ERROR_TRIGGER);

$db = DB::connect($DB_dbType://$DB_user:$DB_pass@$DB_host/$DB_dbName", TRUE);

$db->setFetchMode(DB_FETCHMODE_OBJECT);
?>

OK, so what the setFetchMode thing? Well, it's another way of ensuring less code: It sets a default fetch mode, so that you don't need to specify a fetch mode in the the fetchRow() method. That is, once you've set up a default fetch mode, you can fetch rows like this (notice the lack of a fetch mode argument to the fetchRow() method):

while ($row = $result->fetchRow()) {
    echo $row["name"];
}

There's more information to be found in the PEAR DB manual: http://pear.php.net/manual/en/core.db.php.

login or register to post comments

Less code

Submitted by andrewpander on July 4, 2002 - 16:06.

Well, PEAR::DB vs PEAR DB, pfft... :-) The docs aren't consistent on the matter (see the reference pages for DSN and Query, for example), but whatever...

Re: time and code-saving techniques -- yea that looks really nice, thanks. I've wrapped most of the error handling routines in a custom mailError($res) type routine, which has been great, but using the API to accomplish a similar task is a v. good idea.

Cheers

login or register to post comments

Re: Less code

Submitted by jonaskb on July 4, 2002 - 16:49.

The best way to handle errors in PHP is to use PHP's built-in error handling system which includes the use of set_error_handler() and trigger_error(). If you do it that way, all errors (including the PEAR DB ones) are caught by your very own error handling function. An example: <php # Setting the name of the function we want PHP to call when errors occur: set_error_handler("error"); # Defining our error handling function: function error($type, $message) {     echo "Error: $message\n";     # If you want to receive an email when errors occur,     # you can call the error_log() function here. } # Attempting to open a file that doesn't exist... PHP will call the error() function: $fp = fopen("/file/that/does/not/exist", "r"); # We'll use the same error handling system for PEAR so that # PEAR errors will handled by our error() function: PEAR::setErrorHandling(PEAR_ERROR_TRIGGER); $db = DB::connect("pgsql://user:password@host/database"); ?> - Jonas Koch Bentzen

login or register to post comments

Re: Less code

Submitted by jonaskb on July 4, 2002 - 16:50.

The best way to handle errors in PHP is to use PHP's built-in error handling system which includes the use of set_error_handler() and trigger_error(). If you do it that way, all errors (including the PEAR DB ones) are caught by your very own error handling function. An example:

<php
# Setting the name of the function we want PHP to call when errors occur:
set_error_handler("error");

# Defining our error handling function:
function error($type, $message) {
    echo "Error: $message\n";

    # If you want to receive an email when errors occur,
    # you can call the error_log() function here.
}

# Attempting to open a file that doesn't exist... PHP will call the error() function:
$fp = fopen("/file/that/does/not/exist", "r");

# We'll use the same error handling system for PEAR so that
# PEAR errors will handled by our error() function:
PEAR::setErrorHandling(PEAR_ERROR_TRIGGER);

$db = DB::connect("pgsql://user:password@host/database");
?>

- Jonas Koch Bentzen

login or register to post comments

Re: Less code

Submitted by Martin Tsachev on July 7, 2002 - 18:37.

I've wrapped most of the error handling routines in a custom mailError($res) type routine, which has been great, but using the API to accomplish a similar task is a v. good idea.

You can still use your custom function with PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, "function_name");.

login or register to post comments

oracle connection

Submitted by claytons on March 22, 2006 - 16:35.

here is an example for connecting oracle using the TNS values:

$hostspec = "(DESCRIPTION = (SDU=123456)
(TDU=123456)
(ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(Host = example.com)(Port = 1234)))
(CONNECT_DATA =(SID = your_sid_value)))";

define("DB_DSN",'oci8://user:pass@'.rawurlencode($hostspec).'/prod');
$db = DB::connect(DB_DSN);

if (DB::isError($dbh)) {
print "Database connection failed: ";
print $dbh->getMessage();
exit;
}

login or register to post comments

error in previous comment.

Submitted by claytons on March 22, 2006 - 18:55.

change $dbh to $db

login or register to post comments

Error: connect failed

Submitted by aan.isnaini on May 12, 2006 - 17:45.

Dear All,
I tried this tutorial use oracle 10g database. But the result is failed.
my code for connect to database like below:
<?php
        
require_once ("DB.php");
    
$user="myuser";
    
$pass="mypassword";
    
$host="localhost";
    
$db_name="xs10";
        
$dsn="oci8://$user:$pass@$host/$db_name";
        
$db=DB::connect($dsn,false); // I already try with "true" value also
    //echo $dsn;
    
if(DB::isError($db))
    {
        die(
$db->getMessage());
    }
?>

the result page always "Error: connect failed".
if i used below code:
<?php
$conn 
OCILogon("myuser""mypassword""//localhost/xs10");

        if (!
$conn) {
          exit;
        }

        echo 
OCIServerVersion($conn) ."<br>\n";
        print 
date('Y-m-d H:i:s')."<br><br>\n";
?>

the result is successfully, i mean the browser show result:
-------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production With the Partitioning, OLAP and Data Mining options 2006-05-13 00:04:15
-------------------------------------------------------
but I don't want use this, I just want use Pear DB.

i was tried clytons comment post also, using TNS. the result is failed.

my code following clytons comment is:
<?php
require_once ("DB.php");
$hostspec ="(Description=(SDU=123456)(TDU=123456)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(Host=localhost)(Port=12345)))(CONNECT_DATA=(SID=xs10)))";
    
define("dsn",'oci8://myuser:mypassword@'.rawurlencode($hostspec).'/prod');
    
$db=DB::connect($dsn);
    echo 
$dsn;
    if(
DB::isError($db))
    {
        die(
$db->getMessage());
    }
?>

the result is: "DB Error: not found"
i tried this article connect to mysql, the result is successfully.

<?php
        
require_once ("DB.php");
    
$user="myuser";
    
$pass="mypassword";
    
$host="localhost";
    
$db_name="xs10";
        
$dsn="mysql://$user:$pass@$host/$db_name";
        
$db=DB::connect($dsn,false); 
        
//echo $dsn;
    
if(DB::isError($db))
    {
        die(
$db->getMessage());
    }
?>


please help me.
i need migration (redeveloping) current stable software immediatelly, since my company moving to oracle. i don't want this situation come to me later. that whay i using pear DB to prepare some day change database provider.
I need migration (redeveloping) current stable software immediately, since my company moving to oracle. I don't want this situation come to me later. That why I using pear DB to prepare some day change database provider.
Does some body have article/tutorial/reference or information of book special pear DB and Oracle? I need this for advance programming later on.
Thank You
Best Regard's
Aan Isnaini

login or register to post comments

Architecture Question

Submitted by msword on July 20, 2006 - 02:08.

I'm currently using Pear DB to Abstract my Database code. I've taken it a step further and implemented a Data Access Layer. My day job is an asp.net developer, so we use the concepts of code behind, data access layers, busines layer, and presentation layer. I've tried to carry some of this over to PHP. I use smarty as my presentation layer, and Pear DB as my data access layer. My question is I'm passing the db object around and I'm wondering if it's smart or not. Let me explain. This is a typical page. It calls creates the DB object. calls at DAL object and passes the values to smarty. all my sql code is in the classes which make it nice from a maintenance stand point.
<?
require_once('DB.php');
include ($_SERVER['DOCUMENT_ROOT']."/Config/webconfig.php"); //like an asp web.config file
include ($_SERVER['DOCUMENT_ROOT']."/classes/Bar.class.php");
 
 /**
 * Connect to the database
 */
$db =& DB::connect($dsn);
if (DB::isError($db)) {
die($db->getMessage());
}
code ....
$myBar = new Bar("");
$bars = $myBar->getBarDetails($db, $region);
code ....
$smarty->assign('bars', $bars);
$smarty->display("Listing/Bar/ViewBars.tpl");

 
 ?>
I then have my data access layer which is essentially a class which just does database work. this way I seperate my sql code from my "code behind"
<?
require("Listing.class.php");

 class Bar extends Listing{
 
  code here...
function getBarDetails($db, $id) {
$sql = "sql statements here";
$result =& $db->query($sql);
if (DB::isError($result)) {
die($result->getDebugInfo());
}
$row = $result->fetchRow(DB_FETCHMODE_ASSOC);
return $row;
}
end class code...
Now you can see I'm passing the db object around. I'm wondering if this is a performance issue? I've received a few unable to connect errors, so I dont know if it's related to performance or a hosting problem. does anybody have any comments on the way I do this? Thanks Matt

login or register to post comments

I just don't get this DB.php thing

Submitted by MyWifeIsDead on January 6, 2009 - 13:15.

Okay. So here's the deal.
I read on several different websites about "how to create a login script" and every time it uses PEAR. I understand how good this is, but every time it starts with "require_once 'DB.php';" and I didn't find any explanation what or where that DB.php is. I decided to skip this part (removed require_once 'DB.php';)once and test the code. Everything worked, except for the part, that I couldn't connect to the database, so I was always logged off. I couldn't create new users, etc. But once I add the "require_once 'DB.php';" line I get only white screen. I tried to experiment with new file, called DB.php but all I got was plain text message of what I wrote in the file. Can you explain what and where it is, please?

There are 10 kind of people. Those who understand binary, and those who don't.

login or register to post comments

The access keys for this page are: ALT (Control on a Mac) plus:

evolt.orgEvolt.org is an all-volunteer resource for web developers made up of a discussion list, a browser archive, and member-submitted articles. This article is the property of its author, please do not redistribute or use elsewhere without checking with the author.