3  * PEAR_Dependency2, advanced dependency validation
 
   9  * @author     Greg Beaver <cellog@php.net>
 
  10  * @copyright  1997-2009 The Authors
 
  11  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 
  12  * @link       http://pear.php.net/package/PEAR
 
  13  * @since      File available since Release 1.4.0a1
 
  17  * Required for the PEAR_VALIDATE_* constants
 
  19 require_once 'PEAR/Validate.php';
 
  22  * Dependency check for PEAR packages
 
  24  * This class handles both version 1.0 and 2.0 dependencies
 
  25  * WARNING: *any* changes to this class must be duplicated in the
 
  26  * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc,
 
  27  * or unit tests will not actually validate the changes
 
  30  * @author     Greg Beaver <cellog@php.net>
 
  31  * @copyright  1997-2009 The Authors
 
  32  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 
  33  * @version    Release: 1.10.1
 
  34  * @link       http://pear.php.net/package/PEAR
 
  35  * @since      Class available since Release 1.4.0a1
 
  37 class PEAR_Dependency2
 
  40      * One of the PEAR_VALIDATE_* states
 
  41      * @see PEAR_VALIDATE_NORMAL
 
  47      * Command-line options to install/upgrade/uninstall commands
 
  68      * @var PEAR_DependencyDB
 
  73      * Output of PEAR_Registry::parsedPackageName()
 
  80      * @param array installation options
 
  81      * @param array format of PEAR_Registry::parsedPackageName()
 
  82      * @param int installation state (one of PEAR_VALIDATE_*)
 
  84     function __construct(&$config, $installoptions, $package,
 
  85                               $state = PEAR_VALIDATE_INSTALLING)
 
  87         $this->_config = &$config;
 
  88         if (!class_exists('PEAR_DependencyDB')) {
 
  89             require_once 'PEAR/DependencyDB.php';
 
  92         if (isset($installoptions['packagingroot'])) {
 
  93             // make sure depdb is in the right location
 
  94             $config->setInstallRoot($installoptions['packagingroot']);
 
  97         $this->_registry = &$config->getRegistry();
 
  98         $this->_dependencydb = &PEAR_DependencyDB::singleton($config);
 
  99         if (isset($installoptions['packagingroot'])) {
 
 100             $config->setInstallRoot(false);
 
 103         $this->_options = $installoptions;
 
 104         $this->_state = $state;
 
 105         if (!class_exists('OS_Guess')) {
 
 106             require_once 'OS/Guess.php';
 
 109         $this->_os = new OS_Guess;
 
 110         $this->_currentPackage = $package;
 
 113     function _getExtraString($dep)
 
 116         if (isset($dep['uri'])) {
 
 120         if (isset($dep['recommended'])) {
 
 121             $extra .= 'recommended version ' . $dep['recommended'];
 
 123             if (isset($dep['min'])) {
 
 124                 $extra .= 'version >= ' . $dep['min'];
 
 127             if (isset($dep['max'])) {
 
 128                 if ($extra != ' (') {
 
 131                 $extra .= 'version <= ' . $dep['max'];
 
 134             if (isset($dep['exclude'])) {
 
 135                 if (!is_array($dep['exclude'])) {
 
 136                     $dep['exclude'] = array($dep['exclude']);
 
 139                 if ($extra != ' (') {
 
 143                 $extra .= 'excluded versions: ';
 
 144                 foreach ($dep['exclude'] as $i => $exclude) {
 
 154         if ($extra == ' ()') {
 
 162      * This makes unit-testing a heck of a lot easier
 
 170      * This makes unit-testing a heck of a lot easier
 
 172     function getsysname()
 
 174         return $this->_os->getSysname();
 
 178      * Specify a dependency on an OS.  Use arch for detailed os/processor information
 
 180      * There are two generic OS dependencies that will be the most common, unix and windows.
 
 181      * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix
 
 183     function validateOsDependency($dep)
 
 185         if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) {
 
 189         if ($dep['name'] == '*') {
 
 193         $not = isset($dep['conflicts']) ? true : false;
 
 194         switch (strtolower($dep['name'])) {
 
 197                     if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') {
 
 198                         if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 199                             return $this->raiseError("Cannot install %s on Windows");
 
 202                         return $this->warning("warning: Cannot install %s on Windows");
 
 205                     if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') {
 
 206                         if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 207                             return $this->raiseError("Can only install %s on Windows");
 
 210                         return $this->warning("warning: Can only install %s on Windows");
 
 215                 $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix');
 
 217                     if (in_array($this->getSysname(), $unices)) {
 
 218                         if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 219                             return $this->raiseError("Cannot install %s on any Unix system");
 
 222                         return $this->warning( "warning: Cannot install %s on any Unix system");
 
 225                     if (!in_array($this->getSysname(), $unices)) {
 
 226                         if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 227                             return $this->raiseError("Can only install %s on a Unix system");
 
 230                         return $this->warning("warning: Can only install %s on a Unix system");
 
 236                     if (strtolower($dep['name']) == strtolower($this->getSysname())) {
 
 237                         if (!isset($this->_options['nodeps']) &&
 
 238                               !isset($this->_options['force'])) {
 
 239                             return $this->raiseError('Cannot install %s on ' . $dep['name'] .
 
 240                                 ' operating system');
 
 243                         return $this->warning('warning: Cannot install %s on ' .
 
 244                             $dep['name'] . ' operating system');
 
 247                     if (strtolower($dep['name']) != strtolower($this->getSysname())) {
 
 248                         if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 249                             return $this->raiseError('Cannot install %s on ' .
 
 250                                 $this->getSysname() .
 
 251                                 ' operating system, can only install on ' . $dep['name']);
 
 254                         return $this->warning('warning: Cannot install %s on ' .
 
 255                             $this->getSysname() .
 
 256                             ' operating system, can only install on ' . $dep['name']);
 
 264      * This makes unit-testing a heck of a lot easier
 
 266     function matchSignature($pattern)
 
 268         return $this->_os->matchSignature($pattern);
 
 272      * Specify a complex dependency on an OS/processor/kernel version,
 
 273      * Use OS for simple operating system dependency.
 
 275      * This is the only dependency that accepts an eregable pattern.  The pattern
 
 276      * will be matched against the php_uname() output parsed by OS_Guess
 
 278     function validateArchDependency($dep)
 
 280         if ($this->_state != PEAR_VALIDATE_INSTALLING) {
 
 284         $not = isset($dep['conflicts']) ? true : false;
 
 285         if (!$this->matchSignature($dep['pattern'])) {
 
 287                 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 288                     return $this->raiseError('%s Architecture dependency failed, does not ' .
 
 289                         'match "' . $dep['pattern'] . '"');
 
 292                 return $this->warning('warning: %s Architecture dependency failed, does ' .
 
 293                     'not match "' . $dep['pattern'] . '"');
 
 300             if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 301                 return $this->raiseError('%s Architecture dependency failed, required "' .
 
 302                     $dep['pattern'] . '"');
 
 305             return $this->warning('warning: %s Architecture dependency failed, ' .
 
 306                 'required "' . $dep['pattern'] . '"');
 
 313      * This makes unit-testing a heck of a lot easier
 
 315     function extension_loaded($name)
 
 317         return extension_loaded($name);
 
 321      * This makes unit-testing a heck of a lot easier
 
 323     function phpversion($name = null)
 
 325         if ($name !== null) {
 
 326             return phpversion($name);
 
 332     function validateExtensionDependency($dep, $required = true)
 
 334         if ($this->_state != PEAR_VALIDATE_INSTALLING &&
 
 335               $this->_state != PEAR_VALIDATE_DOWNLOADING) {
 
 339         $loaded = $this->extension_loaded($dep['name']);
 
 340         $extra  = $this->_getExtraString($dep);
 
 341         if (isset($dep['exclude'])) {
 
 342             if (!is_array($dep['exclude'])) {
 
 343                 $dep['exclude'] = array($dep['exclude']);
 
 347         if (!isset($dep['min']) && !isset($dep['max']) &&
 
 348             !isset($dep['recommended']) && !isset($dep['exclude'])
 
 351                 if (isset($dep['conflicts'])) {
 
 352                     if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 353                         return $this->raiseError('%s conflicts with PHP extension "' .
 
 354                             $dep['name'] . '"' . $extra);
 
 357                     return $this->warning('warning: %s conflicts with PHP extension "' .
 
 358                         $dep['name'] . '"' . $extra);
 
 364             if (isset($dep['conflicts'])) {
 
 369                 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 370                     return $this->raiseError('%s requires PHP extension "' .
 
 371                         $dep['name'] . '"' . $extra);
 
 374                 return $this->warning('warning: %s requires PHP extension "' .
 
 375                     $dep['name'] . '"' . $extra);
 
 378             return $this->warning('%s can optionally use PHP extension "' .
 
 379                 $dep['name'] . '"' . $extra);
 
 383             if (isset($dep['conflicts'])) {
 
 388                 return $this->warning('%s can optionally use PHP extension "' .
 
 389                     $dep['name'] . '"' . $extra);
 
 392             if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 393                 return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
 
 397             return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
 
 401         $version = (string) $this->phpversion($dep['name']);
 
 402         if (empty($version)) {
 
 407         if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) {
 
 411         if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) {
 
 415         if ($fail && !isset($dep['conflicts'])) {
 
 416             if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 417                 return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
 
 418                     '"' . $extra . ', installed version is ' . $version);
 
 421             return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
 
 422                 '"' . $extra . ', installed version is ' . $version);
 
 423         } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) {
 
 424             if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 425                 return $this->raiseError('%s conflicts with PHP extension "' .
 
 426                     $dep['name'] . '"' . $extra . ', installed version is ' . $version);
 
 429             return $this->warning('warning: %s conflicts with PHP extension "' .
 
 430                 $dep['name'] . '"' . $extra . ', installed version is ' . $version);
 
 433         if (isset($dep['exclude'])) {
 
 434             foreach ($dep['exclude'] as $exclude) {
 
 435                 if (version_compare($version, $exclude, '==')) {
 
 436                     if (isset($dep['conflicts'])) {
 
 440                     if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 441                         return $this->raiseError('%s is not compatible with PHP extension "' .
 
 442                             $dep['name'] . '" version ' .
 
 446                     return $this->warning('warning: %s is not compatible with PHP extension "' .
 
 447                         $dep['name'] . '" version ' .
 
 449                 } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
 
 450                     if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 451                         return $this->raiseError('%s conflicts with PHP extension "' .
 
 452                             $dep['name'] . '"' . $extra . ', installed version is ' . $version);
 
 455                     return $this->warning('warning: %s conflicts with PHP extension "' .
 
 456                         $dep['name'] . '"' . $extra . ', installed version is ' . $version);
 
 461         if (isset($dep['recommended'])) {
 
 462             if (version_compare($version, $dep['recommended'], '==')) {
 
 466             if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 467                 return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] .
 
 468                     ' version "' . $version . '"' .
 
 469                     ' is not the recommended version "' . $dep['recommended'] .
 
 470                     '", but may be compatible, use --force to install');
 
 473             return $this->warning('warning: %s dependency: PHP extension ' .
 
 474                 $dep['name'] . ' version "' . $version . '"' .
 
 475                 ' is not the recommended version "' . $dep['recommended'].'"');
 
 481     function validatePhpDependency($dep)
 
 483         if ($this->_state != PEAR_VALIDATE_INSTALLING &&
 
 484               $this->_state != PEAR_VALIDATE_DOWNLOADING) {
 
 488         $version = $this->phpversion();
 
 489         $extra   = $this->_getExtraString($dep);
 
 490         if (isset($dep['exclude'])) {
 
 491             if (!is_array($dep['exclude'])) {
 
 492                 $dep['exclude'] = array($dep['exclude']);
 
 496         if (isset($dep['min'])) {
 
 497             if (!version_compare($version, $dep['min'], '>=')) {
 
 498                 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 499                     return $this->raiseError('%s requires PHP' .
 
 500                         $extra . ', installed version is ' . $version);
 
 503                 return $this->warning('warning: %s requires PHP' .
 
 504                     $extra . ', installed version is ' . $version);
 
 508         if (isset($dep['max'])) {
 
 509             if (!version_compare($version, $dep['max'], '<=')) {
 
 510                 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 511                     return $this->raiseError('%s requires PHP' .
 
 512                         $extra . ', installed version is ' . $version);
 
 515                 return $this->warning('warning: %s requires PHP' .
 
 516                     $extra . ', installed version is ' . $version);
 
 520         if (isset($dep['exclude'])) {
 
 521             foreach ($dep['exclude'] as $exclude) {
 
 522                 if (version_compare($version, $exclude, '==')) {
 
 523                     if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 524                         return $this->raiseError('%s is not compatible with PHP version ' .
 
 528                     return $this->warning(
 
 529                         'warning: %s is not compatible with PHP version ' .
 
 539      * This makes unit-testing a heck of a lot easier
 
 541     function getPEARVersion()
 
 546     function validatePearinstallerDependency($dep)
 
 548         $pearversion = $this->getPEARVersion();
 
 549         $extra = $this->_getExtraString($dep);
 
 550         if (isset($dep['exclude'])) {
 
 551             if (!is_array($dep['exclude'])) {
 
 552                 $dep['exclude'] = array($dep['exclude']);
 
 556         if (version_compare($pearversion, $dep['min'], '<')) {
 
 557             if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 558                 return $this->raiseError('%s requires PEAR Installer' . $extra .
 
 559                     ', installed version is ' . $pearversion);
 
 562             return $this->warning('warning: %s requires PEAR Installer' . $extra .
 
 563                 ', installed version is ' . $pearversion);
 
 566         if (isset($dep['max'])) {
 
 567             if (version_compare($pearversion, $dep['max'], '>')) {
 
 568                 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 569                     return $this->raiseError('%s requires PEAR Installer' . $extra .
 
 570                         ', installed version is ' . $pearversion);
 
 573                 return $this->warning('warning: %s requires PEAR Installer' . $extra .
 
 574                     ', installed version is ' . $pearversion);
 
 578         if (isset($dep['exclude'])) {
 
 579             if (!isset($dep['exclude'][0])) {
 
 580                 $dep['exclude'] = array($dep['exclude']);
 
 583             foreach ($dep['exclude'] as $exclude) {
 
 584                 if (version_compare($exclude, $pearversion, '==')) {
 
 585                     if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 586                         return $this->raiseError('%s is not compatible with PEAR Installer ' .
 
 587                             'version ' . $exclude);
 
 590                     return $this->warning('warning: %s is not compatible with PEAR ' .
 
 591                         'Installer version ' . $exclude);
 
 599     function validateSubpackageDependency($dep, $required, $params)
 
 601         return $this->validatePackageDependency($dep, $required, $params);
 
 605      * @param array dependency information (2.0 format)
 
 606      * @param boolean whether this is a required dependency
 
 607      * @param array a list of downloaded packages to be installed, if any
 
 608      * @param boolean if true, then deps on pear.php.net that fail will also check
 
 609      *                against pecl.php.net packages to accommodate extensions that have
 
 610      *                moved to pecl.php.net from pear.php.net
 
 612     function validatePackageDependency($dep, $required, $params, $depv1 = false)
 
 614         if ($this->_state != PEAR_VALIDATE_INSTALLING &&
 
 615               $this->_state != PEAR_VALIDATE_DOWNLOADING) {
 
 619         if (isset($dep['providesextension'])) {
 
 620             if ($this->extension_loaded($dep['providesextension'])) {
 
 623                 $subdep['name'] = $subdep['providesextension'];
 
 624                 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 
 625                 $ret = $this->validateExtensionDependency($subdep, $required);
 
 626                 PEAR::popErrorHandling();
 
 627                 if (!PEAR::isError($ret)) {
 
 633         if ($this->_state == PEAR_VALIDATE_INSTALLING) {
 
 634             return $this->_validatePackageInstall($dep, $required, $depv1);
 
 637         if ($this->_state == PEAR_VALIDATE_DOWNLOADING) {
 
 638             return $this->_validatePackageDownload($dep, $required, $params, $depv1);
 
 642     function _validatePackageDownload($dep, $required, $params, $depv1 = false)
 
 644         $dep['package'] = $dep['name'];
 
 645         if (isset($dep['uri'])) {
 
 646             $dep['channel'] = '__uri';
 
 649         $depname = $this->_registry->parsedPackageNameToString($dep, true);
 
 651         foreach ($params as $param) {
 
 653                   array('package' => $dep['name'],
 
 654                         'channel' => $dep['channel']))) {
 
 659             if ($depv1 && $dep['channel'] == 'pear.php.net') {
 
 661                   array('package' => $dep['name'],
 
 662                         'channel' => 'pecl.php.net'))) {
 
 669         if (!$found && isset($dep['providesextension'])) {
 
 670             foreach ($params as $param) {
 
 671                 if ($param->isExtension($dep['providesextension'])) {
 
 679             $version = $param->getVersion();
 
 683             if ($this->_registry->packageExists($dep['name'], $dep['channel'])) {
 
 686                 $version = $this->_registry->packageinfo($dep['name'], 'version',
 
 689                 if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'],
 
 693                     $version = $this->_registry->packageinfo($dep['name'], 'version',
 
 696                     $version = 'not installed or downloaded';
 
 703         $extra = $this->_getExtraString($dep);
 
 704         if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
 
 705             $dep['exclude'] = array($dep['exclude']);
 
 708         if (!isset($dep['min']) && !isset($dep['max']) &&
 
 709               !isset($dep['recommended']) && !isset($dep['exclude'])
 
 711             if ($installed || $downloaded) {
 
 712                 $installed = $installed ? 'installed' : 'downloaded';
 
 713                 if (isset($dep['conflicts'])) {
 
 716                         $rest = ", $installed version is " . $version;
 
 719                     if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 720                         return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest);
 
 723                     return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest);
 
 729             if (isset($dep['conflicts'])) {
 
 734                 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 735                     return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
 
 738                 return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
 
 741             return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
 
 744         if (!$installed && !$downloaded) {
 
 745             if (isset($dep['conflicts'])) {
 
 750                 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 751                     return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
 
 754                 return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
 
 757             return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
 
 761         if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) {
 
 765         if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) {
 
 769         if ($fail && !isset($dep['conflicts'])) {
 
 770             $installed = $installed ? 'installed' : 'downloaded';
 
 771             $dep['package'] = $dep['name'];
 
 772             $dep = $this->_registry->parsedPackageNameToString($dep, true);
 
 773             if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 774                 return $this->raiseError('%s requires package "' . $depname . '"' .
 
 775                     $extra . ", $installed version is " . $version);
 
 778             return $this->warning('warning: %s requires package "' . $depname . '"' .
 
 779                 $extra . ", $installed version is " . $version);
 
 780         } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail &&
 
 781               isset($dep['conflicts']) && !isset($dep['exclude'])) {
 
 782             $installed = $installed ? 'installed' : 'downloaded';
 
 783             if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 784                 return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra .
 
 785                     ", $installed version is " . $version);
 
 788             return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
 
 789                 $extra . ", $installed version is " . $version);
 
 792         if (isset($dep['exclude'])) {
 
 793             $installed = $installed ? 'installed' : 'downloaded';
 
 794             foreach ($dep['exclude'] as $exclude) {
 
 795                 if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) {
 
 796                     if (!isset($this->_options['nodeps']) &&
 
 797                           !isset($this->_options['force'])
 
 799                         return $this->raiseError('%s is not compatible with ' .
 
 800                             $installed . ' package "' .
 
 801                             $depname . '" version ' .
 
 805                     return $this->warning('warning: %s is not compatible with ' .
 
 806                         $installed . ' package "' .
 
 807                         $depname . '" version ' .
 
 809                 } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
 
 810                     $installed = $installed ? 'installed' : 'downloaded';
 
 811                     if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
 812                         return $this->raiseError('%s conflicts with package "' . $depname . '"' .
 
 813                             $extra . ", $installed version is " . $version);
 
 816                     return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
 
 817                         $extra . ", $installed version is " . $version);
 
 822         if (isset($dep['recommended'])) {
 
 823             $installed = $installed ? 'installed' : 'downloaded';
 
 824             if (version_compare($version, $dep['recommended'], '==')) {
 
 828             if (!$found && $installed) {
 
 829                 $param = $this->_registry->getPackage($dep['name'], $dep['channel']);
 
 834                 foreach ($params as $parent) {
 
 835                     if ($parent->isEqual($this->_currentPackage)) {
 
 842                     if ($param->isCompatible($parent)) {
 
 845                 } else { // this is for validPackage() calls
 
 846                     $parent = $this->_registry->getPackage($this->_currentPackage['package'],
 
 847                         $this->_currentPackage['channel']);
 
 848                     if ($parent !== null && $param->isCompatible($parent)) {
 
 854             if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) &&
 
 855                   !isset($this->_options['loose'])
 
 857                 return $this->raiseError('%s dependency package "' . $depname .
 
 858                     '" ' . $installed . ' version ' . $version .
 
 859                     ' is not the recommended version ' . $dep['recommended'] .
 
 860                     ', but may be compatible, use --force to install');
 
 863             return $this->warning('warning: %s dependency package "' . $depname .
 
 864                 '" ' . $installed . ' version ' . $version .
 
 865                 ' is not the recommended version ' . $dep['recommended']);
 
 871     function _validatePackageInstall($dep, $required, $depv1 = false)
 
 873         return $this->_validatePackageDownload($dep, $required, array(), $depv1);
 
 877      * Verify that uninstalling packages passed in to command line is OK.
 
 879      * @param PEAR_Installer $dl
 
 880      * @return PEAR_Error|true
 
 882     function validatePackageUninstall(&$dl)
 
 884         if (PEAR::isError($this->_dependencydb)) {
 
 885             return $this->_dependencydb;
 
 889         // construct an array of "downloaded" packages to fool the package dependency checker
 
 890         // into using these to validate uninstalls of circular dependencies
 
 891         $downloaded = &$dl->getUninstallPackages();
 
 892         foreach ($downloaded as $i => $pf) {
 
 893             if (!class_exists('PEAR_Downloader_Package')) {
 
 894                 require_once 'PEAR/Downloader/Package.php';
 
 896             $dp = new PEAR_Downloader_Package($dl);
 
 897             $dp->setPackageFile($downloaded[$i]);
 
 902         $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' .
 
 903             strtolower($this->_currentPackage['package']);
 
 904         if (isset($dl->___uninstall_package_cache)) {
 
 905             $badpackages = $dl->___uninstall_package_cache;
 
 906             if (isset($badpackages[$memyselfandI]['warnings'])) {
 
 907                 foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
 
 908                     $dl->log(0, $warning[0]);
 
 912             if (isset($badpackages[$memyselfandI]['errors'])) {
 
 913                 foreach ($badpackages[$memyselfandI]['errors'] as $error) {
 
 914                     if (is_array($error)) {
 
 915                         $dl->log(0, $error[0]);
 
 917                         $dl->log(0, $error->getMessage());
 
 921                 if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
 
 922                     return $this->warning(
 
 923                         'warning: %s should not be uninstalled, other installed packages depend ' .
 
 927                 return $this->raiseError(
 
 928                     '%s cannot be uninstalled, other installed packages depend on this package');
 
 934         // first, list the immediate parents of each package to be uninstalled
 
 935         $perpackagelist = array();
 
 936         $allparents = array();
 
 937         foreach ($params as $i => $param) {
 
 939                 'channel' => strtolower($param->getChannel()),
 
 940                 'package' => strtolower($param->getPackage())
 
 943             $deps = $this->_dependencydb->getDependentPackages($a);
 
 945                 foreach ($deps as $d) {
 
 946                     $pardeps = $this->_dependencydb->getDependencies($d);
 
 947                     foreach ($pardeps as $dep) {
 
 948                         if (strtolower($dep['dep']['channel']) == $a['channel'] &&
 
 949                               strtolower($dep['dep']['name']) == $a['package']) {
 
 950                             if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) {
 
 951                                 $perpackagelist[$a['channel'] . '/' . $a['package']] = array();
 
 953                             $perpackagelist[$a['channel'] . '/' . $a['package']][]
 
 954                                 = array($d['channel'] . '/' . $d['package'], $dep);
 
 955                             if (!isset($allparents[$d['channel'] . '/' . $d['package']])) {
 
 956                                 $allparents[$d['channel'] . '/' . $d['package']] = array();
 
 958                             if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) {
 
 959                                 $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array();
 
 961                             $allparents[$d['channel'] . '/' . $d['package']]
 
 962                                        [$a['channel'] . '/' . $a['package']][]
 
 970         // next, remove any packages from the parents list that are not installed
 
 972         foreach ($allparents as $parent => $d1) {
 
 973             foreach ($d1 as $d) {
 
 974                 if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) {
 
 977                 $remove[$parent] = true;
 
 981         // next remove any packages from the parents list that are not passed in for
 
 983         foreach ($allparents as $parent => $d1) {
 
 984             foreach ($d1 as $d) {
 
 985                 foreach ($params as $param) {
 
 986                     if (strtolower($param->getChannel()) == $d[0][0]['channel'] &&
 
 987                           strtolower($param->getPackage()) == $d[0][0]['package']) {
 
 992                 $remove[$parent] = true;
 
 996         // remove all packages whose dependencies fail
 
 997         // save which ones failed for error reporting
 
 998         $badchildren = array();
 
1001             foreach ($remove as $package => $unused) {
 
1002                 if (!isset($allparents[$package])) {
 
1006                 foreach ($allparents[$package] as $kid => $d1) {
 
1007                     foreach ($d1 as $depinfo) {
 
1008                         if ($depinfo[1]['type'] != 'optional') {
 
1009                             if (isset($badchildren[$kid])) {
 
1012                             $badchildren[$kid] = true;
 
1013                             $remove[$kid] = true;
 
1020                     // start over, we removed some children
 
1026         // next, construct the list of packages that can't be uninstalled
 
1027         $badpackages = array();
 
1028         $save = $this->_currentPackage;
 
1029         foreach ($perpackagelist as $package => $packagedeps) {
 
1030             foreach ($packagedeps as $parent) {
 
1031                 if (!isset($remove[$parent[0]])) {
 
1035                 $packagename = $this->_registry->parsePackageName($parent[0]);
 
1036                 $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']);
 
1037                 $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']);
 
1038                 $packagename['package'] = $pa->getPackage();
 
1039                 $this->_currentPackage = $packagename;
 
1040                 // parent is not present in uninstall list, make sure we can actually
 
1041                 // uninstall it (parent dep is optional)
 
1042                 $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']);
 
1043                 $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']);
 
1044                 $parentname['package'] = $pa->getPackage();
 
1045                 $parent[1]['dep']['package'] = $parentname['package'];
 
1046                 $parent[1]['dep']['channel'] = $parentname['channel'];
 
1047                 if ($parent[1]['type'] == 'optional') {
 
1048                     $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl);
 
1049                     if ($test !== true) {
 
1050                         $badpackages[$package]['warnings'][] = $test;
 
1053                     $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl);
 
1054                     if ($test !== true) {
 
1055                         $badpackages[$package]['errors'][] = $test;
 
1061         $this->_currentPackage          = $save;
 
1062         $dl->___uninstall_package_cache = $badpackages;
 
1063         if (isset($badpackages[$memyselfandI])) {
 
1064             if (isset($badpackages[$memyselfandI]['warnings'])) {
 
1065                 foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
 
1066                     $dl->log(0, $warning[0]);
 
1070             if (isset($badpackages[$memyselfandI]['errors'])) {
 
1071                 foreach ($badpackages[$memyselfandI]['errors'] as $error) {
 
1072                     if (is_array($error)) {
 
1073                         $dl->log(0, $error[0]);
 
1075                         $dl->log(0, $error->getMessage());
 
1079                 if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
 
1080                     return $this->warning(
 
1081                         'warning: %s should not be uninstalled, other installed packages depend ' .
 
1085                 return $this->raiseError(
 
1086                     '%s cannot be uninstalled, other installed packages depend on this package');
 
1093     function _validatePackageUninstall($dep, $required, $dl)
 
1095         $depname = $this->_registry->parsedPackageNameToString($dep, true);
 
1096         $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']);
 
1101         $extra = $this->_getExtraString($dep);
 
1102         if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
 
1103             $dep['exclude'] = array($dep['exclude']);
 
1106         if (isset($dep['conflicts'])) {
 
1107             return true; // uninstall OK - these packages conflict (probably installed with --force)
 
1110         if (!isset($dep['min']) && !isset($dep['max'])) {
 
1112                 return $this->warning('"' . $depname . '" can be optionally used by ' .
 
1113                         'installed package %s' . $extra);
 
1116             if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
1117                 return $this->raiseError('"' . $depname . '" is required by ' .
 
1118                     'installed package %s' . $extra);
 
1121             return $this->warning('warning: "' . $depname . '" is required by ' .
 
1122                 'installed package %s' . $extra);
 
1126         if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) {
 
1130         if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) {
 
1134         // we re-use this variable, preserve the original value
 
1135         $saverequired = $required;
 
1137             return $this->warning($depname . $extra . ' can be optionally used by installed package' .
 
1141         if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
 
1142             return $this->raiseError($depname . $extra . ' is required by installed package' .
 
1146         return $this->raiseError('warning: ' . $depname . $extra .
 
1147             ' is required by installed package "%s"');
 
1151      * validate a downloaded package against installed packages
 
1153      * As of PEAR 1.4.3, this will only validate
 
1155      * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2
 
1156      *              $pkg package identifier (either
 
1157      *                   array('package' => blah, 'channel' => blah) or an array with
 
1158      *                   index 'info' referencing an object)
 
1159      * @param PEAR_Downloader $dl
 
1160      * @param array $params full list of packages to install
 
1161      * @return true|PEAR_Error
 
1163     function validatePackage($pkg, &$dl, $params = array())
 
1165         if (is_array($pkg) && isset($pkg['info'])) {
 
1166             $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']);
 
1168             $deps = $this->_dependencydb->getDependentPackageDependencies($pkg);
 
1173             if (!class_exists('PEAR_Downloader_Package')) {
 
1174                 require_once 'PEAR/Downloader/Package.php';
 
1177             $dp = new PEAR_Downloader_Package($dl);
 
1178             if (is_object($pkg)) {
 
1179                 $dp->setPackageFile($pkg);
 
1181                 $dp->setDownloadURL($pkg);
 
1184             PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 
1185             foreach ($deps as $channel => $info) {
 
1186                 foreach ($info as $package => $ds) {
 
1187                     foreach ($params as $packd) {
 
1188                         if (strtolower($packd->getPackage()) == strtolower($package) &&
 
1189                               $packd->getChannel() == $channel) {
 
1190                             $dl->log(3, 'skipping installed package check of "' .
 
1191                                         $this->_registry->parsedPackageNameToString(
 
1192                                             array('channel' => $channel, 'package' => $package),
 
1194                                         '", version "' . $packd->getVersion() . '" will be ' .
 
1195                                         'downloaded and installed');
 
1196                             continue 2; // jump to next package
 
1200                     foreach ($ds as $d) {
 
1201                         $checker = new PEAR_Dependency2($this->_config, $this->_options,
 
1202                             array('channel' => $channel, 'package' => $package), $this->_state);
 
1204                         $required = $d['type'] == 'required';
 
1205                         $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp));
 
1206                         if (is_array($ret)) {
 
1207                             $dl->log(0, $ret[0]);
 
1208                         } elseif (PEAR::isError($ret)) {
 
1209                             $dl->log(0, $ret->getMessage());
 
1215             PEAR::popErrorHandling();
 
1219             return $this->raiseError(
 
1220                 '%s cannot be installed, conflicts with installed packages');
 
1227      * validate a package.xml 1.0 dependency
 
1229     function validateDependency1($dep, $params = array())
 
1231         if (!isset($dep['optional'])) {
 
1232             $dep['optional'] = 'no';
 
1235         list($newdep, $type) = $this->normalizeDep($dep);
 
1237             return $this->raiseError("Invalid Dependency");
 
1240         if (method_exists($this, "validate{$type}Dependency")) {
 
1241             return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no',
 
1247      * Convert a 1.0 dep into a 2.0 dep
 
1249     function normalizeDep($dep)
 
1253             'ext' => 'Extension',
 
1258         if (!isset($types[$dep['type']])) {
 
1259             return array(false, false);
 
1262         $type = $types[$dep['type']];
 
1267                 $newdep['channel'] = 'pear.php.net';
 
1270                 $newdep['name'] = $dep['name'];
 
1274         $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']);
 
1275         switch ($dep['rel']) {
 
1277                 return array($newdep, $type);
 
1280                 $newdep['conflicts'] = true;
 
1284                 $newdep['min'] = $dep['version'];
 
1285                 if ($dep['rel'] == '>') {
 
1286                     $newdep['exclude'] = $dep['version'];
 
1291                 $newdep['max'] = $dep['version'];
 
1292                 if ($dep['rel'] == '<') {
 
1293                     $newdep['exclude'] = $dep['version'];
 
1298                 $newdep['min'] = '0';
 
1299                 $newdep['max'] = '100000';
 
1300                 $newdep['exclude'] = $dep['version'];
 
1303                 $newdep['min'] = $dep['version'];
 
1304                 $newdep['max'] = $dep['version'];
 
1307         if ($type == 'Php') {
 
1308             if (!isset($newdep['min'])) {
 
1309                 $newdep['min'] = '4.4.0';
 
1312             if (!isset($newdep['max'])) {
 
1313                 $newdep['max'] = '6.0.0';
 
1316         return array($newdep, $type);
 
1320      * Converts text comparing operators to them sign equivalents
 
1322      * Example: 'ge' to '>='
 
1325      * @param  string Operator
 
1326      * @return string Sign equivalent
 
1328     function signOperator($operator)
 
1331             case 'lt': return '<';
 
1332             case 'le': return '<=';
 
1333             case 'gt': return '>';
 
1334             case 'ge': return '>=';
 
1335             case 'eq': return '==';
 
1336             case 'ne': return '!=';
 
1342     function raiseError($msg)
 
1344         if (isset($this->_options['ignore-errors'])) {
 
1345             return $this->warning($msg);
 
1348         return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString(
 
1349             $this->_currentPackage, true)));
 
1352     function warning($msg)
 
1354         return array(sprintf($msg, $this->_registry->parsedPackageNameToString(
 
1355             $this->_currentPackage, true)));