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.
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 both <pearinstaller> and <php> 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.
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.
<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 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.
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.
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 | ||
---|---|---|---|
|
| ||
|
| ||
|
| ||
|
| ||
|
| ||
|
| ||
|
|
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.
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. |
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.
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:
windows
unix
linux
freebsd
darwin (use for Mac OS X)
sunos
irix
hpux
aix
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.
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