A step-by-step guide to publishing your own PEAR channels

Build a private channel to distribute PHP packages

The PHP Extension and Application Repository (PEAR) is a collection of PHP packages built to ease the development required to build an application. V1.4 of the PEAR package manager introduced the concept of channels, which are a way to organize and deliver packages that can be installed with the package manager. This tutorial discusses channels, introduces and explains the channel.xml file, and demonstrates how to build a channel for distributing packages. Channels are ordinarily used to expose PEAR packages through the Internet, but enterprises can uses channels to make distribution of enterprise-specific PHP code easy.

Nathan A. Good, Author and Software Engineer, Freelance Developer

Nathan A. Good lives in the Twin Cities area in Minnesota. Professionally, he does software development, software architecture, and systems administration. When he's not writing software, he enjoys building PCs and servers, reading about and working with new technologies, and trying to get his friends to make the move to open source software. He's written and co-written many books and articles, including Professional Red Hat Enterprise Linux 3, Regular Expression Recipes: A Problem-Solution Approach, Regular Expression Recipes for Windows Developers: A Problem-Solution Approach, PHP 5 Recipes: A Problem-Solution Approach, and his latest: Foundations of PEAR: Rapid PHP Development.



30 January 2007

Also available in

Before you start

About this tutorial

This tutorial introduces channels, which you can use for advanced package management in V1.4 and later versions of the PEAR package manager. The tutorial introduces some commands to use with the package manager for discovering, viewing, and deleting channels.

I wrote this tutorial to provide a base on which to build a custom channel server. Although a few packages are available for implementing a channel server, the server requirements may not work in your enterprise. The examples and steps here will get you going so you can implement a server that works for you.

Before embarking, look at the packages you wish to expose on private channels within your company. Consider their design and whether they can be used by people outside your company. The abundance and quality of PEAR packages is amazing, and I encourage broader contribution so everyone can take advantage of nicely written, reusable code. Information about how to contribute packages to the PEAR channel is available (see Resources).

This tutorial uses a scenario in which a contrived company, which has the intranet domain example.net, has decided to build a private channel and publish domain-specific packages to the channel. The package used throughout is called Account, and it presumably contains classes and methods for dealing with accounts in a manner specific to the owner of example.net.

Objectives

After walking through this tutorial, you should be able to use the example channel.xml file and the skeleton xmlrpc.php file -- along with the examples of the hardcoded implementation -- as a base to implement your own channel server.

This tutorial doesn't offer in-depth coverage of building packages, the proper naming conventions for packages and classes, or PHP syntax. Please see Resources for links to those topics.

Prerequisites

You should be familiar with installing and updating PEAR packages. You also need to know the basics of PHP V5, and you should be familiar with editing and creating XML files. If you have a good working knowledge of XML Remote Procedure Calls (RPCs) and using the pear/XML_RPC package, you will find many of the examples in this tutorial easier to understand.

System requirements

To run the examples, you need to have the following installed, configured, and running:

  • A Web server that can run PHP scripts
  • Write access to a document directory on the Web server (e.g., C:\Inetpub\wwwroot or /var/www/)
  • An integrated development environment (IDE) for editing and validating XML, or equivalent tools
  • PEAR V1.4 and later (the version used for this tutorial was 1.4.11)
  • XML_RPC V1.4.0 or later

PEAR overview

What is PEAR?

PEAR is a collection of many packages of PHP code. Each package is designed to offer a solution for a particular problem or function, such as authentication, logging, unit testing, and file or image manipulation. There are more than 400 PEAR packages in the pear.php.net channel, developed and maintained by developers around the world.

The best part about PEAR is that if you're using a recent version of PHP (V1.4 and later), you already have the package manager installed. All you have to do is update the package list, maybe do a little configuration, and get going.

The package manager

The PEAR package manager is a powerful utility that helps you manage the PEAR packages on your system. You can install, update, and uninstall packages, all while keeping track of package dependencies. Options in the package manager allow you to install required dependencies automatically, so it's easy to install and update without breaking things.

This kind of package management can be useful in an enterprise using PHP. Enterprise packages may include classes and methods for operating on enterprise domain objects, or perhaps PHP interfaces to homegrown applications or services. You can use the PEAR package manager to install .tgz files, but that doesn't simplify deployment. Having a central location from which to install packages is key to easing deployment across many locations.

