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 * @version CVS: $Id: Dependency2.php 313023 2011-07-06 19:17:11Z dufuz $
13 * @link http://pear.php.net/package/PEAR
14 * @since File available since Release 1.4.0a1
18 * Required for the PEAR_VALIDATE_* constants
20 require_once 'PEAR/Validate.php';
23 * Dependency check for PEAR packages
25 * This class handles both version 1.0 and 2.0 dependencies
26 * WARNING: *any* changes to this class must be duplicated in the
27 * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc,
28 * or unit tests will not actually validate the changes
31 * @author Greg Beaver <cellog@php.net>
32 * @copyright 1997-2009 The Authors
33 * @license http://opensource.org/licenses/bsd-license.php New BSD License
34 * @version Release: 1.9.4
35 * @link http://pear.php.net/package/PEAR
36 * @since Class available since Release 1.4.0a1
38 class PEAR_Dependency2
41 * One of the PEAR_VALIDATE_* states
42 * @see PEAR_VALIDATE_NORMAL
48 * Command-line options to install/upgrade/uninstall commands
69 * @var PEAR_DependencyDB
74 * Output of PEAR_Registry::parsedPackageName()
81 * @param array installation options
82 * @param array format of PEAR_Registry::parsedPackageName()
83 * @param int installation state (one of PEAR_VALIDATE_*)
85 function PEAR_Dependency2(&$config, $installoptions, $package,
86 $state = PEAR_VALIDATE_INSTALLING)
88 $this->_config = &$config;
89 if (!class_exists('PEAR_DependencyDB')) {
90 require_once 'PEAR/DependencyDB.php';
93 if (isset($installoptions['packagingroot'])) {
94 // make sure depdb is in the right location
95 $config->setInstallRoot($installoptions['packagingroot']);
98 $this->_registry = &$config->getRegistry();
99 $this->_dependencydb = &PEAR_DependencyDB::singleton($config);
100 if (isset($installoptions['packagingroot'])) {
101 $config->setInstallRoot(false);
104 $this->_options = $installoptions;
105 $this->_state = $state;
106 if (!class_exists('OS_Guess')) {
107 require_once 'OS/Guess.php';
110 $this->_os = new OS_Guess;
111 $this->_currentPackage = $package;
114 function _getExtraString($dep)
117 if (isset($dep['uri'])) {
121 if (isset($dep['recommended'])) {
122 $extra .= 'recommended version ' . $dep['recommended'];
124 if (isset($dep['min'])) {
125 $extra .= 'version >= ' . $dep['min'];
128 if (isset($dep['max'])) {
129 if ($extra != ' (') {
132 $extra .= 'version <= ' . $dep['max'];
135 if (isset($dep['exclude'])) {
136 if (!is_array($dep['exclude'])) {
137 $dep['exclude'] = array($dep['exclude']);
140 if ($extra != ' (') {
144 $extra .= 'excluded versions: ';
145 foreach ($dep['exclude'] as $i => $exclude) {
155 if ($extra == ' ()') {
163 * This makes unit-testing a heck of a lot easier
171 * This makes unit-testing a heck of a lot easier
173 function getsysname()
175 return $this->_os->getSysname();
179 * Specify a dependency on an OS. Use arch for detailed os/processor information
181 * There are two generic OS dependencies that will be the most common, unix and windows.
182 * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix
184 function validateOsDependency($dep)
186 if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) {
190 if ($dep['name'] == '*') {
194 $not = isset($dep['conflicts']) ? true : false;
195 switch (strtolower($dep['name'])) {
198 if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') {
199 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
200 return $this->raiseError("Cannot install %s on Windows");
203 return $this->warning("warning: Cannot install %s on Windows");
206 if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') {
207 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
208 return $this->raiseError("Can only install %s on Windows");
211 return $this->warning("warning: Can only install %s on Windows");
216 $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix');
218 if (in_array($this->getSysname(), $unices)) {
219 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
220 return $this->raiseError("Cannot install %s on any Unix system");
223 return $this->warning( "warning: Cannot install %s on any Unix system");
226 if (!in_array($this->getSysname(), $unices)) {
227 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
228 return $this->raiseError("Can only install %s on a Unix system");
231 return $this->warning("warning: Can only install %s on a Unix system");
237 if (strtolower($dep['name']) == strtolower($this->getSysname())) {
238 if (!isset($this->_options['nodeps']) &&
239 !isset($this->_options['force'])) {
240 return $this->raiseError('Cannot install %s on ' . $dep['name'] .
241 ' operating system');
244 return $this->warning('warning: Cannot install %s on ' .
245 $dep['name'] . ' operating system');
248 if (strtolower($dep['name']) != strtolower($this->getSysname())) {
249 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
250 return $this->raiseError('Cannot install %s on ' .
251 $this->getSysname() .
252 ' operating system, can only install on ' . $dep['name']);
255 return $this->warning('warning: Cannot install %s on ' .
256 $this->getSysname() .
257 ' operating system, can only install on ' . $dep['name']);
265 * This makes unit-testing a heck of a lot easier
267 function matchSignature($pattern)
269 return $this->_os->matchSignature($pattern);
273 * Specify a complex dependency on an OS/processor/kernel version,
274 * Use OS for simple operating system dependency.
276 * This is the only dependency that accepts an eregable pattern. The pattern
277 * will be matched against the php_uname() output parsed by OS_Guess
279 function validateArchDependency($dep)
281 if ($this->_state != PEAR_VALIDATE_INSTALLING) {
285 $not = isset($dep['conflicts']) ? true : false;
286 if (!$this->matchSignature($dep['pattern'])) {
288 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
289 return $this->raiseError('%s Architecture dependency failed, does not ' .
290 'match "' . $dep['pattern'] . '"');
293 return $this->warning('warning: %s Architecture dependency failed, does ' .
294 'not match "' . $dep['pattern'] . '"');
301 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
302 return $this->raiseError('%s Architecture dependency failed, required "' .
303 $dep['pattern'] . '"');
306 return $this->warning('warning: %s Architecture dependency failed, ' .
307 'required "' . $dep['pattern'] . '"');
314 * This makes unit-testing a heck of a lot easier
316 function extension_loaded($name)
318 return extension_loaded($name);
322 * This makes unit-testing a heck of a lot easier
324 function phpversion($name = null)
326 if ($name !== null) {
327 return phpversion($name);
333 function validateExtensionDependency($dep, $required = true)
335 if ($this->_state != PEAR_VALIDATE_INSTALLING &&
336 $this->_state != PEAR_VALIDATE_DOWNLOADING) {
340 $loaded = $this->extension_loaded($dep['name']);
341 $extra = $this->_getExtraString($dep);
342 if (isset($dep['exclude'])) {
343 if (!is_array($dep['exclude'])) {
344 $dep['exclude'] = array($dep['exclude']);
348 if (!isset($dep['min']) && !isset($dep['max']) &&
349 !isset($dep['recommended']) && !isset($dep['exclude'])
352 if (isset($dep['conflicts'])) {
353 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
354 return $this->raiseError('%s conflicts with PHP extension "' .
355 $dep['name'] . '"' . $extra);
358 return $this->warning('warning: %s conflicts with PHP extension "' .
359 $dep['name'] . '"' . $extra);
365 if (isset($dep['conflicts'])) {
370 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
371 return $this->raiseError('%s requires PHP extension "' .
372 $dep['name'] . '"' . $extra);
375 return $this->warning('warning: %s requires PHP extension "' .
376 $dep['name'] . '"' . $extra);
379 return $this->warning('%s can optionally use PHP extension "' .
380 $dep['name'] . '"' . $extra);
384 if (isset($dep['conflicts'])) {
389 return $this->warning('%s can optionally use PHP extension "' .
390 $dep['name'] . '"' . $extra);
393 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
394 return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
398 return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
402 $version = (string) $this->phpversion($dep['name']);
403 if (empty($version)) {
408 if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) {
412 if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) {
416 if ($fail && !isset($dep['conflicts'])) {
417 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
418 return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
419 '"' . $extra . ', installed version is ' . $version);
422 return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
423 '"' . $extra . ', installed version is ' . $version);
424 } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) {
425 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
426 return $this->raiseError('%s conflicts with PHP extension "' .
427 $dep['name'] . '"' . $extra . ', installed version is ' . $version);
430 return $this->warning('warning: %s conflicts with PHP extension "' .
431 $dep['name'] . '"' . $extra . ', installed version is ' . $version);
434 if (isset($dep['exclude'])) {
435 foreach ($dep['exclude'] as $exclude) {
436 if (version_compare($version, $exclude, '==')) {
437 if (isset($dep['conflicts'])) {
441 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
442 return $this->raiseError('%s is not compatible with PHP extension "' .
443 $dep['name'] . '" version ' .
447 return $this->warning('warning: %s is not compatible with PHP extension "' .
448 $dep['name'] . '" version ' .
450 } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
451 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
452 return $this->raiseError('%s conflicts with PHP extension "' .
453 $dep['name'] . '"' . $extra . ', installed version is ' . $version);
456 return $this->warning('warning: %s conflicts with PHP extension "' .
457 $dep['name'] . '"' . $extra . ', installed version is ' . $version);
462 if (isset($dep['recommended'])) {
463 if (version_compare($version, $dep['recommended'], '==')) {
467 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
468 return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] .
469 ' version "' . $version . '"' .
470 ' is not the recommended version "' . $dep['recommended'] .
471 '", but may be compatible, use --force to install');
474 return $this->warning('warning: %s dependency: PHP extension ' .
475 $dep['name'] . ' version "' . $version . '"' .
476 ' is not the recommended version "' . $dep['recommended'].'"');
482 function validatePhpDependency($dep)
484 if ($this->_state != PEAR_VALIDATE_INSTALLING &&
485 $this->_state != PEAR_VALIDATE_DOWNLOADING) {
489 $version = $this->phpversion();
490 $extra = $this->_getExtraString($dep);
491 if (isset($dep['exclude'])) {
492 if (!is_array($dep['exclude'])) {
493 $dep['exclude'] = array($dep['exclude']);
497 if (isset($dep['min'])) {
498 if (!version_compare($version, $dep['min'], '>=')) {
499 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
500 return $this->raiseError('%s requires PHP' .
501 $extra . ', installed version is ' . $version);
504 return $this->warning('warning: %s requires PHP' .
505 $extra . ', installed version is ' . $version);
509 if (isset($dep['max'])) {
510 if (!version_compare($version, $dep['max'], '<=')) {
511 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
512 return $this->raiseError('%s requires PHP' .
513 $extra . ', installed version is ' . $version);
516 return $this->warning('warning: %s requires PHP' .
517 $extra . ', installed version is ' . $version);
521 if (isset($dep['exclude'])) {
522 foreach ($dep['exclude'] as $exclude) {
523 if (version_compare($version, $exclude, '==')) {
524 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
525 return $this->raiseError('%s is not compatible with PHP version ' .
529 return $this->warning(
530 'warning: %s is not compatible with PHP version ' .
540 * This makes unit-testing a heck of a lot easier
542 function getPEARVersion()
547 function validatePearinstallerDependency($dep)
549 $pearversion = $this->getPEARVersion();
550 $extra = $this->_getExtraString($dep);
551 if (isset($dep['exclude'])) {
552 if (!is_array($dep['exclude'])) {
553 $dep['exclude'] = array($dep['exclude']);
557 if (version_compare($pearversion, $dep['min'], '<')) {
558 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
559 return $this->raiseError('%s requires PEAR Installer' . $extra .
560 ', installed version is ' . $pearversion);
563 return $this->warning('warning: %s requires PEAR Installer' . $extra .
564 ', installed version is ' . $pearversion);
567 if (isset($dep['max'])) {
568 if (version_compare($pearversion, $dep['max'], '>')) {
569 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
570 return $this->raiseError('%s requires PEAR Installer' . $extra .
571 ', installed version is ' . $pearversion);
574 return $this->warning('warning: %s requires PEAR Installer' . $extra .
575 ', installed version is ' . $pearversion);
579 if (isset($dep['exclude'])) {
580 if (!isset($dep['exclude'][0])) {
581 $dep['exclude'] = array($dep['exclude']);
584 foreach ($dep['exclude'] as $exclude) {
585 if (version_compare($exclude, $pearversion, '==')) {
586 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
587 return $this->raiseError('%s is not compatible with PEAR Installer ' .
588 'version ' . $exclude);
591 return $this->warning('warning: %s is not compatible with PEAR ' .
592 'Installer version ' . $exclude);
600 function validateSubpackageDependency($dep, $required, $params)
602 return $this->validatePackageDependency($dep, $required, $params);
606 * @param array dependency information (2.0 format)
607 * @param boolean whether this is a required dependency
608 * @param array a list of downloaded packages to be installed, if any
609 * @param boolean if true, then deps on pear.php.net that fail will also check
610 * against pecl.php.net packages to accomodate extensions that have
611 * moved to pecl.php.net from pear.php.net
613 function validatePackageDependency($dep, $required, $params, $depv1 = false)
615 if ($this->_state != PEAR_VALIDATE_INSTALLING &&
616 $this->_state != PEAR_VALIDATE_DOWNLOADING) {
620 if (isset($dep['providesextension'])) {
621 if ($this->extension_loaded($dep['providesextension'])) {
624 $subdep['name'] = $subdep['providesextension'];
625 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
626 $ret = $this->validateExtensionDependency($subdep, $required);
627 PEAR::popErrorHandling();
628 if (!PEAR::isError($ret)) {
634 if ($this->_state == PEAR_VALIDATE_INSTALLING) {
635 return $this->_validatePackageInstall($dep, $required, $depv1);
638 if ($this->_state == PEAR_VALIDATE_DOWNLOADING) {
639 return $this->_validatePackageDownload($dep, $required, $params, $depv1);
643 function _validatePackageDownload($dep, $required, $params, $depv1 = false)
645 $dep['package'] = $dep['name'];
646 if (isset($dep['uri'])) {
647 $dep['channel'] = '__uri';
650 $depname = $this->_registry->parsedPackageNameToString($dep, true);
652 foreach ($params as $param) {
654 array('package' => $dep['name'],
655 'channel' => $dep['channel']))) {
660 if ($depv1 && $dep['channel'] == 'pear.php.net') {
662 array('package' => $dep['name'],
663 'channel' => 'pecl.php.net'))) {
670 if (!$found && isset($dep['providesextension'])) {
671 foreach ($params as $param) {
672 if ($param->isExtension($dep['providesextension'])) {
680 $version = $param->getVersion();
684 if ($this->_registry->packageExists($dep['name'], $dep['channel'])) {
687 $version = $this->_registry->packageinfo($dep['name'], 'version',
690 if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'],
694 $version = $this->_registry->packageinfo($dep['name'], 'version',
697 $version = 'not installed or downloaded';
704 $extra = $this->_getExtraString($dep);
705 if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
706 $dep['exclude'] = array($dep['exclude']);
709 if (!isset($dep['min']) && !isset($dep['max']) &&
710 !isset($dep['recommended']) && !isset($dep['exclude'])
712 if ($installed || $downloaded) {
713 $installed = $installed ? 'installed' : 'downloaded';
714 if (isset($dep['conflicts'])) {
717 $rest = ", $installed version is " . $version;
720 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
721 return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest);
724 return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest);
730 if (isset($dep['conflicts'])) {
735 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
736 return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
739 return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
742 return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
745 if (!$installed && !$downloaded) {
746 if (isset($dep['conflicts'])) {
751 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
752 return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
755 return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
758 return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
762 if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) {
766 if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) {
770 if ($fail && !isset($dep['conflicts'])) {
771 $installed = $installed ? 'installed' : 'downloaded';
772 $dep['package'] = $dep['name'];
773 $dep = $this->_registry->parsedPackageNameToString($dep, true);
774 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
775 return $this->raiseError('%s requires package "' . $depname . '"' .
776 $extra . ", $installed version is " . $version);
779 return $this->warning('warning: %s requires package "' . $depname . '"' .
780 $extra . ", $installed version is " . $version);
781 } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail &&
782 isset($dep['conflicts']) && !isset($dep['exclude'])) {
783 $installed = $installed ? 'installed' : 'downloaded';
784 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
785 return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra .
786 ", $installed version is " . $version);
789 return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
790 $extra . ", $installed version is " . $version);
793 if (isset($dep['exclude'])) {
794 $installed = $installed ? 'installed' : 'downloaded';
795 foreach ($dep['exclude'] as $exclude) {
796 if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) {
797 if (!isset($this->_options['nodeps']) &&
798 !isset($this->_options['force'])
800 return $this->raiseError('%s is not compatible with ' .
801 $installed . ' package "' .
802 $depname . '" version ' .
806 return $this->warning('warning: %s is not compatible with ' .
807 $installed . ' package "' .
808 $depname . '" version ' .
810 } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
811 $installed = $installed ? 'installed' : 'downloaded';
812 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
813 return $this->raiseError('%s conflicts with package "' . $depname . '"' .
814 $extra . ", $installed version is " . $version);
817 return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
818 $extra . ", $installed version is " . $version);
823 if (isset($dep['recommended'])) {
824 $installed = $installed ? 'installed' : 'downloaded';
825 if (version_compare($version, $dep['recommended'], '==')) {
829 if (!$found && $installed) {
830 $param = $this->_registry->getPackage($dep['name'], $dep['channel']);
835 foreach ($params as $parent) {
836 if ($parent->isEqual($this->_currentPackage)) {
843 if ($param->isCompatible($parent)) {
846 } else { // this is for validPackage() calls
847 $parent = $this->_registry->getPackage($this->_currentPackage['package'],
848 $this->_currentPackage['channel']);
849 if ($parent !== null && $param->isCompatible($parent)) {
855 if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) &&
856 !isset($this->_options['loose'])
858 return $this->raiseError('%s dependency package "' . $depname .
859 '" ' . $installed . ' version ' . $version .
860 ' is not the recommended version ' . $dep['recommended'] .
861 ', but may be compatible, use --force to install');
864 return $this->warning('warning: %s dependency package "' . $depname .
865 '" ' . $installed . ' version ' . $version .
866 ' is not the recommended version ' . $dep['recommended']);
872 function _validatePackageInstall($dep, $required, $depv1 = false)
874 return $this->_validatePackageDownload($dep, $required, array(), $depv1);
878 * Verify that uninstalling packages passed in to command line is OK.
880 * @param PEAR_Installer $dl
881 * @return PEAR_Error|true
883 function validatePackageUninstall(&$dl)
885 if (PEAR::isError($this->_dependencydb)) {
886 return $this->_dependencydb;
890 // construct an array of "downloaded" packages to fool the package dependency checker
891 // into using these to validate uninstalls of circular dependencies
892 $downloaded = &$dl->getUninstallPackages();
893 foreach ($downloaded as $i => $pf) {
894 if (!class_exists('PEAR_Downloader_Package')) {
895 require_once 'PEAR/Downloader/Package.php';
897 $dp = &new PEAR_Downloader_Package($dl);
898 $dp->setPackageFile($downloaded[$i]);
903 $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' .
904 strtolower($this->_currentPackage['package']);
905 if (isset($dl->___uninstall_package_cache)) {
906 $badpackages = $dl->___uninstall_package_cache;
907 if (isset($badpackages[$memyselfandI]['warnings'])) {
908 foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
909 $dl->log(0, $warning[0]);
913 if (isset($badpackages[$memyselfandI]['errors'])) {
914 foreach ($badpackages[$memyselfandI]['errors'] as $error) {
915 if (is_array($error)) {
916 $dl->log(0, $error[0]);
918 $dl->log(0, $error->getMessage());
922 if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
923 return $this->warning(
924 'warning: %s should not be uninstalled, other installed packages depend ' .
928 return $this->raiseError(
929 '%s cannot be uninstalled, other installed packages depend on this package');
935 // first, list the immediate parents of each package to be uninstalled
936 $perpackagelist = array();
937 $allparents = array();
938 foreach ($params as $i => $param) {
940 'channel' => strtolower($param->getChannel()),
941 'package' => strtolower($param->getPackage())
944 $deps = $this->_dependencydb->getDependentPackages($a);
946 foreach ($deps as $d) {
947 $pardeps = $this->_dependencydb->getDependencies($d);
948 foreach ($pardeps as $dep) {
949 if (strtolower($dep['dep']['channel']) == $a['channel'] &&
950 strtolower($dep['dep']['name']) == $a['package']) {
951 if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) {
952 $perpackagelist[$a['channel'] . '/' . $a['package']] = array();
954 $perpackagelist[$a['channel'] . '/' . $a['package']][]
955 = array($d['channel'] . '/' . $d['package'], $dep);
956 if (!isset($allparents[$d['channel'] . '/' . $d['package']])) {
957 $allparents[$d['channel'] . '/' . $d['package']] = array();
959 if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) {
960 $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array();
962 $allparents[$d['channel'] . '/' . $d['package']]
963 [$a['channel'] . '/' . $a['package']][]
971 // next, remove any packages from the parents list that are not installed
973 foreach ($allparents as $parent => $d1) {
974 foreach ($d1 as $d) {
975 if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) {
978 $remove[$parent] = true;
982 // next remove any packages from the parents list that are not passed in for
984 foreach ($allparents as $parent => $d1) {
985 foreach ($d1 as $d) {
986 foreach ($params as $param) {
987 if (strtolower($param->getChannel()) == $d[0][0]['channel'] &&
988 strtolower($param->getPackage()) == $d[0][0]['package']) {
993 $remove[$parent] = true;
997 // remove all packages whose dependencies fail
998 // save which ones failed for error reporting
999 $badchildren = array();
1002 foreach ($remove as $package => $unused) {
1003 if (!isset($allparents[$package])) {
1007 foreach ($allparents[$package] as $kid => $d1) {
1008 foreach ($d1 as $depinfo) {
1009 if ($depinfo[1]['type'] != 'optional') {
1010 if (isset($badchildren[$kid])) {
1013 $badchildren[$kid] = true;
1014 $remove[$kid] = true;
1021 // start over, we removed some children
1027 // next, construct the list of packages that can't be uninstalled
1028 $badpackages = array();
1029 $save = $this->_currentPackage;
1030 foreach ($perpackagelist as $package => $packagedeps) {
1031 foreach ($packagedeps as $parent) {
1032 if (!isset($remove[$parent[0]])) {
1036 $packagename = $this->_registry->parsePackageName($parent[0]);
1037 $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']);
1038 $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']);
1039 $packagename['package'] = $pa->getPackage();
1040 $this->_currentPackage = $packagename;
1041 // parent is not present in uninstall list, make sure we can actually
1042 // uninstall it (parent dep is optional)
1043 $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']);
1044 $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']);
1045 $parentname['package'] = $pa->getPackage();
1046 $parent[1]['dep']['package'] = $parentname['package'];
1047 $parent[1]['dep']['channel'] = $parentname['channel'];
1048 if ($parent[1]['type'] == 'optional') {
1049 $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl);
1050 if ($test !== true) {
1051 $badpackages[$package]['warnings'][] = $test;
1054 $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl);
1055 if ($test !== true) {
1056 $badpackages[$package]['errors'][] = $test;
1062 $this->_currentPackage = $save;
1063 $dl->___uninstall_package_cache = $badpackages;
1064 if (isset($badpackages[$memyselfandI])) {
1065 if (isset($badpackages[$memyselfandI]['warnings'])) {
1066 foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
1067 $dl->log(0, $warning[0]);
1071 if (isset($badpackages[$memyselfandI]['errors'])) {
1072 foreach ($badpackages[$memyselfandI]['errors'] as $error) {
1073 if (is_array($error)) {
1074 $dl->log(0, $error[0]);
1076 $dl->log(0, $error->getMessage());
1080 if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
1081 return $this->warning(
1082 'warning: %s should not be uninstalled, other installed packages depend ' .
1086 return $this->raiseError(
1087 '%s cannot be uninstalled, other installed packages depend on this package');
1094 function _validatePackageUninstall($dep, $required, $dl)
1096 $depname = $this->_registry->parsedPackageNameToString($dep, true);
1097 $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']);
1102 $extra = $this->_getExtraString($dep);
1103 if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
1104 $dep['exclude'] = array($dep['exclude']);
1107 if (isset($dep['conflicts'])) {
1108 return true; // uninstall OK - these packages conflict (probably installed with --force)
1111 if (!isset($dep['min']) && !isset($dep['max'])) {
1113 return $this->warning('"' . $depname . '" can be optionally used by ' .
1114 'installed package %s' . $extra);
1117 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
1118 return $this->raiseError('"' . $depname . '" is required by ' .
1119 'installed package %s' . $extra);
1122 return $this->warning('warning: "' . $depname . '" is required by ' .
1123 'installed package %s' . $extra);
1127 if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) {
1131 if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) {
1135 // we re-use this variable, preserve the original value
1136 $saverequired = $required;
1138 return $this->warning($depname . $extra . ' can be optionally used by installed package' .
1142 if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
1143 return $this->raiseError($depname . $extra . ' is required by installed package' .
1147 return $this->raiseError('warning: ' . $depname . $extra .
1148 ' is required by installed package "%s"');
1152 * validate a downloaded package against installed packages
1154 * As of PEAR 1.4.3, this will only validate
1156 * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2
1157 * $pkg package identifier (either
1158 * array('package' => blah, 'channel' => blah) or an array with
1159 * index 'info' referencing an object)
1160 * @param PEAR_Downloader $dl
1161 * @param array $params full list of packages to install
1162 * @return true|PEAR_Error
1164 function validatePackage($pkg, &$dl, $params = array())
1166 if (is_array($pkg) && isset($pkg['info'])) {
1167 $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']);
1169 $deps = $this->_dependencydb->getDependentPackageDependencies($pkg);
1174 if (!class_exists('PEAR_Downloader_Package')) {
1175 require_once 'PEAR/Downloader/Package.php';
1178 $dp = &new PEAR_Downloader_Package($dl);
1179 if (is_object($pkg)) {
1180 $dp->setPackageFile($pkg);
1182 $dp->setDownloadURL($pkg);
1185 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1186 foreach ($deps as $channel => $info) {
1187 foreach ($info as $package => $ds) {
1188 foreach ($params as $packd) {
1189 if (strtolower($packd->getPackage()) == strtolower($package) &&
1190 $packd->getChannel() == $channel) {
1191 $dl->log(3, 'skipping installed package check of "' .
1192 $this->_registry->parsedPackageNameToString(
1193 array('channel' => $channel, 'package' => $package),
1195 '", version "' . $packd->getVersion() . '" will be ' .
1196 'downloaded and installed');
1197 continue 2; // jump to next package
1201 foreach ($ds as $d) {
1202 $checker = &new PEAR_Dependency2($this->_config, $this->_options,
1203 array('channel' => $channel, 'package' => $package), $this->_state);
1205 $required = $d['type'] == 'required';
1206 $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp));
1207 if (is_array($ret)) {
1208 $dl->log(0, $ret[0]);
1209 } elseif (PEAR::isError($ret)) {
1210 $dl->log(0, $ret->getMessage());
1216 PEAR::popErrorHandling();
1220 return $this->raiseError(
1221 '%s cannot be installed, conflicts with installed packages');
1228 * validate a package.xml 1.0 dependency
1230 function validateDependency1($dep, $params = array())
1232 if (!isset($dep['optional'])) {
1233 $dep['optional'] = 'no';
1236 list($newdep, $type) = $this->normalizeDep($dep);
1238 return $this->raiseError("Invalid Dependency");
1241 if (method_exists($this, "validate{$type}Dependency")) {
1242 return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no',
1248 * Convert a 1.0 dep into a 2.0 dep
1250 function normalizeDep($dep)
1254 'ext' => 'Extension',
1259 if (!isset($types[$dep['type']])) {
1260 return array(false, false);
1263 $type = $types[$dep['type']];
1268 $newdep['channel'] = 'pear.php.net';
1271 $newdep['name'] = $dep['name'];
1275 $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']);
1276 switch ($dep['rel']) {
1278 return array($newdep, $type);
1281 $newdep['conflicts'] = true;
1285 $newdep['min'] = $dep['version'];
1286 if ($dep['rel'] == '>') {
1287 $newdep['exclude'] = $dep['version'];
1292 $newdep['max'] = $dep['version'];
1293 if ($dep['rel'] == '<') {
1294 $newdep['exclude'] = $dep['version'];
1299 $newdep['min'] = '0';
1300 $newdep['max'] = '100000';
1301 $newdep['exclude'] = $dep['version'];
1304 $newdep['min'] = $dep['version'];
1305 $newdep['max'] = $dep['version'];
1308 if ($type == 'Php') {
1309 if (!isset($newdep['min'])) {
1310 $newdep['min'] = '4.4.0';
1313 if (!isset($newdep['max'])) {
1314 $newdep['max'] = '6.0.0';
1317 return array($newdep, $type);
1321 * Converts text comparing operators to them sign equivalents
1323 * Example: 'ge' to '>='
1326 * @param string Operator
1327 * @return string Sign equivalent
1329 function signOperator($operator)
1332 case 'lt': return '<';
1333 case 'le': return '<=';
1334 case 'gt': return '>';
1335 case 'ge': return '>=';
1336 case 'eq': return '==';
1337 case 'ne': return '!=';
1343 function raiseError($msg)
1345 if (isset($this->_options['ignore-errors'])) {
1346 return $this->warning($msg);
1349 return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString(
1350 $this->_currentPackage, true)));
1353 function warning($msg)
1355 return array(sprintf($msg, $this->_registry->parsedPackageNameToString(
1356 $this->_currentPackage, true)));