How to Publish Your Own PEAR Channel

I recently revisited PEAR, after several years away in the land of Composer, with a view to once more distributing some of my PHP libraries using the PEAR system.

In this article I describe the steps that I took to create my own PEAR channel, generated by the awesome Pirum and served freely from GitHub Pages.

What is PEAR?

The PHP Extension and Application Repository (PEAR) is a curated collection of PHP libraries and applications. There are over 400 packages in the PEAR repository, a relatively small number because each package is intended to offer a single, standard, universal solution to a generic problem or function, such as authentication, logging and testing.

PEAR packages are downloaded and installed from the official PEAR repository using the pear command line tool, known as the PEAR package manager. But since version 1.4 of the PEAR package manager, you can use it to download and install third-party packages that are shipped through other PEAR-compatible repositories – called PEAR channels. It means that all sorts of PHP libraries and applications can now be managed using the PEAR package manager, not just the limited collection of official PEAR packages.

PEAR vs Composer

Recently, Composer has emerged as the new de facto package management tool for PHP. It is easy to understand why. Composer is better at dependency management, packages are easier to configure and maintain, and adding packages to Composer's official repository does not involve a lengthy application and approval process, as is required for submissions to the official PEAR repository. All that is required to make PHP packages available for install via Composer is for the source code to exist in a distributed version control system such as Git. Job done, already.

Plus, Composer packages are installed on a project-by-project basis rather than globally, which tends to better suit application developers.

Nevertheless, I believe that PEAR still has a role to play in modern PHP application development, if a diminished one. It's still the best way to install PECL extensions. And in some circumstances the PEAR system could be a better choice than Composer for distribution of PHP source code. For example, with PEAR an application's dependencies can be installed at the system level as extensions to PHP's native API, rather than shipped with the application itself. In large-scale application development there are certainly efficiencies to be found in removing dependency management from the application level and elevating it to the infrastructure level. Large organisations can also use PEAR to distribute proprietary domain-specific packages via private PEAR channels.

To distribute PHP components using the PEAR system, all you need is your own bespoke PEAR channel. And it's very easy to make one of those thanks to free tools like Pirum and free hosting services like GitHub Pages.

Install the PEAR package manager

The first step is to install the PEAR package manager. The following instructions are correct for Windows hosts, but will be similar for macOS and Linux.

Go to http://pear.php.net/go-pear.phar and save the PHAR file to PHP's installation directory. Change to that directory and run the go-pear.phar program.

cd C:\WAMP\php\7.0.11
php go-pear.phar

You will be asked: "Are you installing a system-wide PEAR or a local copy?" Type "local" and hit Enter. Confirm by typing "yes".

You will be shown a list of locations where PEAR's files will be installed. Press Enter to accept all of the default locations.

When prompted, type "Y" to confirm that you want PHP's include_path setting to be updated automatically. Doing so means you will be able to include PEAR and PEAR-compatible packages in any PHP application using require_once(). You will need to restart your web server so that changes made to php.ini will come into effect.

On success you will be told that "the pear command is now at your service...". Assuming that you already have added PHP's installation directory to the Windows system-level path variable, you will be able to use the pear and pecl commands globally from the Windows command line. Try running the pear command. If everything has been installed correctly, the response will be a list of available sub-commands.

pear

Check that PEAR packages can be included in any local PHP application by running the following script:

require_once 'System.php';
var_dump(class_exists('System', false));

System.php is shipped with every PEAR installation so it should now be available to your local PHP applications. The output of this script should be:

bool(true)

If the output reads "require_once(System.php): failed to open stream", you will need to fix the include_path setting in PHP's php.ini config file. It needs to point to your local PEAR directory. Example:

include_path=".;C:\WAMP\php\7.0.11\pear"

Install Pirum

PEAR channels are XML RPC APIs. The PEAR specification requires PEAR channels to serve a file called channel.xml, which defines the channel and exposes all of its endpoints. When you subscribe to a PEAR channel via the PEAR package manager command line tool, the tool reads the channel's channel.xml file to get a list of packages available to download from the channel.

If you have time, you could write your own PEAR channel from scratch. This IBM DeveloperWorks article explains how. But there is an even easier way. You can use Pirum, a static-site generator dedicated to making PEAR channels and written by the industrious Fabien Potencier.

Use Pirum to generate the static files for your PEAR channel, push them to GitHub Pages, and you'll have yourself a freely-hosted PEAR channel serving the world in just an hour or two.

Pirum itself can be downloaded using PEAR. Run the following commands with administrator privileges.

pear channel-discover pear.pirum-project.org
pear install pirum/Pirum

You should now have an executable called pirum. Try it.

pirum

Create a Github repository

