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
, definesphp_dir
as/usr/local/lib/php
-
User configuration in
/home/username/.pearrc
, definesphp_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.
Installing and Building PECL extensions
PEAR's handling of PECL extensions has been somewhat dodgy, with reports of issues with phpize failing, and other problems. Pyrus attempts to fix this through two major changes to the way PECL packages are built and installed.
-
PECL packages are installed into a new location
src/
and then built directly inside this location. - The same tool process used to build extensions by hand is used verbatim by pyrus to build the extension
PECL installation changes
PEAR builds PECL packages by creating a temporary directory, installing all of the source files into this directory, building the extension, harvesting built files, and finally removes the temporary directory. This system works most of the time, but if there is a problem, it is impossible to debug because the sources are removed.
Pyrus solves this by splitting PECL package installation into two components,
installation and build. The installation process simply places the
source files into a sub-directory of the src_dir
configuration
variable, and thus makes it possible to debug problems or even apply patches
to the source and re-build.
In addition, because installation is separate from the actual building, PECL packages can now implement post-install scripts to handle truly complex configuration of extensions beyond what configure options can handle.
PECL build
The new build
command enhances PEAR's package building by
directly calling this sequence:
phpize --clean
phpize
./configure [any options specified by <configureoptions>]
make
make install
This is the same sequence one would use to build a PECL extension by hand. In addition, proc_open() is used instead of popen(), which allows better monitoring and control of the processes in question.
Lastly, Pyrus is more cross-platform than PEAR, as it replaces a
call to find
and xargs
with native
PHP iteration over the modules directory when listing extension components
that were built.