As of V1.4 of the PEAR package manager, a new feature is supported that allows you to subscribe to PEAR channels. Once you're subscribed, the packages contained in the channel are available for easy maintenance with the package manager.


PEAR channels

You can add and remove PEAR channels with the PEAR package manager. When you add a channel, you're telling the package manager to monitor that channel for updates when you issue the package manager an update command.

Channel management

Common commands used for viewing, discovering, and deleting channels are listed here. To view all the commands available to the PEAR package manager, type pear help at the command line, and pay special attention to the commands that begin with channel-.

List channels

To list the channels available, use the list-channels command with pear:

$ pear list-channels

If you've just installed PEAR and haven't added any new channels, you get the default list, which looks like this:

Listing 1. Default list output
Registered Channels:
====================
Channel      Summary
pear.php.net      PHP Extension and Application Repository
pecl.php.net      PHP Extension Community Library
__uri        Pseudo-channel for static packages

When you're finished building and discovering your new channel, you can run this command again and see the channel listed with the default channels.

Discover channels

To add a channel to your list, use the pear channel-discover [CHANNEL URL] command, where [CHANNEL URL] is the URL of the channel. The discovery process automatically adds the channel to your list.

Adding Channel "pear.example.net" succeeded
Discovery of channel "pear.example.net" succeeded

Remove channels

It's possible to remove channels, but only after all the packages in that channel have been uninstalled. To remove a channel, use pear channel-delete [CHANNEL NAME], where [CHANNEL NAME] is the name of the channel, which can be a full name like pear.example.net or a short name like abccompany.


A closer look at channel.xml

The channel.xml file is used to define a channel. I put the file together for my channel using the Eclipse IDE (Callisto) with the Web tools installed, which provides a designer for XML. Once the right schema declarations are put into the XML, the Eclipse IDE lets you right-click and add elements appropriately. This way, you don't have to spend much time making sure the file was valid before you can begin using it. The file can just as well be put together with a Plain Ol' Text Editor (POTE). I'll discuss the structure of the file in this section.

The file structure

You can find the schema for the channel.xml file at pear.php.net/dtd/channel-1.0.xsd. Use this to validate your file with a validating XML. An example of a complete channel.xml file is shown below.

Listing 2. Example channel.xml file
<?xml version="1.0" encoding="UTF-8"?>
<channel version="1.0" xmlns="http://pear.php.net/channel-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pear.php.net/channel-1.0
http://pear.php.net/dtd/channel-1.0.xsd">
  <name>pear.example.net</name>
  <suggestedalias>abccompany</suggestedalias>
  <summary>Summary of my channel</summary>
  <servers>
    <primary port="80">
      <xmlrpc>
   <function version="1.0">loginTest</function>
   <function version="1.0">package.getDownloadURL</function>
   <function version="1.0">package.getDepDownloadURL</function>
   <function version="1.0">package.info</function>
   <function version="1.0">package.listAll</function>
   <function version="1.0">package.listLatestReleases</function>
   <function version="1.0">package.search</function>
   <function version="1.0">channel.listAll</function>
      </xmlrpc>
    </primary>
</servers>
</channel>

The name element

The name element contains the name of the channel. The channel name should be a URL that is resolvable from your computer, such as pear.example.net. (Replace pear.example.net with the name of your host and domain.) I like the PEAR convention for the hostname, so I created an DNS alias to an existing server in my domain. I was able to do this quickly on my network because I control my internal DNS. If you're setting up a private PEAR channel server inside a company, get in touch with your DNS administrator (if you're not the administrator) and make sure the PEAR hostname is given an IP address.

Alternatively, you can use the hosts file on your workstation to manually assign an IP address to the channel name. Be aware that this technique is messy because everyone who wants to automatically discover the channel will need the same entries, so consider it a temporary solution.

Path names are fine to be included in the name of the channel, such as pear.example.net/my/path. This provides greater flexibility with channel names and locations on your server because you can potentially add many channels to be served by a single server. If you have different PHP domain objects you want to expose for different lines of business within your enterprise, for example, you can have channels named pear.example.net/linea, pear.example.net/lineb, etc.