Because Pirum will build your PEAR server as a series of static files, GitHub Pages is a great way of hosting your PEAR server – for free. All you need to do is create a public Git repository to host your PEAR server.

On GitHub, create a new Git repository. Then, locally:

git init
git remote add origin [email protected]:<username>/<repository>.git

Replace "<username>" with your GitHub username and "<repository>" with the name of the remote repository that you created on GitHub. Add an empty file to the root directory of your local repo, and commit it.

git add -A
git commit -m "Initial commit."

Rename your master branch to gh-pages and push it to GitHub. Anything pushed to branches of this name, GitHub will automatically publish on github.io, the home of GitHub Pages.

git branch -m master gh-pages
git push origin gh-pages

Create a custom hostname

The URL for your PEAR channel will be "http://<username>.github.io/<repository>", where "username" is your GitHub username and "<repository>" is the name of your repository. Better still, you can use a custom domain to serve your PEAR channel, for example "pear.example.com" where "example.com" is a top-level domain name that you control.

There are two steps.

First, from your domain registrar's control panel, add a DNS record to point your custom hostname to "<username>.github.io". Example:

Second, tell GitHub about your custom GitHub Pages domain name, so it knows where to route requests to that hostname. Go to GitHub.com, navigate to the remote repository for your PEAR channel, click Settings, then under GitHub Pages enter your custom hostname, e.g. "pear.example.com".

Create a Pirum configuration file

In the root directory of your new local Git repo, create a file called pirum.xml. Pirum will use this file to build your PEAR channel.

Add the following content to the pirum.xml file.

<?xml version="1.0" encoding="UTF-8" ?>
<server>
    <name>pear.example.com</name>
    <alias>mypearchannel</alias>
    <summary>My PEAR channel server</summary>
    <url>http://pear.example.com</url>
</server>

Swap "pear.example.com" for the fully-qualified domain name from where you will serve your PEAR channel. The alias is a short name for your channel that will be used to refer to the channel via pear commands. Also include a short summary and the full URL path to the PEAR channel.

Now, to build your PEAR channel server, from the repository's root directory:

pirum build .

Pirum will generate all of the files needed to serve your PEAR channel. All you need to do now is stage, commit and push all of these files to the remote repository in the normal way.

Go to http://pear.example.com (replacing the domain name with your PEAR channel's custom hostname). You should see an HTML page that describes your PEAR channel, with instructions to register the channel and to list and install its packages. Try it out for yourself:

pear channel-discover pear.example.com
pear channel-info mypearchannel
pear list-all -c mypearchannel

Add packages to your PEAR channel

Now you've got your own PEAR channel, you can use it to distribute your PEAR packages.

PEAR-compatible packages are built from a configuration file called package.xml. It is known as the package definition file.

Compared to Composer's composer.json file, PEAR's package.xml is verbose and convoluted, and the documentation is not much help either. There's a command line utility to help generate PEAR package definition files, but I couldn't get the damn thing to work.

No matter. After a bit of trial and error, it's not actually that difficult to write and maintain package.xml files per hand. Here's a template. This contains the bare minimum of information needed for the file to be valid. It is compatible with version 2.0 of the package definition file schema.

<?xml version="1.0" encoding="UTF-8"?>
<package version="2.0" xmlns="http://pear.php.net/dtd/package-2.0"
    xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
    http://pear.php.net/dtd/tasks-1.0.xsd
    http://pear.php.net/dtd/package-2.0
    http://pear.php.net/dtd/package-2.0.xsd">
    <name>helpers</name>
    <channel>pear.example.com</channel>
    <summary>A collection of standalone "helper" functions for everyday tasks.</summary>
    <description>This package contains generic utility functions for working with strings, arrays, and other primitive types.</description>
    <lead>
        <name>Kieran Potts</name>
        <user>kieranpotts</user>
        <email>[email protected]</email>
        <active>yes</active>
    </lead>
    <date>2016-12-04</date>
    <time>12:00:00</time>
    <version>
        <release>1.0.0</release>
        <api>1.0.0</api>
    </version>
    <stability>
        <release>stable</release>
        <api>stable</api>
    </stability>
    <license uri="https://mit-license.org/">MIT License</license>
    <notes>No release notes.</notes>
    <contents>
        <dir name="/">
            <file name="Vendor/Helper/Arr.php" role="php"></file>
            <file name="Vendor/Helper/Str.php" role="php"></file>
        </dir>
    </contents>
    <dependencies>
        <required>
            <php>
                <min>7.0</min>
            </php>
            <pearinstaller>
                <min>1.4.0</min>
            </pearinstaller>
            <package>
                <name>exceptions</name>
                <channel>pear.example.com</channel>
                <min>1.0.0</min>
                <max>1.999.999</max>
            </package>
        </required>
    </dependencies>
    <phprelease></phprelease>
    <changelog>
        <release>
            <version>
                <release>1.0.0</release>
                <api>1.0.0</api>
            </version>
            <stability>
                <release>stable</release>
                <api>stable</api>
            </stability>
            <date>2016-12-04</date>
            <license uri="https://mit-license.org/">MIT License</license>
        </release>
    </changelog>
