Pyrus: Improvements from the PEAR Installer

Overview Pyrus is a re-factored version of the PEAR installer, re-designed for new features available in PHP 5.3 and newer. As a result, Pyrus is more robust than PEAR as well as faster. Several of the subtle design flaws in the PEAR Installer have been fixed, and so Pyrus is more stable than the PEAR Installer for handling an existing PEAR repository. Here is a brief summary of the differences from PEAR: Simpler to use than PEAR Pyrus is distributed as a single file, pyrus.phar. Because of PHP's new phar extension, Pyrus does not need to be installed, and can run directly from the file pyrus.phar. Pyrus also simplifies the command-line options available, and provides a far greater range of developer tools for creating, managing and distributing packages through tools such as the simple channel server and package.xml creation command make . More secure than PEAR Several security vulnerabilities in the design of PEAR were discovered due to the particular Command Pattern implementation used to detect file roles, commands, and other plugins. Pyrus fixes this by requiring that all plugins be installed into a centralized location separate from the actual PEAR installation. In addition, installation of plugins cannot happen at the same time as installation of packages, thus the enforced separation ensures a level of security that is much higher than PEAR supports, while preserving the flexibility that extending the installer provides. Pyrus also feaures true package signing and signature verification using OpenSSL PKCS#12 and X.509 certificates. This allows users to directly verify the validity of a package, protecting from man-in-the-middle attacks and other potential disruptions of a package release. This feature requires the openssl extension, which is not enabled by default. PEAR supports signing packages using PGP keys, but has no mechanism in place to verify the signed packages. Pyrus will refuse to install a signed package without verifying the signature even if the openssl extension is not enabled. In addition, the new paranoia setting can be used to control how upgrades are performed to releases that change the API, helping to guarantee safe upgrades to future releases. Smaller than PEAR Because Pyrus takes advantage of PHP 5.3's built in support for XML processing, archive handling, and advanced structures through the simplexml, libxml2, phar, sqlite3, and spl extensions, Pyrus is significantly smaller than PEAR, and as a result consumes far less memory to accomplish its tasks. Faster than PEAR Pyrus is also faster than PEAR because of its reliance on built-in features of PHP 5.3 and a more structured object-oriented design. More robust than PEAR Pyrus has redundant registries in XML and Sqlite3 database formats, as well as support for the existing PEAR registry. Reconstruction of a corrupted registry is simple and fully supported. In addition, all installation tasks occur within an atomic transaction, including file installation and removal, so that if an installation or uninstall command fails mid-stream, or something as drastic as a power failure occurs, the PEAR installation will be not be left in a half-installed state. More flexible than PEAR Pyrus supports cascading installations, so that a system-wide installation of core packages can be recognized. By default, include_path is used to detect PEAR installations, but a different location for a PEAR installation can be passed directly to Pyrus as its first argument. Convention over configuration allows packages constructed with the new PEAR2 coding standards to be installed simply by extracting the archive, and then later upgraded using Pyrus without the intermediate step of using Pyrus to install the packages. For the first time, this allows a try-before-you-buy approach to be possible. The same principle also makes bundling a PEAR2 package in another package's source repository possible, and Pyrus can be used to easily upgrade the package or revert to a previous version. More tested than PEAR Pyrus has been developed with extensive unit testing and xdebug coverage data has been used to verify that the code is being executed. As a result, the first alpha release of Pyrus has 10% higher code coverage than the most recent stable version of the PEAR Installer.

Configuration files One of the most important conceptual changes in Pyrus is how configuration is handled. PEAR was designed to handle at most 2 installations by default, a system and a user PEAR installation, and it excels at this. As soon as PEAR is used on multiple installations, a separate configuration file must be specified (as in pear -c /path/to/another/pear.conf install blah ). This leads to what is colloquially referred to as "config hell", where it is easy to accidentally install things into the wrong place without realizing it. Pyrus's configuration handling is specifically engineered to eliminate config hell, and to make handling multiple PEAR installations simple. PEAR stores all configuration values in a single configuration file, and allows specifying a different configuration file for different setups. In addition, PEAR supports automatic cascade of a system configuration file and a user configuration file. The configuration values are used when installing applications, and for customizing things like the path to php in the PEAR Installer's pear command. Configuration files are stored separate from the PEAR installations that they represent. Pyrus instead splits up configuration files into two separate components: one file contains user customizations such as the preferred stability of packages to install, the username and password for logging into a channel, the verbose setting and so on. Configuration variables that affect where to install files are stored in a separate configuration that is tightly bound to the PEAR installation. Thus, a PEAR configuration setup might look like: System configuration in /etc/pear.conf , defines php_dir as /usr/local/lib/php

User configuration in /home/username/.pearrc , defines php_dir as /home/username/pear .

include_path is set to /home/username/pear:/usr/local/lib/php . The equivalent configuration setup with Pyrus would look like: Pyrus-based installation in /usr/local/lib/pear , system configuration stored in /usr/local/pear/.config and php files in /usr/local/lib/pear/php .

Another Pyrus-based installation in /home/username/pear , system configuration stored in /home/username/pear/.config and php files in /home/username/pear/php .

User configuration in /home/username/.pear/pearconfig.xml .