The channel name is the base URL for the rest of the files, so when you're finished, the full URL of the channel.xml file will be http://pear.example.net/channel.xml. For example, the channel file for the standard PEAR channel is at pear.php.net/dtd/channel-1.0.xsd. If a path is included in the channel name, the channel file should be located at that path -- for example, http://pear.example.net/my/path/channel.xml.

The suggestalias element

The suggestalias is a short name for the channel. This can be used in place of the long name when you use the pear command to make it easier to specify the name of the channel. It's particularly useful when the package name defined in the name element is long.

The end user is free to alias the channel to something meaningful to them. For instance, if you're developing a private channel inside ABC Widget Corp., you might have a value of abccompany for the suggested alias. The user could go a step further, using the pear channel-alias command to set the alias to abc.

The summary element

The summary element contains a short summary of the channel. It should be descriptive enough to describe how the channel is used to a user. The summary, among other things, is displayed with the channel information using the pear channel-info CHANNEL_NAME command, where CHANNEL_NAME can be the full name or an alias for the channel.

The validatepackage element

The validatepackage element provides the opportunity to define a package for validating names and versions of the package names. For our purposes, I use the default, which is PEAR_Validate package. That means the packages defined in the sample channel should follow the same naming conventions as the PEAR packages.

The servers element

The servers element contains two child elements for servers: primary and mirror. For our purposes, you're only concerned with the primary server; no mirror server is defined (see Listing 2). I left the port the standard port 80 for my Web server, but it's important to note that it can be changed if you want to install a Web server for distributing your channel and you want that Web server to listen for connections on a nonstandard port.

Inside the primary or mirror element are the xmlrpc, soap, and rest child elements. These allow you to define the functions the PEAR package installer uses to get information about the packages. Although all three are supported by the schema, this tutorial is concerned only with the xmlrpc element.

The XML RPC methods are used to get information about the packages contained in the channel, to search for new packages, to get the URL of a package, and more.

Validate the file

Because I used the Eclipse IDE to build out my channel.xml file, I was warned by the IDE that I was breaking rules set by the schema. I felt safe by the time I was done, and no errors were displayed for the file. If you're using a standard text editor and aren't sure how to validate the XML, there are a couple different ways to do so. Resources includes links to freely available tools to help you validate.


Build the XML RPC methods

The methods exposed through XML RPC by the channel server provide the functionality for the PEAR installer to be able to list, find, and download packages in the channel. The API for the methods is listed in this section.

The XML RPC API