</package>

You will need to change some of the information contained in this file. Pay particular attention to the following elements:

  • The package <name> must not contain any spaces. It is conventional to use the underscore character to represent namespaces, so "Package\SubPackage" becomes "Package_SubPackage". I tend to use lowercase package names – "package_subpackage" – for consistentency with PEAR channel aliases.
  • The <channel> must match exactly the full-qualified domain name of the channel on which the package is to be distributed. Example: "pear.example.com".
  • All files listed under <contents> will be bundled in the package.
  • Allowed <stability> values are "alpha", "beta" and "stable".

You do not need to include all of your project's files under <contents>. For example, it is not necessary to ship tests with your PEAR packages, though many package authors do. Here's how you reference documentation within the <contents> block.

<!-- documentation -->
<file name="CHANGELOG.md" role="doc"/>
<file name="CONTRIBUTING.md" role="doc"/>
<file name="CREDITS.md" role="doc"/>
<file name="LICENSE.txt" role="doc"/>
<file name="README.md" role="doc"/>

Save your package.xml file to the directory that contains your project's files, so that the <contents> described in the package definition file are relative to it. In my projects, package.xml sits in the src directory alongside the source code proper.

Validate the package definition file

Check that the package definition file is valid. Run the following command from the directory that contains the package.xml file:

pear package-validate

You might get some warnings, but as long as you don't get any full-blown errors, you are good to go.

Build the PEAR package

To make your PEAR package, run the following command. This will create a .tgz file that's named after the package name and its version number, as defined in package.xml. An example of a PEAR package would be helpers-1.0.0.tgz.

pear package

Test installation of the package locally

Before publishing the package to your public PEAR channel, you should do a local installation to check that the package works as expected. Run the following command, replacing the filename as appropriate.

pear install helpers-1.0.0.tgz

If you get an "install ok" response, you should be able to start using your package from PHP right away. Try it. Remember, PEAR packages need to be explicitly included, since they are not autoloaded.

<?php
    require_once 'Vendor/Helper/Arr.php';
    var_dump(class_exists('\Vendor\Helper\Arr', false));

Incidentally, the files will be installed in PHP's pear directory. Mine is at C:\WAMP\php\7.0.11\pear. The files will be organised in the same directory structure as specified in your package's definition file.

Uninstall the test package using the pear uninstall command:

pear uninstall channel://<channel_name>/<package_name>-<package_version>

Example:

pear uninstall channel://pear.example.com/helpers-1.0.0

Publish the package to your PEAR channel

Now that you know your package works, you can distribute it via your public PEAR channel.

Copy the packaged .tgz file to the root directory of your PEAR channel's Git repository on your local machine. Navigate to that directory and use pirum to add the package to the channel.

pirum add . <filename>.tgz

You can now delete the package file from the root directory. Pirum will have copied it to the get directory in your PEAR channel's generated build code.

Use git to upload the changes to your remote Git repository. Remember to push to the gh-pages branch so that the changes get published to GitHub Pages.

git add .
git commit -m "Added helpers-1.0.0"
git push origin gh-pages

Navigate to your public PEAR channel on GitHub pages (e.g. http://pear.example.com/) and you see the new package in the channel's list of available packages.

Installing the package from your PEAR channel

Finally, check that you can install the package from your PEAR channel using the pear command line utility.

If you have not done so previously, tell pear to "discover" your PEAR channel.

pear channel-discover <channel_name>

If you've done that already, update it:

pear channel-update <channel_name>

Example:

pear channel-update pear.example.com

List all of the packages in the channel:

pear list-all -c <channel_alias>

Example:

pear list-all -c mypearchannel

Your newly published package should be included in your channel's list of available packages. To download and install it:

pear install <channel_alias>/<package>(-<version>)

Example:

pear install mypearchannel/helpers

The latest version of the requested package will be downloaded and installed, unless you specified a specific version number.

pear install mypearchannel/helpers-1.0.0

Try using your new PEAR package.

<?php
    require_once 'Vendor/Helper/Arr.php';
    var_dump(class_exists('\Vendor\Helper\Arr', false));

Removing packages

If you want to remove packages from your PEAR channel, just manually delete the package files from the channel's get directory and then rebuild the channel using Pirum.

pirum build .

Commit and push the rebuilt channel files to GitHub Pages.

Customising your PEAR channel

There's much more that you can do with Pirum to customise your PEAR channel, including implementing a bespoke design. See the Pirum documentation.