Working with installed packages and channels: The Registry API

Introduction

Pyrus provides a very simple API for accessing its registry. Pyrus stores
meta-information on installed packages in redundant registries. There are
three kinds of registries that Pyrus recognizes, Sqlite3,
Xml and the legacy Pear1 registry. A
pyrus-based installation can have up to all three kinds of registries
redundantly storing the installed packages and the known channels. By default
the Sqlite3 registry is the primary registry used for
querying information, with the Xml registry as a backup.

When Pyrus is used to manage an installation, it checks to see which
registries are already present, if any, and will use the existing registries.
This fact can be used to provide more flexible installation options. For
instance, Pyrus can be used to manage an existing legacy PEAR installation
without any special configuration, it will simply detect that the legacy
registry is present and use it. If a package is extracted into a bundled
location, Pyrus will detect its extracted package.xml as belonging to the
Xml registry, and will use only that registry for installation purposes,
which allows upgrading the extracted package and avoids placing any
absolute paths into the installation.

Pyrus provides full atomic installation transactions for all of its
registry types, including the legacy Pear1 registry, unlike the PEAR
installer. In addition, each registry provides a single method which
can be used to remove it from disk, and there is also a single method which
can be used for converting from one registry type to another. Another method
is available for repairing a corrupted registry from one of its redundant
registries.

Pyrus provides a separate logical registry for storing channels from the registry
that stores packages. Each registry handles this slightly differently. The
Sqlite3 registry, for instance, stores all information in a single database.
The Xml registry stores information in separate files, like the legacy Pear1
registry.

Basic Registry principles

All registry classes implement the PEAR2\Pyrus\IChannel
interface, and all channelregistry classes implement the
PEAR2\Pyrus\ChannelRegistry interface. The
PEAR2\Pyrus\Registry class acts as an aggregator of
underlying registries, and implements the ability to cascade to parent
registries, as does the PEAR2\Pyrus\ChannelRegistry
class.

The simplest way to retrieve a registry object is to use the one strongly
tied to the PEAR2\Pyrus\Config
object:

Accessing a specific installed package retrieves an object that is
API-identical to a PackageFile
object. The registry is implemented logically as an associative array.
By requesting a package's logical name, which is channel/packagename,
we get an object that can be manipulated just as if it were the package prior
to installation

<?php$package = PEAR2\Pyrus\Config::current()->registry->package['pear2.php.net/PEAR2_Pyrus_Developer'];$remotepackage = new PEAR2\Pyrus\Package('pear2.php.net/PEAR2_Pyrus_Developer');// both packages can be queried with the same API?>

The same principle applies to channels:

<?php$channel = PEAR2\Pyrus\Config::current()->channelregistry['pear2.php.net'];$localchannel = new PEAR2\Pyrus\ChannelFile('channel.xml');// both channels can be queried with the same API?>

Iteration also works with both just as it would for an array:

<?phpforeach (PEAR2\Pyrus\Config::current()->registry->package as $name => $package) {// $name is channel/package // $package is a packagefile object}foreach (PEAR2\Pyrus\Config::current()->channelregistry as $name => $channel) {// $name is the channel name // $channel is a channelfile object}?>

Installation-related API tasks

There are 4 installation-related methods, as well as 3 transaction methods.
These methods are:

install() and replace()

uninstall()

exists()

begin(), commit() and
rollback().

The install() method registers a package as installed,
and sets its date/time to the current time so that the installation time
can be tracked. The replace() method registers a package
as installed, but does not modify its date/time. This is useful for
repairing a corrupted entry, or simply storing a package as it is. Both
methods accept a PEAR2\Pyrus\IPackageFile object. A
packagefile object can be retrieved from a PEAR2\Pyrus\Package
object by calling its getPackageFileObject() method.
A PEAR2\Pyrus\Registry\Exception is thrown on any errors.

The uninstall() method accepts two parameters, the
name of the package, and the package's channel. A
PEAR2\Pyrus\Registry\Exception is thrown on any errors.

The exists() method also accepts two parameters, and
returns TRUE or FALSE depending on whether the package exists. If
severe errors occur such as registry corruption, a
PEAR2\Pyrus\Registry\Exception object is thrown.

Note that array access can also be used to handle installation-related tasks:

When performing any installation or uninstallation task, it is recommended
to use the registry's built-in transaction support. The
Sqlite3 registry uses the database's native transaction
support. Both the Xml and Pear1
registries use Pyrus's PEAR2\Pyrus\AtomicFileTransaction
for its transaction support. Thus, it is always best to do a transaction
by first enabling the registry transaction, and then the atomic file transaction
within this registry transaction:

If using the Installer API,
the transactions and installation to registry is all automatic, this code is
only needed for customizing installation.

Specialized querying of the registry

Other methods for querying the registry include:

info()

listPackages()

getDependentPackages()

detectFileConflicts()

detectRegistries()

removeRegistry()

info()

The info() method provides a way of peeking at
a single attribute of a package. When used with the Sqlite3
registry, it is extremely efficient both in terms of memory use and speed.
Both the Xml and Pear1 registries are
far slower because they must load the complete packagefile into memory for
every query. For these registries, it is better to simply retrieve a
packagefile and query it using the
PackageFile API.

Parameters to info() are the package name, package channel,
and the field name to retrieve.

All of the
Basic package.xml properties
can be directly accessed using info(). In addition, two
special properties, installedfiles and dirtree
are available.

installedfiles returns a list of files and their properties
as they have been installed. Here is a sample return value:

dirtree returns a list of every directory that would have
been created if installing the package in a new installation. This can
be used to prune empty directories after uninstalling. Here is a sample
return value:

listPackages()

This method accepts a channel name as an argument, and returns an array
of the names of installed packages from that channel.

getDependentPackages()

getDependentPackages() requires a single argument,
a PEAR2\Pyrus\IPackageFile object.
This method returns an array of PEAR2\Pyrus\Package
objects representing installed packages that depend upon the package
passed in. If the optional second boolean parameter is set to true
(which it is by default), performance is improved when querying an
Sqlite3 database by returning packages containing only
the name of the package and its dependencies.

detectFileConflicts()

This method is used to implement file conflict detection to prevent
overwriting installed files with those from another package. It accepts a
single argument, a PEAR2\Pyrus\IPackageFile object.
The Pear1 registry is the most efficient at this
operation (at the expense of drastically decreased efficiency at installation or
uninstallation), the Sqlite3 is the next most
efficient, and the Xml registry is the least efficient,
and in fact is so inefficient, this method should only be called
on an Xml registry that is for a very small installation.

detectRegistries()

This static method accepts a string containing the path to check for registries,
and returns an array containing the names of registries
found. The possible return values include Sqlite3,
Xml and Pear1. Note that only a call
to PEAR2\Pyrus\Registry::detectRegistries() will return
a list of all registries found. A call to
PEAR2\Pyrus\Registry\Sqlite3::detectRegistries() will
only return either array() or
array('Sqlite3') depending on whether the registry exists.

removeRegistry()

This static method accepts a string containing the path to remove a registry
from. A call to PEAR2\Pyrus\Registry::removeRegistry()
will completely remove all traces of a PEAR installation. A call to
an individual registry's removeRegistry, such as a call to
PEAR2\Pyrus\Registry\Pear1::removeRegistry() will only
remove that registry from the installation path.