The method implementations for the channel in this tutorial are located in a file called xmlrpc.php, which is located at the base URL for the channel (for example, http://pear.example.net/xmlrpc.php). That location is the default location of the file, and there should typically be no reason to change it. The contents of a skeleton of the file are shown in Listing 3. The API for the methods is listed in the following sections.

Listing 3. Skeleton xmlrpc.php file
<?php

require_once 'XML/RPC/Server.php';

function logintest()
{
  // Implementation code goes here
}

function package_getDepDownloadURL($packageinfo,
  $preferred_state = false, $installed_version = false)
{
  // Implementation code goes here
}

function package_getDownloadURL($packageinfo,
  $preferred_state = false, $installed_version = false)
{
  // Implementation code goes here
}

function package_info($package, $field = null)
{
  // Implementation code goes here
}

function package_listAll($released_only = true, $stable_only = true)
{
  // Implementation code goes here
}

function package_listLatestReleases($state = '')
{
  // Implementation code goes here
}

function package_search($fragment, $summary = false, 
	$released_only = true, $stable_only = true)
{
  // Implementation code goes here
}

$functionMap = array (
  'logintest' => array('function' => 'logintest'),
  'package.getDepDownloadURL' => array('function' => 'package_getDepDownloadURL'),
  'package.getDownloadURL' => array('function' => 'package_getDownloadURL'),
  'package.info' => array('function' => 'package_info')
  'package.listAll' => array('function' => 'package_listAll'),
  'package.listLatestReleases' => array('function' => 'package_listLatestReleases'),
  'package.search' => array('function' => 'package_search'),
);

$server = new XML_RPC_Server ($functionMap, 1);

?>

logintest

This and the following functions return and accept XML RPC-encoded values. Fortunately, the XML_RPC PEAR package handles that, so there is no need for custom routines to handle the messages. Some working knowledge of the XML_RPC package API is required for adding the implementation of the methods in the xmlrpc.php file, so if you're unsure how to add XML RPC implementation, see Resources. logintest is a simple function that returns a Boolean TRUE value.

package.getDepDownloadURL

The package.getDepDownloadURL function returns the same structure as the package.getDownloadURL function, which is detailed here with an example of putting together the correct structure for returning the information the package manager expects.

Listing 4. package.getDepDownloadURL returns same structure as package.getDownloadURL
false|struct package.getDepDownloadURL (string $xsdversion, 
   struct $dependency, struct $parentpackage 
   [, string $preferred_state = stable 
   [, string $installed_version = false]])

package.getDownloadURL

The package.getDownloadURL method returns the URL and other information about the requested package. You need it when you install the package through the channel with the package manager.

false|struct package.getDownloadURL (struct $packageinfo 
    [, string $preferred_state = stable 
    [, string $installed_version = false]])

The $packageinfo structure is an array as shown here, where {channel name} is the name of the channel -- for example, pear.example.net. The {package name} is the name of the package, such as Account.

Listing 5. $packageinfo structure
array(
  'channel' => {channel name},
  'package' => {package name},
  ['version' => specific version to retrieve,]
  ['state' => specific state to retrieve,]
)

This example demonstrates how to put together a response that can be read by the PEAR package manager. It uses simple hardcoded values in order to focus on getting a working example of downloading a package and not on the implementation of connecting to a database server. But in a real implementation, this would most likely connect to a data source -- such as a MySQL database, XML files, or even the file system and caching.

Listing 6. Getting the download URL
function package_getDownloadURL($packageinfo, $preferred_state = false, $install
ed_version = false)
{
  /* The $packageinfo structure will be in an array of the following
   * format:
   *        'channel' => {name of channel}
   *        , 'package' => {name of package}
   *        [, 'version' => {version to get}]
   *        [, 'state' => {state of package}]
   *
   * For example, for this tutorial it looks like:
   * array (
   *      'channel' => 'pear.example.net',
   *      'package' => 'Account'
   * )
   */

  /* Just reading the package.xml file here and handing
   * it out to the output. A PEAR server could alternatively
   * read this out of a database or out of the .tgz file
   * directly using the PEAR_PackageFile class.
   */

  $file = 'Account/package.xml';

  if ($preferred_state) {
    // The downloader prefers a state, do something here..
  }

  if ($installed_version) {
    // Do something here
  }

  $returnData = array(
    'version' => '1.0.0',
    'info' => File::readAll($file),
    'url' => 'http://pear.example.net/Account/Account-1.0.0'
    );

  return new XML_RPC_Response(XML_RPC_encode($returnData));

}

package.info

The package.info method returns more information about a given package:

false|struct package.info(string $package [, string $field = null])

package.listAll

The package.listAll function lists the information about all the packages included in the channel:

struct package.listAll([bool $released_only = true [, bool $stable_only = true]])

Both parameters to package.listAll are optional. If $released_only is set to false, the packages that have no releases should be included in the output. If $stable_only is set to false, even packages that don't have any stable releases should be included in the output.

Listing 7. If $stable_only is false, packages that don't have stable releases included in output
function package_listAll($released_only = true, $stable_only = true)
{
  /* Get information from the database, check the
   * $released_only and $stable_only params to see
   * what the caller wants to include in the output.
   */


  $pkgInfo = array (
    'packageId' => '3',
    'categoryId' => '1',
    'category' => 'DomainObjects',
    'license' => 'My license here.',
    'summary' => 'My summary',
    'description' => 'My useful description',
    'lead' => 'ngood',
    'stable' => '1.0.0',
    'unstable' => false,
    'state' => 'state',
    'deps' => array()
  );

  $data = array('Account'=> $pkgInfo);
  return new XML_RPC_Response(XML_RPC_Encode($data));

}

package.listLatestReleases

The package.listLatestReleases function does as its name implies -- it lists only the latest release, given a state. If nothing is supplied to the function as a state, the function should return the newest release for all packages.

struct package.listLatestReleases ([string $state = ''])

An example implementation is shown below.

Listing 8. Sample implementation
function package_listLatestReleases($state)
{
	/* Go get the information from some data container */
	
  $releaseInfo = array (
    'version' => '1.0.0',
    'state' => 'stable',
    'filesize' => 1024
  );

  $data = array('Account' => $releaseInfo);
  return new XML_RPC_Response(XML_RPC_Encode($data));
}

package.search

The package.search function lets you search for a package with only a partial package name.

Listing 9. package.search
struct package.listAll (string $fragment 
    [, string|bool $summary = false 
    [, bool $released_only = true 
    [, bool $stable_only = true]]])

The $fragment is the partial name of the package. If summary contains a string value, this value should be used to search through the summaries of the packages. The $released_only and $stable_only parameters have the same meaning as those passed to the package.listAll function.


Publish your channel

Now it's time to add some code to publish the channel so new packages can be installed by the PEAR package manager. This section shows where to place channel.xml and xmlrpc.php so the PEAR package manager can access them.

Discover your channel

To publish a channel and show that it's discovered and added appropriately, the bare minimum implementation you need to add in the xmlrpc.php file are the package.listAll and package.listLatestReleases methods. I started with those, published channel.xml and xmlrpc.php to the Web server, and used pear channel-discover to discover and add the channel successfully and list the packages provided by the channel.

To try your implementation, create your own channel.xml file with the appropriate information as shown in the channel.xml file structure section. For the most part, you should be able to copy the example file shown in Listing 2 and update the name, suggestedalias, and summary elements to be appropriate for your environment. If you have a mirror server on which you'll also be hosting your private channel, you need to add one or more mirror child elements under the servers element.

Make sure the channel.xml file is valid by validating the XML against the schema. At the very least, make sure the file is well formed. That will head off a lot of problems.

Now create a file called xmlrpc.php and start by copying the skeleton implementation listed in Listing 3. For your initial run of discovering the channel and listing the packages found in it, add implementation to the package.listAll and package.listLatestReleases functions. In my implementation, I used hardcoded strings when I built my array, but for obvious reasons, that would be a step backward in a production implementation. You should add code to connect to some type of dynamic data container that's easy for you to maintain.

Copy the two files -- channel.xml and xmlrpc.php -- to the base location for your channel's URL. For instance, if you name yours channel pear.example.net, you need to copy the two files onto the server where the site of that name is hosted. In my implementation, I copied them to the base document directory of my Web server. When you're finished copying the files, open a browser and see if you can access the XML file. Type http://pear.example.net/channel.xml. If you get the file, then you can proceed to the next step. If the browser gives you a 404 error, double-check the location and make sure you copied the files to the correct place.

Once you've verified that the files are in the correct location, try discovering the channel with the PEAR package manager. If the channel is named pear.example.net, your command will look like the following, and the result will look like that back in the Discover channels section.

pear channel-discover pear.example.net

It's important that the package name in the channel.xml file matches the name of the channel being discovered here. If the package manager gives you no errors and tells you the packages have been added, your channel has been added successfully. You can use pear list-channels on the command line and see your newly added channel.


Install the test package

Now that you have a channel up and running, you'll want to install a package from the channel. In order for the package manager to know where the packages are located, you need to add the implementation code behind the package.getDownloadURL method to return the URL and information about the package. If the package has dependencies, you must also add implementation to the package.getDepsDownloadURL method. Look at my sample implementation to understand the structure of the data being returned.

After you add the implementation code to the xmlrpc.php file, copy it where the previous file was located. You don't need to copy the channel.xml file again.

Building a sample package

The sample package for this tutorial is simple. It doesn't need to be complicated because the package is only used to verify that all the plumbing works as the channel is being published. The example package contains two files, Account.php and AccountFactory.php, located in the Factory subdirectory. The contents of the files are unimportant, but the package.xml file is important because it contains the version of the package.

Listing 10. package.xml file contains package version
<?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>Account</name>
  <channel>pear.example.net</channel>
  <summary>Domain objects for ABC Company</summary>
  <description>
    This package contains domain objects that are to be used by
    services and applications in ABC Company.
  </description>
  <lead>
    <name>Nathan A. Good</name>
    <user>ngood</user>
    <email>mail@nathanagood.com</email>
    <active>yes</active>
  </lead>
  <date>2006-11-17</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="http://pear.abccompany.com/license">
    ABC Company License
  </license>
  <notes>Release notes go here.</notes>
  <contents>
    <dir name="/">
      <dir name="Factory">
        <file name="AccountFactory.php" role="php"></file>
      </dir>
      <file name="Account.php" role="php"></file>
    </dir>
  </contents>
  <dependencies>
    <required>
      <php>
        <min>5.0</min>
      </php>
      <pearinstaller>
        <min>1.4.0</min>
      </pearinstaller>
    </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>2006-11-17</date>
      <license uri="http://pear.example.net/license">
        ABC Company License
      </license>
      <notes>Brand new version.</notes>
    </release>
  </changelog>
</package>

This file reflects V2.0 of the package.xml file, which has replaced V1.0. It's important to use V2.0 because it includes the channel element. The text inside the channel element must match the name of the channel or you'll get errors when you attempt to install the package using PEAR.

Getting in-depth about building PEAR packages isn't the focus of this tutorial. As a quick overview, you can follow the steps here, but see Resources for more information. To get started with the sample package, put the package.xml and Account.php files in a directory and AccountFactory.php into a subdirectory called Factory to mirror what was defined in package.xml. Fortunately, a couple commands come with the PEAR package manager that make it easy to validate the package file and build the package, so there is no guesswork in terms of package structure.

To verify the package file, open a command window, change into the directory where you put the package files, and verify the package.

Listing 11. Verify package
$ pear package-validate package.xml
Analyzing Factory/AccountFactory.php
Analyzing Account.php
Validation: 0 error(s), 0 warning(s)

After the package file has been verified, use the PEAR package manager to build the package. Without leaving the directory, use the following command:

Listing 12. Build package
$ pear package
Analyzing Factory/AccountFactory.php
Analyzing Account.php
Adding file Factory/AccountFactory.php
Adding file Account.php
Package Account-1.0.0.tgz done

Now, move the Account-1.0.0.tgz file to the location you'll specify in the url key of the structure returned by the package.getDownloadURL implementation in the xmlrpc.php file. For instance, you might decide on a convention of putting packages into a directory on your Web server that's accessible by http://pear.example.net/pkg. Make sure the package.getDownloadURL function returns a URL of http://pear.example.net/pkg/Account-1.0.0 for a package called Account-1.0.0. Notice that the file extension .tgz isn't included in the URL. The package manager appends it for you.

Once the package is in place and the xmlrpc.php file has been updated to included the new implementation, install the package using the package manager as shown below.

Listing 13. Install package
pear install abccompany/Account
downloading Account-1.0.0.tgz ...
Starting to download Account-1.0.0.tgz (1,245 bytes)
....done: 1,245 bytes
+ create dir /usr/local/php5/lib/php/Factory
md5sum ok: /usr/local/php5/lib/php/Factory/AccountFactory.php
md5sum ok: /usr/local/php5/lib/php/Account.php
about to commit 7 file operations
successfully committed 7 file operations
install ok: channel://pear.example.net/Account-1.0.0

Upgrade the test package

The point of using the channel is to make updating packages easy. Although channel support in PEAR is fairly new, I believe it will become a widespread method of keeping enterprise libraries of PHP code up to date.

Update the files

In order to have a frame of reference and proof that your updating worked, add a method to the Account.php file you've been working with in the sample package. The new method is shown in bold below.

Listing 14. Add method to Account.php
<?php
// ... snipped comments
class Account
{
  /**
   * The name of the account
   * @var string
   * @access private
   */
  var $_name;

  /**
   * New var
   * @var string
   * @access private
   */
  var $_id = 7;
	

	// ... (snipped setters and getters for _name)

	
  /**
   * New function
   * @access public
   * @return int The account's ID
  public function getId()
  {
    return $this->_id;
  }
	

}

?>

Update the package

Updating the package is as simple as adding new entries in the package.xml file and rerunning the process in the Building a sample package section.

Listing 15. Update package
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.4.11" 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>Account</name>
 <channel>pear.example.net</channel>
 <summary>Domain objects for ABC Company</summary>
 <description>This package contains domain 
 objects that are to be used by services and applications in ABC Company.
 </description>
 <!-- snipped -->
 <date>2006-11-17</date> <!-- Update time! -->
 <time>18:54:20</time>
 
 <version>
  <release>1.1.0</release>
  <api>1.1.0</api>
 </version>
 
 <!-- snipped -->
 <changelog>
 
  <release>
   <version>
    <release>1.1.0</release>
    <api>1.1.0</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2006-11-17</date>
   <license uri="http://pear.example.net/license">
	 ABC Company License</license>
   <notes>Added new and improved getID function.</notes>
  </release>
	
  <release>
   <version>
    <release>1.0.0</release>
    <api>1.0.0</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2006-11-17</date>
   <license uri="http://pear.example.net/license">
	 ABC Company License</license>
   <notes>Brand new version.</notes>
  </release>
 </changelog>
</package>

After the package is updated, put it in the same location as the previous version. You don't need to delete the other package file. The structures returned by the xmlrpc.php should provide the URL for the latest version. In the sample implementation, I edited the file and replaced all occurrences of 1.0.0 with 1.1.0. In a database-driven implementation, this type of behavior should be dynamic, and the latest version should automatically be returned as long as the selection criteria are correct.

Update the channel

At this point, you should have the new package in place next to the older package. Once you verify this, you can update the channel to make sure you have the latest channel information.

$ pear channel-update pear.example.net
Retrieving channel.xml from remote server
Channel pear.example.net channel.xml is up to date

Now you're ready to run the upgrade command to tell the package manager to upgrade your package.

Upgrade with the package manager

You can use the pear upgrade-all command to upgrade all the packages on the system. Alternatively, the pear upgrade PACKAGE command works equally well, where PACKAGE is the name of the package (in this case, account). I have a bare-bones system, so I used upgrade-all because it's fewer characters to type, and I'm lazy.

Listing 16. Upgrade package
$ pear upgrade-all
Will upgrade account
downloading Account-1.1.0.tgz ...
Starting to download Account-1.1.0.tgz (1,333 bytes)
....done: 1,333 bytes
md5sum ok: /usr/local/php5/lib/php/Factory/AccountFactory.php
md5sum ok: /usr/local/php5/lib/php/Account.php
about to commit 12 file operations
successfully committed 12 file operations
upgrade-all ok: channel://pear.example.net/Account-1.1.0

At this point, the package has been upgraded as expected. You can verify the upgrade by peeking into the PEAR repository location and checking the file. This location of this directory is configurable and different across most systems, but you can find out the configured directory on your system by using the pear config-get php_dir command.


Troubleshooting

I ran into a few problems when I was working on the start of an implementation for a channel server.

The first issue is that I built the package.xml file by hand. That isn't an issue in itself, but when the pear package command is used to build the package, the package file is reformatted. My implementation of the getDownloadURL function pulled the contents from the old file and returned it as a result of the XML RPC call. This caused an error because the package.xml downloaded didn't match that returned by the xml-rpc call. I suggest using the PEAR_PackageFile class to build your package file so you don't have issues. If you'd rather build the file by hand, make sure the file is the same (including indenting) as the version put into the .tgz file created by the pear package command. I found it useful more than once to pull the generated package.xml file out of the .tgz file and inspect it.

If you get unexpected results from the XML-RPC commands after putting new versions of the xmlrpc.php file in place, make sure your Web server isn't caching anything. Caching can cause unexpected results because you make changes but don't see them reflected.


Summary

The support of channels by the PEAR package manager is an innovative, intuitive approach to organizing and distributing packages. Although the PEAR channel contains more than 400 packages, your enterprise's specific needs, such as domain objects, may necessitate having private channels hosted on an internal server.

This tutorial demonstrates at a high level how to implement basic functionality for a channel server. With a database connection or intelligent parsing of XML files or the file system, you can use these basic examples to implement a full-blown channel server in your enterprise.

Resources

Learn

Get products and technologies

Discuss

  • The developerWorks PHP Developer Forum provides a place for all PHP developer discussion topics. Post your questions about PHP scripts, functions, syntax, variables, PHP debugging and any other topic of relevance to PHP developers.
  • Get involved in the developerWorks community by participating in developerWorks blogs.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=192277
ArticleTitle=A step-by-step guide to publishing your own PEAR channels
publish-date=01302007