PEAR stands for ‘PHP Extension and Application Repository’ and has been
slowly building itself over the last few years. There are classes for logging,
compression, XML, and on and on. The most popular one is the database abstraction
class, which supports most popular databases.
In this article I’m going to lay out how you can go about using this great library
in your own classes. Specifically, we will be covering PEAR, PEAR_Error, DB, and
Log.
One thing to note here is that if you don’t know what OOP stands for or you have
yet to dive into PHP OOP programming this will most likely go over your head.
Starting with an OOP primer would be a good idea.
PEAR Primer
The PEAR Class
The base PEAR class is fairly abstracted and shouldn’t be used on its own, however,
it is a great class to build your classes off of. It’s major feature is that
it imitates destructors. With PHP5 on its way this will be void, since PHP5
will support destructors natively, I believe. The class file for PEAR also includes
the PEAR_Error class, which we will talk about in more detail at a later time.
First let’s discuss basing your classes on PEAR. We are going to start by creating
our own specific base class and then extending that to a user class to be used
on a site. Remember to document your classes using PHPDoc!
classes on PEAR. We are going to start by creating our own specific base class
and then extending that to a user class to be used on a site. Remember to document
your classes using PHPDoc!
require_once(‘PEAR.php’);
require_once(‘DB.php’);
require_once(‘Log.php’);
/**
* Default PEAR DSN
*
* @author Joe Stump
* @global string BASE_PEAR_DSN
* @access public
* @see Base::Base(), Base::$db
*/
define(‘BASE_PEAR_DSN’,’mysql://root:@localhost/base’);
/**
* Base Class
*
* Our base class will hold only the basic necessities that all
* of our child classes will need. Mainly DB connectivity and
* the ability to log errors.
*
* @author Joe Stump
*/
class Base extends PEAR
{
/**
* DB Class
*
* @author Joe Stump
* @access public
*/
var $db;
/**
* Log Class
*
* @author Joe Stump
* @access public
*/
var $log;
/**
* Base Contstructor
*
* Connect to the DB and create our Log, which can then be
* used by all children classes.
*
* @author Joe Stump
* @acces public
* @return void
*/
function Base()
{
$this->PEAR();
if(get_class($this) == ‘base’)
{
$this = new PEAR_Error(‘Base is an abstracted class!’);
}
else
{
$this->db =& DB::connect(BASE_PEAR_DSN,true);
if(DB::isError($this->db))
{
$this = new PEAR_Error($this->db->getMessage());
}
else
{
$this->db->setFetchMode(DB_FETCHMODE_ASSOC);
$this->log =& Log::factory(‘syslog’,’Base’);
}
}
}
/**
* Base Destructor
*
* Just add a ‘_’ to your class’s name and voila! you have a PEAR
* controlled destructor!
*
* @author Joe Stump
* @access public
* @return void
*/
function _Base()
{
if(!DB::isError($this->db))
{
$this->db->disconnect();
}
}
}
?>
There are a few things that you will notice that are quite a bit different from
your average PHP class. The first is it’s documented! No, just kidding, we all
document our code. Jokes aside you’ll notice that all we really needed to do
to make our class a true PEAR class is extend it from PEAR and make sure we
had a properly named destructor.
There is a side note on destructors. When you create an instance of any PEAR
based class you MUST assign by reference, meaning =& and not just =. If
you do not assign by reference then your destructors will NOT run!
PEAR Primer
The PEAR_Error Class
The PEAR_Error class is really easy to use. It’s a very basic class that serves
a very basic purpose, error checking and error reporting. I now return nothing
but PEAR_Error’s in my classes, instead of using true/false. Why? I’m not only
able to tell easily if my class is in error, but I’m also able to pass along
an error message for easy debugging. You’ll note in the above class that I assign
a new instance of PEAR_Error to $this when an error occurs. This means that
if an error occurs while connecting to the database the class that you originally
created as an instance of Base will now be an instance of PEAR_Error, not allowing
you to do any Base related operations on that instance. Here’s a quick example:
$base =& new Base();
if(Base::isError($base))
{
echo $base->getMessage();
}
?>
The above code would create an instance of Base, but since Base is an abstracted
class it would error out with “Base is an abstracted class!” There
are three main things to note about PEAR_Error when you are using it. The first
is that the constructor takes your error message as its first parameter. The
second is that the function isError() is part of the PEAR class and can be used
in any child class with the scope operator ::. The last is the getMessage()
function is used to get your error message. But since the classes we write are
perfect we won’t be needing this, will we?
could write two or three articles on the DB class included in PEAR, which is
why I’ll only briefly cover its use here. I use it flawlessly with MySQL and
thus far my only complaint is that it uses sequence tables instead of merely
returning the insert id in MySQL’s auto_increment fields.
require_once(‘Base.php’);
$db =& DB::connect(BASE_DEFAULT_DSN,true);
if(!DB::isError($db))
{
$db->setFetchMode(DB_FETCHMODE_ASSOC);
$sql = “SELECT *
FROM table
WHERE foo=’bar'”;
$result = $db->query($sql);
if(!DB::isError($result) && $result->numRows())
{
while($row = $result->fetchRow())
{
echo ‘<li>’.$row[‘foo’].”
“;
}
}
}
else
{
echo $db->getMessage();
}
?>
This is the most basic of examples on how to use the DB class, but those of
you who have used Perl’s DBI or the many PHP database functions will note the
structure of connecting to a database is pretty much intact. One thing to note
is that the result is a separate class from the DB class with its own functions,
etc. For a more detailed overview of the DB class check out http://pear.php.net/manual/en/core.db.php.
The PEAR Log Class
I like logging certain errors and information straight to syslog, like failed
logins to my site’s admin section. It’s also a less intrusive way to debug your
code. The PEAR Log class lets you log to syslog, your own log files, SQL databases,
and more. It also has some more sophisticated features that let you “attach”
classes to logs and then “notify” those classes when things are logged.
I have yet to tackle those features, but they sound interesting. I’m going to
build on the PEAR DB example to incorporate the Log class.
require_once(‘Base.php’);
$log = Log::factory(‘syslog’,’My App’);
$db =& DB::connect(BASE_DEFAULT_DSN,true);
if(!DB::isError($db))
{
$db->setFetchMode(DB_FETCHMODE_ASSOC);
$sql = “SELECT *
FROM table
WHERE foo=’bar'”;
$result = $db->query($sql);
if(!DB::isError($result) && $result->numRows())
{
while($row = $result->fetchRow())
{
echo ‘<li>’.$row[‘foo’].”
“;
}
}
else
{
$this->log($sql);
}
}
else
{
$log->log($db->getMessage());
}
?>
Now tail your syslog and run the above example. You should find that there are
some errors. It includes all sorts of date information and the identifier (the
second argument in the constructor) you assigned your log. You can alternately
use your own log files by using the type ‘file’ instead of ‘syslog’.
Putting It All Together!
Now that you basically know how to use the main PEAR classes and we’ve built
our own base class, which is based on PEAR, we can put it all together in a
User class for our site!
require_once(‘Base.php’);
/**
* User class
*
* A class to retrieve user information and perform basic
* user related tasks.
*
* @author Joe Stump
*/
class User extends Base
{
/**
* @author Joe Stump
* @access public
*/
var $userID;
/**
* @author Joe Stump
* @access public
*/
var $data;
/**
* User constructor
*
* @author Joe Stump
* @access public
* @param int $userID
* @return void
*/
function User($userID = 0)
{
$this->Base();
if($userID)
{
$sql = “SELECT *
FROM users
WHERE userID=’$userID'”;
$result = $this->db->query($sql);
if(!DB::isError($result) && $result->numRows())
{
$this->data = $result->fetchRow();
$this->userID = $userID;
}
else
{
$this = new PEAR_Error(“Invalid userID: $userID”);
}
}
else
{
$this->userID = 0;
$this->data = array();
}
}
/**
* isAdmin
*
* Check to see if the userID is in our admins table
*
* @author Joe Stump
* @access public
* @return bool
*/
function isAdmin()
{
$sql = “SELECT *
FROM admins
WHERE userID='”.$this->userID.”‘”;
$result = $this->db->query($sql);
if(DB::isError($result) && $result->numRows())
{
return true;
}
$this->log->log(‘Failed isAdmin() with userID ‘.$this->userID.’!’);
return false;
}
/**
* User Destructor
*
* @author Joe Stump
* @access public
* @return void
*/
function _User()
{
$this->_Base();
}
}
?>
As you can see from the above example once you have an instance of your User
class you have everything you need to start manipulating data and doing some
advanced debugging. Probably the best thing about working with PEAR is that
it makes debugging your classes a lot easier.
wrap things up I’ll finish with a simple example of using the User class from
within your site.
require_once(‘User.php’);
$user =& new User($_COOKIE[‘userID’]);
if(!Base::isError($user) && $user->isAdmin())
{
echo “You are an admin!”;
}
else
{
echo “Go AWAY!”;
}
?>
One thing I started to notice the more I used PEAR was that I ended up having
a lot less code in my final application. This is a great thing about OOP. Once
you get your base class working correctly you know it’s always going to work
and errors you encounter will most likely be in your very small final application.
To boot your code is abstracted and more portable.
Related Materials
? http://pear.php.net
? http://phpdocu.sourceforge.net
About the Author
Joe Stump is in his last semester at Eastern Michigan University studying Computer
Information Systems. He enjoys creating complex web applications using PHP,
MySQL, and XML. You can find him at http://www.joestump.net or email him at
joe (at) joestump (dot) net.