Efficient handling of code can make a big difference; for example when
administrating more than one PHP application, or several installations
of the same application, or simply to aid the distribution of several
PHP applications using the same base libraries.
Specifically, this article will look at packaging of your own PHP libraries
using the PEAR (http://pear.php.net)
package format, and your own PHP extensions using PECL (http://pecl.php.net). Basic PEAR and PECL familiarity is assumed, but
most of the features discussed in this article will be explained.
You will need PHP5 if you wish to put into practice the discussion on PECL packaging.
(We shall be looking at it through using the PECL package PECL_Gen, which is dependent on
PHP5.) The discussion on PEAR packaging, on the other hand, applies equally to either
PHP4 or PHP5.
Reusing PHP Code
The value of efficient code reuse is not a new idea to coding in general, or to PHP.
Various degrees of efficient code reuse can be achieved through libraries, classes,
object oriented programming, etc. However, an important aspect of code reuse is the
management and accessibility of these libraries.
PEAR is a good example of reusable PHP code; its name is an acronym for “PHP Extension
and Application Repository”. PEAR is a collection of packages, and is installed in a
central location accessible to any PHP code running on that machine.
We shall briefly examine how PEAR packaging works and look at how we can create our
own PEAR-style packages that are easily ported and installed centrally alongside the
other PEAR packages.
This article has been inspired by the recent conversion to PEAR packages of Horde’s
(http://horde.org) set of core
libraries, to form what is now called the Horde Framework. Taking this idea further,
we shall also look at how your own extensions to PHP could be built and packaged,
allowing for portable custom PHP functions.
We should consider first of all the concept of packages. These could be defined as a
logical group of libraries with common functionality. This does not necessarily mean
that code packages have to be dependent one upon the other, although packages can be
defined which require other packages. For example, it may not make much sense clumping
together as one package a set of text formatting libraries with a set of file system
libraries. However, if necessary, one could be defined as dependent upon the other
and kept as two separate packages.
PEAR Packaging of Libraries
Advantages of PEAR Packages
Other than creating an easily accessible central repository of libraries for code reuse,
one major advantage of library packaging is the simplification of product release management.
Separating out the core libraries from a product, and releasing them as packages, means
that a single bug fix in a library will not prompt the generation of a whole new product
release; you could simply release a new version of the single library package concerned,
and leave your product unchanged.
Packaging requires some thoughtful division of functionality, so the end result could
actually give you the hidden bonus of more portable and cleaner code.
PEAR Package Definition
PEAR uses a file named package.xml for defining the structure of the package and associated files.
It is an XML file and follows a specific format. The start to the file gives some instructions
on how the file will be structured. These are pretty much fixed and should be the same for
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE package SYSTEM "http://pear.php.net/dtd/package-1.0">
DOCTYPE tag specifies the DTD structure of this XML file. You can find the exact DTD here:
in which you can see all the possible elements and attributes.
We shall first take an overview of the structure of this XML file. Depending on how you plan
to use your packages, not all of the following elements will be required, but it is a good
idea to get acquainted with what is available:
- package – defines the package with a required version attribute.
- name – the name of the package.
- summary – a one line summary of the package.
- description – a longer description outlining what the package is about.
- license – the license under which this package is released.
- maintainers – a list of persons associated with the package.
- maintainer – details about a person associated with the package.
- user – a username to associate with this maintainer.
- role – what this maintainer’s role is with the package, eg. lead, developer, etc.
- name – the maintainer’s full name.
- email – a contact email address for the maintainer.
- maintainer – details about a person associated with the package.
- release – this is the main section defining the current release of the package.
- version – the current version of this package.
- state – allows for a state to be assigned and then filter for it during PEAR package operations, currently recognized states: stable, beta, alpha, devel and snapshot.
- date – the date of this package release.
- notes – any notes regarding this release.
- filelist – this is the section defining the files that are part of this package.
- file – a definition of a file which is part of this directory, we shall look at its attributes later.
- dir – defines a new directory as part of the package and can contain further nested directory and file definitions.
- deps – a list of dependencies for this package.
- dep – a dependency definition which we will examine in detail later.
- changelog – the classic changelog using the release element and sub-elements.
We shall not cover the most obvious elements of the package.xml file, but concentrate on two
crucial sections: the <filelist> and the <deps>.
You can look at existing PEAR packages for examples of package definition elements, for example:
The <filelist> Section
This section is important to the whole package definition, as it describes where the
package files are to be installed. The way that you subsequently include your class files in
applications would obviously need to match the way this section is defined.
The sub-element of the <filelist> section can have any combination of <file>
and <dir> elements, and include nesting of one or more <dir> or <file>
elements within a <dir> element. For example, the following describes a file,
Widget.php, that is part of this package:
<file role="php" baseinstalldir="/">Widget.php</file>
role attribute describes the kind of file and hence the directory structure
it should be installed into. Possible choices are:
- php – a regular PHP source file which would be installed to the PEAR include directory.
- ext – a PHP extension file which would be installed to the PHP extensions directory or to the directory specified by PHP_PEAR_EXTENSION_DIR setting.
- doc – a documentation file, installed to [PEAR docs directory]/PackageName/
- test – a test file, installed to [PEAR tests directory]/PackageName/
- data – a data file, installed to [PEAR data directory]/PackageName/
- script – a script used with this package which would be installed to the PHP binary directory or to the directory specified by PHP_PEAR_BIN_DIR setting.
- src/extsrc – C or C++ source which is not installed itself but is used to build an extension.
The location of php role files can be further fine-tuned using the
In the above example, the file Widget.php is to be installed in the root of the PEAR directory.
You could specify any other directory in this attribute, relative to the root.
Note that when nesting files within a <dir> section you can imply some <file> attributes
by stating them in the <dir> element, for example:
<file role="php" baseinstalldir="/">Widget.php</file>
<dir role="php" baseinstalldir="/Widget">
In the above example, the files foo.php and bar.php have an implied php role,
and the base install directory is now “Widget” in PEAR’s root.
The <deps> Section
Although the idea may be to create self-enclosed packages of functionality, package code
often relies on certain external functions or factors. You can specify not only other
packages as dependencies, but a vast range of conditions.
Consider the following example of hypothetical dependencies for our Widget package:
<dep type="php" rel="ge">4.3.0</dep>
<dep type="ext" rel="has">gettext</dep>
<dep type="pkg" rel="has" version="1.2">HTTP</dep>
What this section is stating is that the Widget package has to have a PHP version
greater than or equal to 4.3.0, PHP has to have the gettext extension, and a
specific version, 1.3, of the HTTP package needs to be installed.
There are several possible values for the
type attribute; however, the three types
primarily used are:
- pkg – the dependency is another PEAR package.
- php – the dependency is a specific PHP version.
- ext – the dependency is a PHP extension.
rel attribute defines the relationship that is to be used for the
check, and the following values are available:
- has – contains.
- eq – is equal to.
- lt – is less than.
- le – is less than or equal to.
- gt – is greater than.
- ge – is greater than or equal to.
The <deps> tag could also have an additional attribute
if left out, is assumed to have a value of “no”. If the value “yes” is specified, the dependency
is no longer absolutely required, but only a recommendation which may enhance the
functionality or features of the package.
Check that you have included all your dependencies. As good practice, ensure also
that they have been reduced to the necessary minimum. Look through your code and
see if any dependencies could be resolved differently, perhaps by structuring packages
in a different way, or by using a different approach.
You should finally make sure that your package.xml file is valid. Fortunately PEAR has
a utility within its
pear command to do just that. Run the following code on your
newly created package.xml:
pear package-validate package.xml
If any errors are reported go back to your package.xml file and fix them, otherwise
you are all set to install your package.
Now that you have a valid package you can again turn to the pear command for installation:
pear install package.xml
This will use the information in the package.xml file to install your libraries to the
default PEAR directory. You can add some variations to this command using some extra flags:
- –force – to force an installation of a package ignoring any existing versions already
installed. This is useful in situations when a package is updated but the version number
is not incremented.
- –nodeps – do not check for dependencies when installing this package and just install
this single package.
As a further note, you can automate the install of a large number of packages using a simple
script to crawl a set of directories and execute
pear install on each package.xml
it finds. For an example of such a script, look at how this was handled by Horde at
Since the PEAR directory is by default in the PHP include path, you can easily include
your custom PEAR package in your applications as follows:
PECL Packaging of Custom Extensions
Using the PECL package PECL_Gen (http://pecl.php.net/package/PECL_Gen) and an XML file, similar to the PEAR
packaging one above, we can define how our custom PHP extension is to be built.
As mentioned earlier, PECL_Gen is dependent upon PHP5, so if you have access to only PHP4
you may want to take a look at the ext_skel script for creating your own custom
extensions to PHP. PECL_Gen itself works using only default PHP functions, and this is important
for the portability of your extension. Wherever you have PHP 5 installed, you can run PECL_Gen
and create a ready to compile PHP extension.
PECL Extension Definition
Unlike the PEAR package definition file package.xml, the PECL extension definition
filename is arbitrary, since it is specified when calling the PECL_Gen script. You can see
the full DTD here: http://cvs.php.net/cvs.php/pecl/PECL_Gen/extension.dtd. Note that it has a
similar base structure to PEAR’s package.xml, including summary, description,
maintainers, release and changelog sections.
We shall briefly look over the main sections to give you an overview. However, you should
look at the PECL_Gen manual for an in-depth guide to putting together a PECL extension
definition file: http://cvs.php.net/cvs.php/pecl/PECL_Gen/manual.html.
The <deps> Section
PECL_Gen allows you to specify language and platform dependencies for an extension, as well
as where to look for libraries and headers, which libraries and optionally functions within
those libraries are required, and finally any needed header files and how to include them.
The <resources> Section
Resources contain any PHP resource types for your extension, and their payloads. The
alloc attribute defines whether PHP should allocate and free the payload.
If PHP is not allocating the payload you can include some C code in the <destruct>
element to do the cleaning up.
The <functions> and <code> Sections
Extension functions are defined using one or more <function> elements within the
<functions> section. These functions can be one of the following types:
- public – available to any PHP code.
- internal – used by the PHP extension API and can be one of the following:
- MINIT() – module initialization function, called once at PHP module startup.
- MSHUTDOWN() – module shutdown function, called when the PHP module is properly terminated.
- RINIT() – request initialization function, called before a PHP script request.
- RSHUTDOWN() – request shutdown function, called when the PHP script has finished.
- MINFO() – function which provides the information for the phpinfo() call.
- private – static C functions used within the extension.
Public functions can have the <summary> and <description> included, which
would be used for DocBook XML generation. Using the <code> element within
<function> we can specify our function code.
The <code> section outside of <functions> will insert additional
code into your source files, either in the code file using the
attribute or into the header file using
role="header", both of which
can be further controlled using the
position attribute to indicate
an insertion at the “top” or “bottom” of the file.
The <constants> Section
This section permits the setting up of extension constants using a list of <constant>
elements, which include a
attributes. Furthermore, when parsing the constants list, PECL_Gen will use any content
in the <constant> tag for DocBook XML generation.
The <globals> Section
Globals that need to be defined are php.ini directives, and, to ensure thread
safety and initialization, variables which are only global to a specific request.
Global variables that are global throughout the entire extension are simply defined in
the <code> element using regular C code.
Running the Script
With your extension.xml file you can use the PECL_Gen script to generate the
code ready for compiling:
You should now be able to produce your own packages, which you can easily snap into place
alongside the regular PEAR and PECL packages.
You can follow PEAR and PECL developments either through their respective web sites or
About the Author
Marko Djukic works and lives in Florence, Italy, running his own company,
The Oblo Project, with the
goal of bringing innovative Open Source solutions to local government and
SMEs. He is also a core developer for the Horde Project at http://horde.org.