Introduction to dependencies in package.xml 2.0
Dependencies are the most difficult aspect of programming. Using code written by other people
can be a nightmare without simple ways to control bugs and API changes. Arguably, the handling
of dependencies is PEAR's greatest strength, although there are many other nice features. Regardless
of your personal opinion of the importance of dependencies, it is crucial to understand how to
specify a dependency, and the different ways to ensure that your package is only used on systems
that support it.
In package.xml 1.0, dependencies were relatively simple, but at the cost of usefulness. Specifying
a dependency on a package for applications was actually dangerous. If you wished to limit an installed
version of a package to a single version, it would mean preventing upgrade at any cost. package.xml
2.0 provides a simple way to enforce stricter dependency versioning without making upgrades onerous.
In package.xml 1.0, dependencies on PHP extensions like PECL
extensions was near to a disaster. Extensions had to be present in the php.ini and loaded in
memory in order to validate as being installed. This is often impossible, as a different php.ini
is used for a production website versus the php.ini used for the pear installer. With package.xml
2.0, dependencies on a PECL-like extension is simple and straightforward, and only requires that
the package be installed, and not that the extension be present in memory, although an older
style extension dependency is also supported.
package.xml 1.0 supports two kinds of dependencies, required and optional. package.xml 2.0
also supports these two dependency types, but introduces a new kind of dependency concept: an
optional dependency group. Dependency groups define a feature set. Instead of thinking
"This package optionally depends on Net_FTP and optionally depends on Log" think
"To remotely install packages, I need the remoteinstall feature, which needs Net_FTP
and the Log package". This grouping allows defining sets of packages and extensions that
comprise a feature and must be installed all at once for the feature to function properly.
package.xml 1.0 only supported php, package, and extension dependencies. package.xml 2.0
supports dependencies on php, package, extension, os, architecture, and PEAR installer. In
addition, package.xml 2.0 supports depending on a static package located at a url, and depending
on a package that provides an extension to PHP like PECL packages.
The PEAR installer dependency is not a dependency on the PEAR package, but a dependency on the
currently running PEAR installer, and is more similar to a PHP dependency in that it requires
the specified version to be running in memory. This is very useful for circumventing difficult
bugs in the PEAR installer that render a package install useless.
Structure of <dependencies>
The <dependencies> tag re-organizes dependencies into groups and "extracts"
attributes into tags. It also un-abbreviates words for clarity and human-readability.
The following excerpt of a package.xml version 1.0:
<deps>
<dep type="pkg" rel="ge" version="1.3.1">Archive_Tar</dep>
<dep type="php" rel="ge" version="4.2.0"/>
<dep type="pkg" rel="has" optional="yes">PEAR_Frontend_Web</dep>
</deps> |
Approximately translates into this format in package.xml 2.0:
<dependencies>
<required>
<pearinstaller>
<min>1.4.0a7</min>
</pearinstaller>
<php>
<min>4.2.0</min>
<max>6.0.0</max>
</php>
<package>
<name>Archive_Tar</name>
<channel>pear.php.net</channel>
<min>1.3.1</min>
</package>
</required>
<optional>
<package>
<name>PEAR_Frontend_Web</name>
<channel>pear.php.net</channel>
</package>
</optional>
</dependencies> |
These changes were made to simplify xml validation and parsing. Note that unlike package.xml
1.0, the <pearinstaller> and <php> dependencies are required in all package.xml.
In addition the <min> tag is required in <pearinstaller>, and both the <min>
and <max> tags are required for <php> dependencies.
<pearinstaller> dependencies
The <pearinstaller> dependency defines the minimum version of PEAR that can properly
parse and install the package.xml containing it. As with all dependency tags that support
versioning, these 4 tags are supported to define versioning:
<min> - minimum version of PEAR required to install this package.xml. This tag is
required in all package.xml <pearinstaller> dependencies.
<max> - maximum version of PEAR installer supported. Use with caution! This tag
will prevent the package from being installed by anyone with a newer version of PEAR!
<recommended> - recommended version of PEAR installer. This tag is used for strict
version control. The installer will refuse to install a package without the --force
option unless the version exactly matches recommended. This can be used to provide a level
of extra security, as a package can be set to install using a version that is known to work
without limiting future upgrades.
<exclude> - incompatible versions of PEAR installer. Use this to prevent the package
from being installed by any PEAR version that cannot properly install the package. Multiple
<exclude> tags may be used to exclude more than one version.
<php> dependencies
As with all dependency tags that support
versioning, these 4 tags are supported to define versioning:
<min> - minimum version of PHP required to install this package.xml. This tag is
required in all package.xml <php> dependencies.
<max> - maximum version of PHP supported. This tag is required in all package.xml
<php> dependencies.
<recommended> - recommended version of PHP. This tag is used for strict
version control. The installer will refuse to install a package without the --force
option unless the version exactly matches recommended. This can be used to provide a level
of extra security, as a package can be set to install using a version that is known to work
without limiting future upgrades.
<exclude> - incompatible versions of PHP. Use this to prevent the package
from being installed by any PHP version that cannot properly work with the package. Multiple
<exclude> tags may be used to exclude more than one version.
<subpackage> dependencies
Subpackage dependencies share the same xml format as package dependencies. The subpackage
dependency should only be used if a package is split into more than one package. In other
words, if the child package contains the same files as any earlier version of the parent
package, the child package would normally conflict with the parent package because it
would be attempting to overwrite the parent package's files with its own files.
A simple example should make this clear. Package Foo 1.0.0 contains Foo.php and Foo/Bar.php.
Package Foo's developers decide to split Foo into two packages: Foo and Foo_Bar. Foo 1.1.0
contains foo.php, and Foo_Bar 0.1.0 contains Foo/Bar.php. Foo_Bar 0.1.0 conflicts directly
with Foo 1.0.0, as both contain the file Foo/Bar.php.
If Foo has a subpackage dependency on Foo_Bar, then the installer will ignore the conflict
between Foo 1.0.0's Foo/Bar.php and Foo_Bar 0.1.0's Foo/Bar.php just as it ignores the conflict
between Foo 1.0.0's Foo.php and Foo 1.1.0's Foo.php.
<package> dependencies
Understandably, the <package> dependency is PEAR's most complex dependency. PEAR 1.4.0
supports 3 different kinds of package dependencies:
Normal, traditional channel server-based package dependencies (same idea as package.xml 1.0).
Dependencies on packages that provide PHP extensions (like PECL
packages). (These can be both server-based and uri-based dependencies)
Static, non-traditional uri-based package dependencies.
channel-based <package> depedendencies
The most common kind of package dependency is a channel-based dependency. This dependency
from package.xml version 1.0:
<deps>
<dep type="pkg" rel="has">PEAR</dep>
</deps> |
translates to this dependency in package.xml version 2.0:
<dependencies>
<required>
<!-- ... -->
<package>
<name>PEAR</name>
<channel>pear.php.net</channel>
</package>
</required>
</dependencies> |
Note that <channel> is a required tag for all typical package dependencies. Use pear.php.net
for all packages that were packaged using package.xml 1.0, regardless of where they are downloaded
from.
As with all dependency tags that support versioning, all standard versioning tags
are supported (min, max, recommended, exclude). In addition, the <conflicts>
tag is supported to create a negative dependency.
<min> - minimum version of a dependency. If the dependency package is installed, and
is older than this version, installation will fail.
<max> - maximum version of a dependency. If the dependency package is installed, and
is newer than this version, installation will fail.
<recommended> - recommended version of a dependency. This tag is used for strict
version control. The installer will refuse to install a package without the --force
option unless the version exactly matches recommended. This can be used to provide a level
of extra security, as a package can be set to install using a version that is known to work
without limiting future upgrades.
Note that use of the <compatible>
tag in the dependency's package.xml can be used
to circumvent the installer's strictness. In essence, the <compatible> tag tells the
installer that a dependent package is compatible with the current package, even though
it is not the recommended version.
<exclude> - incompatible versions of a package. Multiple
<exclude> tags may be used to exclude more than one version of a dependency.
<conflicts> - Negates the dependency. If the package is installed, it cannot
satisfy the requirements of the dependency or installation will fail.
Here is a rough chart describing how to convert from package.xml 1.0 "rel" attributes
to a package.xml 2.0 equivalent.
Table 16-1. Converting package.xml 1.0 package dependencies to package.xml 2.0
1.0 | 2.0 equivalent |
---|
<dep type="pkg" rel="has">Foo</dep> |
|
<package>
<name>Foo</name>
<channel>pear.php.net</channel>
</package> |
|
<dep type="pkg" rel="ge" version="1.0.0">Foo</dep> |
|
<package>
<name>Foo</name>
<channel>pear.php.net</channel>
<min>1.0.0</min>
</package> |
|
<dep type="pkg" rel="gt" version="1.0.0">Foo</dep> |
|
<package>
<name>Foo</name>
<channel>pear.php.net</channel>
<min>1.0.0</min>
<exclude>1.0.0</exclude>
</package> |
|
<dep type="pkg" rel="le" version="1.0.0">Foo</dep> |
|
<package>
<name>Foo</name>
<channel>pear.php.net</channel>
<max>1.0.0</max>
</package> |
|
<dep type="pkg" rel="ge" version="1.0.0">Foo</dep> |
|
<package>
<name>Foo</name>
<channel>pear.php.net</channel>
<max>1.0.0</max>
<exclude>1.0.0</exclude>
</package> |
|
<dep type="pkg" rel="ge" version="1.0.0">Foo</dep>
<dep type="pkg" rel="le" version="1.9.0">Foo</dep> |
|
<package>
<name>Foo</name>
<channel>pear.php.net</channel>
<min>1.0.0</min>
<max>1.9.0</max>
</package> |
|
<dep type="pkg" rel="not">Foo</dep> |
|
<package>
<name>Foo</name>
<channel>pear.php.net</channel>
<conflicts/>
</package> |
|
uri-based <package> dependencies
Let's look at uri-based package dependencies. Here is a simple example:
<package>
<name>Foo<name>
<uri>http://www.example.com/Foo-1.3.0</uri>
</package> |
This dependency tells the installer to fetch http://www.example.com/Foo-1.3.0.tgz or
http://www.example.com/Foo-1.3.0.tar (both must be available!) if the package Foo
is not installed. All uri packages must contain a
<uri> tag rather than a
<channel> tag and will
automatically belong to the pseudo-channel "__uri", but that is not important to
the discussion of how to format the xml to create the uri-based package dependency.
uri-based package dependencies cannot contain any versioning information, as this is irrelevant:
there is only one version possible with a static uri. uri-based dependencies can contain the
<conflicts/> tag to specify an absolute conflict with the package, and the
<providesextension> tag to specify an extension provided by the static package.
PEAR-style <package> dependencies vs. PECL-style <package> dependencies
package.xml 2.0 supports differentiating
release types, and as such also
supports dependencies on PECL-style packages that use the extbinrelease or extsrcrelease type.
To specify a dependency on a PHP extension that can be distributed as
a PECL package, but could also be bundled with PHP by default, such as the PDO extension,
use this dependency style:
<package>
<name>PDO</name>
<channel>pecl.php.net</channel>
<min>0.3.1</min>
<providesextension>PDO</providesextension>
</package> |
The magic is in the <providesextension> tag. This tag tells the installer to take this
process when validating the dependency:
Is the extension "PDO" present in memory? If so, is it version 0.3.1 or higher?
If not, is the user also installing pecl.php.net/PDO at the same time as this package? If
so, is it version 0.3.1 or higher?
If not, is pecl.php.net/PDO installed, and is the version 0.3.1 or higher?
If any of the three conditions above validate in the order specified, the dependency will be
satisfied and installation will continue. This system allows users to use a different php.ini
to install PHP extensions and also provides a fail-safe system to depend on extensions.
Warning |
<providesextension>, like all other extension-related functions in PHP, is case-sensitive.
Do not use "pdo" for the "PDO" extension, or your dependency will always
fail.
|
<extension> dependencies
As with all dependency tags that support versioning, all standard versioning tags
are supported (min, max, recommended, exclude). In addition, the <conflicts>
tag is supported to create a negative dependency.
<min> - minimum version of PHP extension to install this package.xml.
<max> - maximum version of PHP extension supported.
<recommended> - recommended version of PHP extension. This tag is used for strict
version control. The installer will refuse to install a package without the --force
option unless the version exactly matches recommended. This can be used to provide a level
of extra security, as a package can be set to install using a version that is known to work
without limiting future upgrades.
<exclude> - incompatible versions of PHP extension. Multiple
<exclude> tags may be used to exclude more than one version.
<conflicts> - Negates the dependency. If the extension is present, it cannot
satisfy the requirements of the dependency or installation will fail.
<os> dependencies
The OS dependency is used to restrict a package to both a particular class of OSes (like unix) and to
a specific OS (like darwin, or freebsd). Here is an example:
<os>
<name>linux</name>
</os> |
To specify that a package can be installed on every OS except the one specified, use the
<conflicts/> tag:
<os>
<name>windows</name>
<conflicts/>
</os> |
Possible OS values are:
In addition, any esoteric OS that supports the
php_uname() function can be used.
Note that the "unix" OS is defined as any of linux, freebsd, darwin, sunos, irix, hpux,
or aix.
<arch> dependencies
The arch dependency is used to restrict a package to a specific os and processor architecture.
Here is an example:
<arch>
<pattern>linux-*-i386-*</pattern>
</arch> |
To specify that a package can be installed on every architecture except the one specified, use the
<conflicts/> tag:
<arch>
<pattern>linux-*-i?86-*</pattern>
<conflicts/>
</arch> |
The arch pattern is defined by the OS_Guess->matchSignature() method, and
is as follows: sysname[-release[-cpu[-extra]]]. All segments within [] are optional,
and the wildcard "*" can be used in all segments instead of a value. In addition, the
"?" wildcard can be used to specify a single character that can match any value.
i?86 will match i386, i686, i586 and so on.
sysname is the same as the os dependency, except unix is not supported.
release is the version of the operating system.
cpu is the specific cpu, and is typically i?86, sparc, powerpc.
extra is any other stuff on the end of php_uname(), including the glibc version