One of the most important features of the
PEAR installer (and any installer) is its ability to resolve and process dependencies between modules.
PEAR 1.3.4 and earlier is based on package.xml version 1.0. In this system, dependencies could be defined between packages, on a php version, and on extensions. Optional dependencies were introduced in version 1.2.0. Dependencies were defined as a "has" or "not" type for basic exist/conflict dependencies, and were based on version comparisons for other cases, structured around php's
version_compare().
PEAR 1.4.0a1 introduces package.xml version 2.0. In addition to other enhancements (such as channel support and post-install scripts), package.xml version 2.0 refines and enhances dependencies to an even greater level of precision than anything that was possible with version 1.0.
PEAR 1.4.0a1 supports several more kinds of dependencies:
- package
- subpackage
- extension
- php
- pear installer
- os
- arch
The package, extension, and php dependencies are the same kind of dependency that you are used to from package.xml 1.0.
The pear installer dependency allows a package to define a PEAR dependency without specifying that the actual PEAR package must be installed. This is to allow greater freedom from the PEAR installer itself, in case someone were to come along and implement a java-based installer that uses package.xml or some other customized installer. It also halts a critical situation that has arisen in the past. Imagine:
PEAR 1.4.0 is installed. Package A uses a package.xml feature that was only introduced in PEAR 1.5.0. If Package A has a required dependency on PEAR, it will be upgraded prior to installation, but if the user typed:
<code>
$ pear upgrade A PEAR
</code>
Then PEAR 1.4.0 will be used to upgrade
both PEAR and A, which is a problem, as A's installation may be corrupt. This issue was first encountered after PEAR 1.2.0 introduced optional dependencies - earlier PEAR versions would happily process and attempt to install packages that use them, but would treat ALL dependencies as required. The pearinstaller dependency combats this issue by requiring the RUNNING version of PEAR to match the dependency.
An OS dependency uses the OS_Guess class bundled in PEAR to determine whether a generalized OS is matched. Possible values include windows, linux, darwin, and any defining OS string like linux, freebsd, etc. The list of possible values is shown by examining the output of php_uname(), and also the customized aliases defined by OS_Guess and PEAR_Dependency2.
An arch dependency validates a php_uname() string against the operating system's string, allowing wildcards * and . It uses OS_Guess's matchSignature function verbatim.
A subpackage dependency does not in fact define a subpackage relationship necessarily. What the subpackage dependency does is define a package dependency, and tell the installer to ignore any conflicts between files in the subpackage and the parent package. This last sentence is long and confusing, so an example is in order:
Package A defines a subpackage dependency on Package B. Let's say Package A's version 1.0.0 file list is:
a.php
b.php
Now, Package A releases version 1.1.0, and splits into two packages, Package A and Package B. Package A 1.1.0 contains:
a.phpand package B version 1.1.0 contains
b.phpNow, if Package A has a simple package dependency on Package B, this problem would occur when upgrading package A:
Package B is upgraded first as a dependency. When installing, each file in Package B is checked to see if it conflicts with a file in any installed package, including Package A. The installer will find that Package A version 1.0.0 has b.php installed, which conflicts with Package B's b.php, and the install will fail. The subpackage dependency removes this check, and allows replacing of Package A-1.0.0's b.php with Package B-1.1.0's b.php.
Basic dependencies
Dependencies that rely on versioning, which includes package, php, pearinstaller and extension dependencies all use the same basic scheme for defining a dependency. Unlike the old way of doing things:
[xml]<dep type="php" rel="ge" version="4.3.0"/>
<dep type="php" rel="lt" version="5.0.0"/>[/xml]
Where each version had to be specified in a separate dep tag, the new system allows aggregating all versioning into a single location. This makes machine processing more accurate, and user error messages more informative ("Package A requires PHP version >= 4.3.0 and < 5.0.0, != 4.3.10"). In addition, the type attribute has been expanded to a tag. Dependency types will not change rapidly, any major change would require an upgrade to package.xml most likely, even with the <dep> system, and using tags over attributes makes xml validation simpler and more reliable cross-system. So, <dep type="php"/> becomes:
[xml]<dependencies>
<php>
<min>4.3.0</min>
<max>5.0.0</max>
<exclude>4.3.10</exclude>
<exclude>5.0.0</exclude>
</php>
</dependencies>[/xml]
This simple listing of versions tells the installer the same information in a single tag. "Any PHP version between 4.3.0 and 5.0.0 inclusive, except for 4.3.10 and 5.0.0"
The rel="has" is implemented by simply excluding <min> <max> or <exclude>
[xml] <package>
<name>Foo</name>
<channel>pear.php.net</channel>
</package>
[/xml]
rel="not" is implemented by the empty tag <conflicts/>
[xml] <package>
<name>BadPackage</name>
<channel>foo.example.com</channel>
<conflicts/>
</package>[/xml]
This tells the installer that the package defined by package.xml cannot co-exist with BadPackage from channel foo.example.com, no matter what the version. This is most useful for defining conflicts between file installs, or two packages that provide the same extension, for instance.
Package dependencies can be used to note a dependency on a package distributed by a channel, as in the example above. For the first time, it is possible to specify a dependency on a single package identified by a physical URI. For many developers, they provide a few private utility classes that are used by all of their freelance jobs. Setting up a channel server just would not be worth the effort.
For these developers, this kind of package dependency can be used:
[xml] <package>
<name>Foo</name>
<uri>http://www.example.com/path/to/Foo.tgz</uri>
</package>[/xml]
In the Foo.tgz, the package.xml must define the same uri, for security reasons, or installation will fail.
[xml]<package version="2.0">
<name>Foo</name>
<uri>http://www.example.com/path/to/Foo.tgz</uri>
...[/xml]
Each of these static packages is automatically placed in the __uri pseudo-channel, to prevent malicious overwriting/upgrading of packages from other channels.
Advanced dependencies
Basic dependencies are fantastic for most small packages or non-enterprise applications. For the robust application, it is often not enough to say "Use Package A, oh, I don't know, any version newer than 1.3.0" It is important to verify that any upgrade will not break an existing package. This can be done using the <recommendedversion> tag in a dependency.
Let's look at this example package dependency from hypothetical package MegaApp version 1.3.0:
[xml] <package>
<name>Blah</name>
<min>1.4.0</min>
<recommendedversion>1.5.3</recommendedversion>
</package>
[/xml]
In this case, an install of MegaApp 1.3.0 will fail unless Blah version 1.5.3 is installed. However, if a newer version of Blah is released that supports MegaApp 1.3.0, Blah 1.5.4 can specifically state in its package.xml that it has been tested with MegaApp 1.3.0 and certified to work using the <compatible> tag:
[xml] <compatible>
<name>MegaApp</name>
<min>1.2.0</min>
<max>1.3.0</max>
</compatible>
[/xml]
This will allow extremely strict dependency validation, while still allowing seamless upgrade of tested application-aware components. If an install is prevented by a recommendedversion tag, the --force option can be used to override this situation without penalty. Unlike other dependencies, <recommendedversion> does not affect uninstall at all.
In Part 2, I will expose the most dramatic addition to PEAR's dependencies: optional dependency feature groups.
This is a must read for any PEAR developer: Greg posted detailled information about news and changes regarding PEAR 1.4 (coming quite soon now) and package.xml v2.0. The main focus of his post (#1 in a series of 2) is on package dependencies.
Tracked: Feb 11, 00:32
In Part 1 of the Dependencies series, I wrote of basic dependencies, and the structure of how things work. In Part 2, I will discuss several crucial changes to the way dependencies work that help to drastically improve application support in PEAR.For y
Tracked: Feb 16, 23:19