include_path is set to /home/username/pear:/usr/local/lib/pear/php . By default, Pyrus uses the include_path to locate PEAR installations, but this is configurable with the new user configuration variable my_pear_path , which is a PATH_SEPARATOR separated list of paths to PEAR installations. In addition, an explicit path can be directly passed to Pyrus: php pyrus.phar /home/username/pear:/usr/local/lib/pear list-packages The above command will list the installed packages in both registries in /home/username/pear and in /usr/local/lib/pear/php . A detailed reference of Pyrus's handling of configuration files is here

Registries Pyrus fully supports PEAR's registry format, but introduces 2 new registry formats, an sqlite3 database-based registry, and an XML file-based registry. These registries are fully redundant, and can be used to repair or reconstruct a corrupt registry. In addition, unlike PEAR, which stores the registry in the same directory as the PHP source files, Pyrus stores the registry in its parent directory. Thus, PHP files stored in /usr/local/lib/php have their registry in /usr/local/lib . For backwards compatibility, an older PEAR registry is always stored in the location the PEAR Installer expects it to be stored. Pyrus is intelligent enough to detect which registries are present, and to use them. If only an older PEAR registry exists, Pyrus will not automatically upgrade to the newer format. However, the upgrade-registries command is available to convert from an older registry to the newer format. Some of the benefits of the newer registry format include much speedier processing of a large registry at installation time due to Sqlite3's speedy processing. Additionally, truly safe uninstall-time resolution of dependencies is possible, something that PEAR can only do for relatively simple package dependency trees. In addition, the XML registry consists of storing the package.xml and channel.xml files for package releases in the same location that they are packaged. This is what makes it possible to extract a package created with Pyrus and then later use Pyrus to upgrade it. For instance, the hypothetical PEAR2_Foo package from channel pear2.php.net version 1.2.3 will store its package.xml in path .xmlregistry/packages/pear2.php.net/PEAR2_Foo/1.2.3-package.xml inside the archive, so that when it is extracted, it lines up exactly with how the package would look on disk when installed with the XML registry.

package.xml changes Pyrus no longer supports package.xml version 1.0, although it will include a package.xml version 1.0 in an archive designed to support both earlier PEAR versions and the more recent versions. It does not validate the package.xml, however, and so it is important to validate any older package.xml format using PEAR and not Pyrus. In addition, Pyrus has introduced support for PEAR2 packages that can be extracted to disk and then later upgraded using Pyrus. To implement this feature, Pyrus transforms paths in a different way from PEAR. For example, this entry from a package.xml: <dir name="php" baseinstalldir="PEAR2"> <dir name="Pyrus"> <dir name="Developer"> <dir name="CoverageAnalyzer"> <dir name="SourceFile"> <file role="php" name="PerTest.php"/> </dir> </dir> </dir> </dir> </dir> would cause PEAR to install PerTest.php into the relative path PEAR2/php/Pyrus/Developer/CoverageAnalyzer/SourceFile/PerTest.php . Pyrus, however, recognizes that php is actually the default value of the php_dir system configuration variable, and strips it from the path, resulting in PerTest.php being installed into the path: PEAR2/Pyrus/Developer/CoverageAnalyzer/SourceFile/PerTest.php . To enable this handling, one need only set the <pearinstaller> dependency to version 2.0.0a1 or newer. Pyrus will automatically recognize any package.xml with a <pearinstaller> dependency on any version of the PEAR Installer as an older package.xml, and will not perform the magic removal of configuration values from directories. No other changes have been made to package.xml handling, except that the default version of package.xml used when generating a package.xml is version 2.1, which has been supported by the PEAR Installer since version 1.5.0 .

Extending Pyrus: plugins The PEAR Installer allowed packages to install custom commands as well as custom file roles and custom file tasks that are used in package.xml. Pyrus also allows this, but the format of plugins is very different. If you are simply a user of PEAR, you probably won't notice the difference, except that some packages that use custom file roles or tasks will not be installable by Pyrus until the maintainer releases an update that will work with both PEAR and Pyrus. PEAR extensions are installed directly into the location where the PEAR installer is located. Thus, if PEAR is located in /usr/local/lib/php/PEAR , a custom command must install its XML information file and PHP script into /usr/local/lib/php/PEAR/Command , a custom file role must install its XML information file and PHP script into /usr/local/lib/php/PEAR/Installer/Role and a custom file task must install its PHP script into /usr/local/lib/php/PEAR/Task . Pyrus is distributed as a phar archive, so this model is no longer physically possible, one cannot just magically insert files into the phar archive without considerable pain and annoyance (the phar.readonly INI setting must be disabled by hand). Instead, Pyrus installs all plugins into a location specified by the new plugins_dir user configuration variable. By default, this installs plugins into $HOME/.pear/plugins on unix systems, and My Documents\pear\plugins on Windows. All plugins to Pyrus now must provide an xml file with one of the three new file roles customcommand , customrole or customtask in package.xml. Pyrus uses the information in the XML file to locate the PHP script that will execute the plugin. In addition, only one plugin is allowed per package, and the first one Pyrus encounters is the one that will be used. More information on custom plugins is provided in the Pyrus plugins section of the manual. For developers of existing PEAR custom roles/tasks and post-install scripts, a special kind of file role that allows configuration of your package after installation, making your work compatible with Pyrus can be accomplished. See the documentation on Custom Roles, Custom Tasks, and Post-install scripts.