This article is a general introduction to PEAR.
The PEAR Installer
To install PEAR packages you need the installer. The PEAR installer keeps track of what packages you have installed, your configuration and of course it lets you install, upgrade and uninstall packages.
The installer is part of the PEAR core package, and will be installed by default from PHP 4.3.0 on Unix, and will be included in the upcoming 4.3.1 release for Windows. If you don't have it installed for some reason, a quick and simple way of getting it is to use go-pear.

go-pear
Windows users with PHP 4.3.0 must run this command:
C:\> cli\php -n -r `include http://go-pear.org;'
If you have an earlier version, open
go-pear.org in your browser, save the file somewhere and run it manually:
C:\PHP> php -q go-pear.php
From this point,
go-pear is supposed to be self-explanatory, so we continue assuming that you succeed in installing PEAR on your machine. Verify that the installer is installed by running this command:
$ pear -V
PEAR Version: 1.0.1
PHP Version: 4.3.0
Zend Engine Version: 1.3.0
If you get command not found instead, you need to read the last lines of the go-pear output for hints about how to set your PATH environment variable.
The pear command has several sub-commands, similar to cvs. You get a list of them with pear help.

pear help
You may also get detailed help for each command with pear help
command. For lazy typists, pear help shortcuts is useful.
Okay, let's get on with it, and take a look at which packages were installed by
go-pear:

pear list
Here, the package name is in the left column, the installed release in the middle column, and the state, or release quality, of the package in the right column. By default, the installer will prefer stable packages, but you may override this by setting the preferred_state config parameter. See the Installer Configuration section at the end of the article for details. Version numbers also have a meaning in PEAR, and we will learn about the version numbering semantics a bit later.
Preparing for Work
We are going to use a number of PEAR packages in the following examples, so let's demonstrate how to use the installer simply by installing what we need.
C:\PHP> pear install DB
downloading DB-1.3.tgz ...
...done: 59,332 bytes
install ok: DB 1.3
C:\PHP> pear -q install Mail
downloading Mail-1.0.2.tgz ...
...done: 12,287 bytes
install ok: Mail 1.0.2
C:\PHP> pear -q install HTTP
HTTP already installed
Core Features - Error Handling
An important part of PEAR is providing a common mechanism for error handling. PEAR uses an error paradigm that is based around errors being values of a specific type that can be returned, sent as arguments and so on. The best way to do this in PHP is to use a specific class, so a PEAR error is defined as an object that is_a PEAR_Error. is_a means that an object either is an instance of the class, or an instance of a child class.
The PEAR error handling API provides:
- a way of generating errors (PEAR::raiseError and PEAR::throwError)
- detecting errors (PEAR::isError)
- setting default error handlers (PEAR::setErrorHandling)
- making exceptions from default error handlers (PEAR::pushErrorHandling, PEAR::expectError)
Listing 1 is an example of a script that catches all PEAR-generated errors and handles them by printing an error message and dying.
Listing 1: global default error handling <?php
require_once `PEAR.php';
require_once `DB.php';
PEAR::setErrorHandling(PEAR_ERROR_DIE, Aaaaargh! Error: %s);
$dsn = `mysql://user:password@localhost/db';
$connection = DB::connect($dsn);
print Connection succeeded!<br />\n;
?>
Here, we set PEAR's global default error handling to die and print mode with the PEAR::setErrorHandling() call. We also specify a format string to control somewhat the error message that will be displayed before PHP exits.
Connection succeeded! will only be displayed when the connection actually succeeds. We can boldly assume this because the PEAR_ERROR_DIE default error handler will make PHP exit if the DB::connect() call raises a PEAR error.
The error handling modes available are shown in Table 1.
Table 1: PEAR error handling modes
|
Mode
|
Description |
|
PEAR_ERROR_RETURN
|
Simply return the error object
|
|
PEAR_ERROR_PRINT
|
Print the error message and return the object
|
|
PEAR_ERROR_TRIGGER
|
Trigger a PHP error and return the object
|
|
PEAR_ERROR_DIE
|
Print the error message and die
|
|
PEAR_ERROR_CALLBACK
|
Call a function with the error object as argument
|
|
PEAR_ERROR_EXCEPTION
|
Throw an exception (Zend Engine 2 only)
|
|
You may OR these modes together, if you find any sense in that.
The most flexible error mode is PEAR_ERROR_CALLBACK. It lets you define a function that does the rest. You may choose to exit PHP in this function, or you may choose to keep running. Unless you explicitlyexit in the handler function, PHP will keep running.
Listing 2: Global error handling <?php
require_once `PEAR.php';
require_once `DB.php';
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, `error_handler');
$connection = DB::connect(`mysql://test@localhost/test');
function error_handler(&$obj) {
$msg = $obj->getMessage();
$code = $obj->getCode();
$info = $obj->getUserInfo();
printf(`<h2 style=color:#cc0000>%s (%d)</h2>', $msg, $code);
if ($info) {
print Debug info: <pre>;
print htmlspecialchars($info);
print </pre>\n;
}
}
?>
We may also set default error handlers for each object. These will apply for errors that are raised within that object.
Listing 3: per-object default error handling <?php
require_once `PEAR.php';
require_once `DB.php';
PEAR::setErrorHandling(PEAR_ERROR_DIE, Aaaaargh! Error: %s);
$dsn = `mysql://user:password@localhost/db';
$connection = DB::connect($dsn);
$connection->setErrorHandling(PEAR_ERROR_TRIGGER, E_USER_ERROR);
?>
In this example we are demonstrating two things: the PEAR_ERROR_TRIGGER error mode, and per-object default error handling.
Any error caused by code in the $connection object from now on will trigger a PHP error of the type E_USER_ERROR. Because E_USER_ERROR is one of PHP's fatal errors, this script will also die at the sight of an error. This makes writing code easier because you can fire and forget and don't bother about handling errors. But it also makes your code much less robust, there are a lot of situations where errors should be caught and handled gracefully. There is a way of getting the best of both worlds, with some extra code as shown in listing 4.
Listing 4: using expectError() <?php
require_once `PEAR.php';
require_once `DB.php';
PEAR::setErrorHandling(PEAR_ERROR_DIE, Error: %s<br />\n);
$db = DB::connect(mysql://test@localhost/test);
$db->expectError(DB_ERROR_ALREADY_EXISTS);
$err = $db->query(
INSERT INTO table VALUES(NULL,?),
array($_GET[`name'])
);
$db->popExpect();
if (PEAR::isError($err)) {
print An entry for $_GET[name] already exists!;
} else {
print Ok!;
}
?>
We are still setting up a global default error handler that will make PHP exit at the scent of an error. But before doing the query, we call expectError() in the $db object, instructing it to ignore any default error handlers for errors where the error code is DB_ERROR_ALREADY_EXISTS.
If an error occurs during the DB::connect() call, DB will raise a PEAR error. When errors are raised, the default handler for $this (the object the error was raised within) is checked first, or the global default handler if no object-specific default handler was found.
Emulated Destructors
Since PHP 4 does not offer destructors, the PEAR base class offers a form of destructors that may be used in many cases. What it does is simply to call a destructor method at the end of the request, if you have defined one. All you need to do to activate this is to inherit the PEAR class, call the PEAR class constructor, and have a destructor method in your class (named just like the constructor, but with a leading underscore). See the example in listing 5.
Listing 5 <?php
require_once 'PEAR.php';
require_once 'System.php';
// This is a utility class that you can use to register files that
// you want to be deleted at the end of the request.
//
// Usage:
//
// $janitor = &new Janitor;
// $janitor->rememberFile('one-temp-file.txt');
// $janitor->rememberFile('another-temp-file.txt');
//
// Now you may use "one-temp-file.txt" and "another-temp-file.txt"
// knowing that they will be removed when your script is done.
//
class Janitor extends PEAR
{
var $cache_file;
var $files = array();
function Janitor() {
// must call the PEAR class constructor, or the destructor
// won't be called
parent::PEAR();
}
function _Janitor() {
print "Janitor destructor called!<br />\n";
foreach ($this->files as $file) {
if (file_exists($file)) {
unlink($file);
}
}
}
function rememberFile($filename) {
$this->files[] = $filename;
}
}
?>
So how does this compare to real destructors? The destructors that PHP 5 will have are called when the object is about to cease existing. There is no way to catch this event in PHP 4, so if an object is deleted (with unset in PHP 4), the destructor will not be called immediately.
Actually, since PEAR's emulated destructors store a reference to the object, it won't be deleted even if you unset your only variable with it. PHP 4 will not free a value until there are no references to it. But when the request completes, PEAR has registered a shutdown callback that will call the destructor of every registered object.
PEAR::DB
The DB package is a database abstraction layer for PEAR. In PHP Magazine issue 1.03 Lukas Smith already covered MDB, which is the next-generation database abstraction layer for PEAR. This article will focus on DB, but you should check out MDB if you are looking for truly portable database applications.
Connecting
PEAR DB uses a data source name, or DSN for short, to specify the address and connection parameters for a database. PEAR DB's DSNs are purposely similar to ODBC DSNs, but have a different syntax. The DB DSN format is:
dbtype://user:passwd@databasehost/database
To connect to the database, you pass the DSN as a parameter to DB::connect(), which returns a connection object. If an error occurs, a PEAR error is raised. This means that unless you have configured your default error handler so that PHP exits on errors, you need to check whether you got an error object back:
$conn = DB::connect(mysql://test:pw@localhost/test);
if (PEAR::isError($conn)) {
die(DB::connect failed!);
}
Running Queries
To execute an SQL query, call the query() method:
$result = $conn->query(`SELECT id, name FROM mytable');
The query() method returns a result object, or an error object. Once again, remember using PEAR::isError() if necessary. Now, you can fetch rows from the result set:
while ($result->fetchInto($row)) {
print $row[0] - $row[1]<br />\n;
}
By default, fetches return ordered arrays, that is with numeric indexes starting at 0. You may set the default behaviour to associative arrays instead like this:
$conn->setFetchMode(DB_FETCHMODE_ASSOC);
Our fetchInto() example above would look like this in associative mode:
while ($result->fetchInto($row)) {
print $row[id] - $row[name]<br />\n;
}
There are also some quick methods for getting the results in the same operation as running the query:
$id_to_name = $conn->getAssoc(`SELECT id, name FROM mytable');
The return value here is an associative array with values from the id database field as the key, and values from the name field as the array item's value. If you specify more than two fields in the SELECT, the value will become an ordered array with these fields.
Other utility methods are getOne() for fetching the first field in the first result row, getRow() for getting the entire first row, and getAll() for returning the entire result set in a two-dimensional array. getAssoc() also has a GROUP BY mode that can be very handy.
Abstracted Errors
One of the things that is really tricky when dealing with different PHP database extensions is how each of them reports errors, what messages or codes are returned and so on. PEAR DB takes a simple approach to this and says that every error can be represented by a simple error code.
For example, an SQL syntax error is represented by DB_ERROR_SYNTAX in PEAR DB. In MySQL, this is the error code 1064, in ODBC it's both 37000 and 42000, in Oracle it's 900 and 923, and so on. You get the idea.
The approach taken to this by PEAR DB is to map the error codes per database driver. The effect of this for you as a programmer who wants to use DB, is that you can actually write database-portable code that checks whether your query failed due to a referential integrity constraint or something else.
We have seen examples of this earlier in this article, but here is another one:
$conn->expectError(DB_ERROR_CONSTRAINT);
$conn->query(`INSERT INTO table VALUES(?,?)',
array(42, `Nokia'));
if (PEAR::isError($conn, DB_ERROR_CONSTRAINT)) {
PEAR::raiseError("Need to define entry 42".
"in some other table first!);
}
This example demonstrates two new things: the second parameter to PEAR::isError() which is available from PEAR 1.1. With earlier PEAR versions the same if statement would be:
if (PEAR::isError($conn)
&& $conn->getCode() == DB_ERROR_CONSTRAINT)
{
The second thing demonstrated is how to provide more detail to an error by catching it and re-throwing it with a more specific error message. The error message generated by DB for this error code is simply constraint violation, so specifying errors this way can give users a much better idea of what went wrong.
Placeholder Queries
Placeholder queries are used to separate the actual query and data. In some databases, such as Oracle, the parsed representation of the query is cached, so embedding data in the query itself can have a serious impact on performance. Other reasons for separating the data is that you no longer need to worry about quoting, and of course the warm fuzzy feeling you get when writing clean code.
In DB, every query method except simpleQuery() provides for the use of placeholders. But please note that if you wish to harvest the performance gain, execute() and prepare() is the preferred approach. Some examples are shown in listing 6.
Listing 6 <?php
$query = `SELECT name FROM mytable WHERE id = ?';
// This is the method for returning the first field of the first column:
$name = $conn->getOne($sql, array($id));
// Example of prepare/execute:
$sql = `INSERT INTO mytable (id,name) VALUES(?,?)';
$sth = $conn->prepare($sql);
// let's pretend $data is an array of arrays that you want to stuff
// into your database:
foreach ($data as $row) {
$conn->execute($sth, $row);
}
// plain queries support placeholders, too:
$sql = SELECT * FROM mytable WHERE name LIKE `%?%';
$result = $conn->query($sql, array($search_term));
while ($result->fetchInto($rowobject, DB_FETCHMODE_OBJECT)) {
printf(<b>%d</b> : %s <br />\n, $rowobject->id, $rowobject->name);
}
?>
Finally, there are two more types of placeholders other than the ? format we have seen previously. First, the file reference placeholder:
$file = '/tmp/image.png';
$sql = 'INSERT INTO mytable (id, rawdata) VALUES(?, &)';
$id = $conn->nextId('mytable_id');
$conn->query($sql, array($id, $file));
What happens during the execution of this is that DB replaces the `&' in the query with the contents of the file that is passed as the parameter. The file reference placeholder is basically an implementation of BLOBs (binary large objects), with the limitation of being embedded in the query. This works nicely in some database drivers (notably
mysql and
pgsql), but not necessarily in others.
The other thing demonstrated in this example is PEAR's sequence emulation. A very common question in PEAR-related forums and mailing lists is how to get the last insert id from MySQL. The answer is you don't, because not all databases use this paradigm of being able to retrieve this value after the previous INSERT. Also, not all databases have real sequences, so PEAR DB provides a compromise with emulated sequences. All you need to do is call nextId() with the sequence name as the parameter, and the right thing[tm] will be done depending on what database you are using. Then you can use the return value of nextId() in your subsequent INSERT.
One thing you should be aware of with emulated sequences is that PEAR adds a _seq suffix to the sequence name when creating/accessing the sequence or table used for emulating a sequence. If you want your PEAR DB code to not use this suffix, you can set the seqname_format option in the connection object:
$conn->setOption('seqname_format', '%s');
The parameter is just a printf format string, the example above will use the sequence name directly with no prefix or suffix.
Options
As we just saw, the connection object has a few options that you can configure. You can find a list of them in table 2.
Table 2: Connection options
| Option |
Type / Values |
Default |
Description |
| Persistent |
Boolean |
false |
Whether or not to create a persistent database connection |
| Optimize |
`performance' /
'portability' |
`performance' |
Whether to optimize PEAR DB behaviour for portability or performance
|
| Debug |
Integer |
0 |
Debug level, used by maintainers |
| seqname_format |
format string |
`%s_seq' |
Used to format the name of sequences or sequence-emulation tables
|
| Autofree |
Boolean |
false |
Whether to automatically free results after use |
|
You can specify connection options in two ways:
$conn = DB::connect($dsn,
array(`optimize' => `portability'));
// or after the connection object has been
// created:<br></br>$conn->setOption(`optimize', `portability');
In addition, each DB driver defines which features it supports, either natively or through emulation. A list of those can be found in table 3.
Table 3: Supported features
|
prepare
|
Whether the driver natively supports prepare or variable binding. All drivers provide prepare/execute, but if they do it through emulation, this feature will be false for that driver.
|
|
pconnect
|
Whether persistent connections are implemented for the driver
|
|
transactions
|
True if the database offers native transactions
|
|
limit
|
How LIMIT queries (see next section) are implemented, may be `alter' (driver modifies the query with a LIMIT clause), `emulate' (driver fetches results by row number) or false (driver skips rows). |
|
To check whether a driver supports a given feature, use the provides() method:
if (!$conn->provides('transactions')) {
PEAR::raiseError('This application requires'.
'database transactions!');
}
Limit Queries
The last DB feature we will explore is LIMIT queries. Again, this is a feature that different databases have approached differently, and some not at all. Where native database support for limit is not present, DB will emulate it.
The limitQuery() method is just like query(), with the exception of having from and count parameters between the query parameter and the optional params parameter. See listing 7 for an example.
Listing 7 // display 20 items per page
$sql = `SELECT * FROM products WHERE category = ?';
$result = $conn->limitQuery($sql, $_GET[`from'], 20, array($_GET[`category']));
while ($result->fetchInto($row, DB_FETCHMODE_ASSOC)) {
extract($row);
printf(`<a href=product-detail.php?id=%d>%s</a> $%.2f<br />', $id, $name, $price);
}
The fetchInto() method will stop returning rows when the count limit is reached.
PEAR::HTTP
The HTTP package offers some HTTP utility methods: date(), negotiateLanguage(), head() and redirect(). Here are four quick examples:
HTTP::date() <?php
require_once `HTTP.php';
// use the modification date of this script
// for Last-Modified header
header("Last-Modified: " .
HTTP::date(getlastmod()));
?>
HTTP::negotiateLanguage() <?php
require_once `HTTP.php';
$supported_languages = array(
`en-US' => `U.S. English',
`en-GB' => `Proper English',
`de' => `German',
`sv' => `Swedish',
`no' => `Norwegian',
`ja' => `Japanese'
);
$user_language =
HTTP::negotiateLanguage(
$supported_languages, `en-GB'
);
include $user_language/welcome.html;
?>
HTTP::head()
This example is a Unix shell script that will tell you which web server a site is running under according to the Server: HTTP response header.
#!/bin/sh
exec php -q -d output_buffering=1 $0 $@
<?php
ob_end_clean();
require_once 'HTTP.php';
$url = "http://$argv[1]";
if (isset($argv[2])) {
$url .= ":$argv[2]";
}
$url .= "/";
PEAR::setErrorHandling(
PEAR_ERROR_DIE, "$url: %s\n");
$headers = HTTP::head($url);
if (isset($headers['Server'])) {
print "$headers[Server]\n";
} else {
print "$url: not found\n";
}
?>
HTTP::redirect()
This example is a 404 error handler for Apache for a site that just changed its layout. It takes users from known old pages to the new version of the same page.
<?php
$redirect_map = array(
`/old-page1.php' => `/new/page1.php',
`/old-page2.php' => `/new/page2.php',
.
);
if (isset($redirect_map[$_SERVER[`REQUEST_URI']])) {
HTTP::redirect(
$redirect_map[$_SERVER[`REQUEST_URI']]
);
}
$admin = $_SERVER[`SERVER_ADMIN'];
print "<h1>404 Page Not Found</h1>\n";
print "The page you requested was not found. ";
print "Please contact the webmaster at ";
print "<a href=\mailto:$admin\>$admin</a>";
print " if this is an error.";
?>
PEAR::Mail
The Mail package is popular for composing MIME-formatted emails. PHP's built-in mail() function is very simple, and leaves all the content formatting to the developer. The Mail package takes care of all the hassle involved in, for example, building multipart/alternative messages. See the example in listing 8.
Listing 8 <?php
require_once "Mail.php";
require_once "Mail/mime.php";
PEAR::setErrorHandling(PEAR_ERROR_DIE);
$html = '<h1>HTML-formatted Email</h1>
This is an HTML-formatted email.';
$text = 'This is a plain text email';
$msg = &new Mail_mime;
$msg->setHTMLBody($html);
$msg->setTxtBody($text);
print "Sending mail to $_GET[recipient]...<br />";
$mailer = &Mail::factory('mail');
$mailer->send($_GET['recipient'], $msg->headers(), $msg->get());
?>
This example will send a MIME multipart/alternative message with one text/plain part and one text/html part to the recipient address given in the recipient GET variable. We create a message object from the Mail_mime class, set the HTML and text message bodies, create a mail transport object, and send our message with body and headers through it.
You may also include attachments:
$msg = &new Mail_mime
$msg->setTxtBody('Please find attached the Q3'.
' financial report for Acme Inc.'
$msg->addAttachment('q3.ppt',
'application/vnd.ms-powerpoint');
You may repeat addAttachment() as many times as you want.
PEAR:: XML_Parser
The XML_Parser package is a utility wrapper around the expat extension that is bundled with PHP. It provides an inheritance-based interface, in order to customize it you subclass it. See listing 9 for an example.
Listing 9 <?php
error_reporting(E_ALL);
require_once 'XML/Parser.php';
class MyParser extends XML_Parser
{
function MyParser() {
$this->folding = false;
parent::XML_Parser();
}
function startHandler($parser, $elem, $attribs) {
print "<span style='color:#900'><$elem";
foreach ($attribs as $name => $attrval) {
print " $name='<span style='color:#666'>";
print htmlspecialchars($attrval);
print "</span>'";
}
print "></span>";
}
function endHandler($parser, $elem) {
print "<span style='color:#900'></$elem>";
}
function cdataHandler($parser, $data) {
print "<span style='font-weight:bold;'>";
print htmlspecialchars($data);
print "</span>";
}
function defaultHandler($parser, $data) {
print "<span style=\"color:#009900\">";
print htmlspecialchars($data);
print "</span>";
}
}
$obj = &new MyParser;<br></br>$obj->setErrorHandling(PEAR_ERROR_DIE,
"Parse failed: %s");
$obj->setInputFile('janitor.xml');
print "<pre>";
$obj->parse();
print "</pre>";
?>
The example implements a simple XML markup highlighter, with red element and attribute names, gray attribute contents and black character data.
When subclassing XML_Parser, you may implement handlers for element start/end, character data and the rest (comments, entities, stuff that is not caught by any other handlers). The method name in the XML_Parser subclass is fixed. The parameters passed are exactly the same as in the corresponding PHP XML function, for example the cdataHandler() method gets the same parameters as a callback registered with PHP's built-in xml_set_character_data_handler().
Other handlers that may be implemented are piHandler (processing instructions), unparsedHandler (for unparsed entity declarations), notationHandler (for notation declarations) and entityrefHandler (external entity references). Chances are you will never need these.
The previous used what XML_Parser calls event mode. There is another mode called func mode, where the method that is called for element start/end events is named after the element. For element start events, a method called xmltag_
elementname is called, for element end events a method called xmltag_
elementname_ is called (note the trailing underscore). The other handlers are still called cdataHandler, defaultHandler and so on.
Building PEAR Packages
In this section, we will explore how to build your own PEAR packages, for your own use within your organization only, or for making it public on http://pear.php.net.
We start off by taking a look at the package definition file, usually called package.xml. This file must accompany any source tree that wants to become a PEAR package. Listing 10 shows a very simple example that makes a package out of our previous Janitor example.
Listing 10 <?xml version="1.0" encoding="ISO-8859-1" ?>
<package version="1.0">
<name>Janitor</name>
<summary>
Cleans up temporary files at end of request
</summary>
<description>
This package uses PEAR's emulated destructors
to clean up a list of registered files at
the end of the request.
</description>
<license>PHP License</license>
<maintainers>
<maintainer>
<user>pooh</user>
<name>Pooh Bear</name>
<email>pooh@bear.net</email>
<role>lead</role>
</maintainer>
</maintainers>
<release>
<version>1.0</version>
<state>stable</state>
<date>2003-02-10</date>
<notes>Initial release</notes>
<filelist>
<file role="php" name="Janitor.php"/>
</filelist>
<deps>
<dep type=pkg rel=ge version=1.0>
PEAR
</dep>
</deps>
</release>
</package>
Quick package.xml Element Summary
Due to space considerations, we will not cover every single XML element that may appear in the package.xml file, but rather the mandatory and common ones required to build a plain package. At the end we will briefly mention others, so you will know what else to look for.
Package
The <package> element holds everything together. The version attribute is mandatory and states which version of the package file format the package is in.
The <package> element holds everything together. The version attribute is mandatory and states which version of the package file format the package is in.
<name> (package)
Simply the name of the package. Regular PEAR packages are named with initial+studly caps, with underscores separating name elements for example HTML_QuickForm. For PECL packages (which contain only an extension), the name is all lowercase, for example imagick.
<summary>
A one-line description of the package, used, for example, when presenting many packages on a single screen.
<description>
This is a full description of the package. Newlines will be preserved, and initial whitespace that occurs on every line will be skipped.
<license>
Which license applies to the package. If in doubt, use PHP License.
<maintainers>
Contains a list of maintainer elements, each of which must contain a <user>, <name>, <email> and <role> element. The <user> element within <maintainer> must contain a user name that is registered on the pear.php.net web site. The <role> element describes this user's role in relation to the package, and must be one of `lead' (project manager/lead developer/primary maintainer), `developer', `contributor' or `helper'. Each package needs at least one `lead' since only leads may upload releases.
<release>
The <release> element contains information specific to this release. Information about previous releases may be inside a <changelog> element on the same level, but this is completely optional.
<version>
The version number for this release. PEAR's version numbering scheme is described in detail a little bit later in this article.
<state>
The state of the package which describes how stable the code and API is in this release. Must be one of `stable', `beta', `alpha', `snapshot' or `devel' (in decreasing order of stability).
<date>
The release date.
<notes>
Release notes. Same formatting rules apply as those for <description>.
<filelist>
Here the fun begins. This is the list of files in the package, each represented by a <file> element. If the <file> element is not within any <dir> elements, the file is copied to $php_dir/$name, where $name is the name attribute from the <file> element, and $php_dir is your PEAR install directory (by default /usr/local/lib/php on Unix, or C:\php4\pear on Windows). If you want to copy the file to another directory, you may use the baseinstalldir attribute to specify another directory, for example:
<file role=php baseinstalldir=/HTML name=SuperForm.php/>
If you have a lot of files, and structure them in directories, it's a good idea to use the <dir> element to simplify your package.xml file:
<dir name=HTML>
<file role=php name=SuperForm.php/>
</dir>
In this case, the baseinstalldir attribute is not necessary, because <dir> is applied to both the source and target directories. Any number of <dir> elements may be nested.
Finally, the role attribute describes what kind of file this is, which again dictates where it is installed. The possible values are php (PHP source file), ext (extension shared library file), test (.phpt regression test file), doc (any kind of documentation file), data (any kind of data file), src (extension source code) and script (executable program for command-line use).
We can't go much deeper into this here, but it is recommended to snoop around in existing package.xml files on http://cvs.php.net/cvs.php/pear.
<deps>
The last part of our example package.xml file is the dependencies part. <deps> contains a list of <dep> elements with one dependency each. Every <dep> must be satisfied, or the installer will refuse to install the package. Dependencies have a type which may be one of php (PHP version), pkg (PEAR package), ext (extension), prog (external program), os (operating system) or zend (Zend Engine version). The rel attribute tells what kind of comparison to do, typically ge for checking that the version is greater than or equal to, or has for the existence of something. The version attribute is required when rel wants to compare versions. For php and zend type comparisons, the element is empty, for others it contains the name of the package/extension/etc.
Putting it Together
Now we have two files for our little package: Janitor.php and package.xml. They are all we need to build a package file with the pear package command:
$ pear package package.xml
Package /Janitor-1.0.tgz done
Try listing the contents of this package with pear list. The Install Path listed here is where the installer would put the file on your system.
Now you can install it:
$ pear install Janitor-1.0.tgz
install ok: Janitor 1.0
Congratulations, you have created and installed a PEAR package.
Releasing
So now you have a package tarball, but no-one to share it with. If you want to upload it to http://pear.php.net, there is one rule that you should be aware of.
Discuss the package name on pear-dev@lists.php.net until you get enough support for it (currently, this is defined as 5 +1 votes for your suggestion). If you register a package and start uploading releases without doing this, your package may be removed.
When you are done, or during this naming process, apply for a PEAR account if you do not have one already. Fill in the form at http://pear.php.net/account-request.php, and be clear and explicit about the purpose of your account (for example, I am working on package Foo that is being discussed on pear-dev right now).
When you have agreed with pear-dev about the package name, register your package at http://pear.php.net/package-new.php. Then, after testing that you can install your tarball, upload it at http://pear.php.net/release-upload.php. That's it. After uploading, an announcement email is sent out to pear-general@lists.php.net, and PEAR users can start downloading your package simply by typing pear install
YourPackage.
Package Versioning
PEAR has a version numbering scheme that is designed to allow other software to compare two version numbers for the same package, and say something about how similar those two versions are.
The basic structure of a PEAR version number is a number of elements separated by dots, such as 1.2. There may be any number of such elements, but only the first three are given comparative meaning by the PEAR installer.
Each of the three first elements have a name. For example, in 1.2.0b2, the first 1 is the major version number, 2 is the minor version number and 0 is the patch level. The b2 part does not have a name like this.
PEAR has a guideline for when these elements must change in a new release:
- If a release breaks API compatibility with previous releases, the major version number must be increased.
- If a release is API-compatible, but contains significant new functionality, the minor version number must be increased.
- If only bug fixes or functionality changes are included, only the patch level needs to be increased.
- Major version 0 (zero) means that the package is still experimental.
For you as a PEAR user this means:
- It is risky to rely heavily on packages with a version number that is 0.X, the API might change before 1.0.
- Upgrading to a higher patchlevel only is risk-free.
- Upgrading to a higher minor version is mostly risk free, but do read the release notes for warnings.
- Upgrading to a higher major version will (most probably) raise compatibility issues.
It is the responsibility of each package lead to ensure that these guidelines are followed.
Installer Frontends
The PEAR installer gives you the option of several user interfaces, or frontends as they are called. The default is the command-line installer, which we have used earlier in this article.
For Unix/Linux users (and adventurous Windows users) there is also a Gtk interface to the installer written by Alan Knowles. There is also a Web interface written by Christian Dickmann, but due to space limitations we will only look at the Gtk interface here. Just to wet your appetite:

Gtk Installer Screenshot
Gtk Installer
You need two things to make the Gtk installer work:
php-gtk and the
PEAR_Frontend_Gtk package from PEAR. Unfortunately, PEAR is not yet able to install php-gtk for you, so you need to follow the directions from http://gtk.php.net/ for now.
When you have php-gtk installed, simply run:
$ pear install PEAR_Frontend_Gtk
downloading PEAR_Frontend_Gtk-0.3.tgz
...done: 70,008 bytes
install ok: PEAR_Frontend_Gtk 0.3
$ pear -G
If everything is installed in the right places, you will now see the window above. Please note that the Gtk installer requires access to the Internet, or to the pear.php.net XML-RPC interface to be exact.
Installer Configuration
The PEAR installer can be configured in a pretty flexible way. It is possible to maintain one per-user configuration, one for the entire system in addition to the defaults. These are called configuration layers. All of this is managed through the PEAR_Config class, and we call each setting a
configuration parameter.
To see your current PEAR installer configuration, run pear config-show or pear csh if you prefer the shortcut:

pear show-config
The different *_dir parameters determine where files with different roles go when a package is installed, as we saw in the previous section about building packages.
The php_bin parameter contains the name of the PHP binary that scripts should use.
Preferred_state sets a preference to which releases you download. If you set it to stable, you will have the latest stable release of a package presented to you rather than, for exampl,e the latest beta. If you want to keep up with the very latest versions, set it to alpha or beta. For safe development and production use, keep it as stable.
Another useful setting is http_proxy, which will be used for all communication with the pear.php.net server by the installer, including downloads and RPC requests.
To see what is defined in a specific configuration layer, append the layer name to the pear config-show command:

pear show-config user
Notice the last line here, that says <not set> in the value column. When a configuration layer does not have a value set, the next layer is used as a fallback. The order is user, system, default.
To change a configuration parameter, use the pear config-set command:
$ pear config-set http_proxy my.proxy.com:3128
This will set http_proxy in your user configuration layer, but you may specify system as the third parameter to set it there.
If you want to
remove a parameter from a specific layer, you must use the -u or -U option of pear:
$ pear -u http_proxy -s
This (u)nsets the http_proxy in the user layer, or the system layer if -U (capital U) is used. The -s option stores the user layer, -S stores the system layer.
Plans Ahead
The most important challenge today for PEAR core developers is to make the installer rock-solid on Windows. The installer also needs a lot of work with dependencies, package signatures, binary distribution, download mirrors and better integrity checks when building packages. When the feature set of PHP 5 stabilizes, we will plan and implement a smooth transition to PHP 5, slowly phasing in features such as namespaces, exceptions and aggregates. A lot of emphasis needs to be put on PHP 4 compatibility during the transition period.
Summary
This article's goal was to give you a general overview of PEAR basics. Now, you should be able to install PEAR, install new packages and set up the installer as you like. Hopefully, you should be able to start developing your own packages too, if not for publishing on http://pear.php.net, then to make the maintenance of PHP applications inside your organization easier. Enjoy.
Links and Literature