3 * PEAR_Downloader_Package
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: Package.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 * Error code when parameter initialization fails because no releases
19 * exist within preferred_state, but releases do exist
21 define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
23 * Error code when parameter initialization fails because no releases
24 * exist that will work with the existing PHP version
26 define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004);
29 * Coordinates download parameters and manages their dependencies
30 * prior to downloading them.
32 * Input can come from three sources:
34 * - local files (archives or package.xml)
35 * - remote files (downloadable urls)
36 * - abstract package names
38 * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
39 * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
40 * format returned of dependencies is slightly different from that used in package.xml.
42 * This class hides the differences between these elements, and makes automatic
43 * dependency resolution a piece of cake. It also manages conflicts when
44 * two classes depend on incompatible dependencies, or differing versions of the same
45 * package dependency. In addition, download will not be attempted if the php version is
46 * not supported, PEAR installer version is not supported, or non-PECL extensions are not
50 * @author Greg Beaver <cellog@php.net>
51 * @copyright 1997-2009 The Authors
52 * @license http://opensource.org/licenses/bsd-license.php New BSD License
53 * @version Release: 1.9.4
54 * @link http://pear.php.net/package/PEAR
55 * @since Class available since Release 1.4.0a1
57 class PEAR_Downloader_Package
60 * @var PEAR_Downloader
72 * Used to implement packagingroot properly
75 var $_installRegistry;
77 * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
91 var $_downloadDeps = array();
99 var $_analyzed = false;
101 * if this or a parent package was invoked with Package-state, this is set to the
104 * This allows temporary reassignment of preferred_state for a parent package and all of
108 var $_explicitState = false;
110 * If this package is invoked with Package#group, this variable will be true
112 var $_explicitGroup = false;
114 * Package type local|url
119 * Contents of package.xml, if downloaded from a remote channel
123 var $_rawpackagefile;
128 var $_validated = false;
131 * @param PEAR_Downloader
133 function PEAR_Downloader_Package(&$downloader)
135 $this->_downloader = &$downloader;
136 $this->_config = &$this->_downloader->config;
137 $this->_registry = &$this->_config->getRegistry();
138 $options = $downloader->getOptions();
139 if (isset($options['packagingroot'])) {
140 $this->_config->setInstallRoot($options['packagingroot']);
141 $this->_installRegistry = &$this->_config->getRegistry();
142 $this->_config->setInstallRoot(false);
144 $this->_installRegistry = &$this->_registry;
146 $this->_valid = $this->_analyzed = false;
150 * Parse the input and determine whether this is a local file, a remote uri, or an
151 * abstract package name.
153 * This is the heart of the PEAR_Downloader_Package(), and is used in
154 * {@link PEAR_Downloader::download()}
156 * @return bool|PEAR_Error
158 function initialize($param)
160 $origErr = $this->_fromFile($param);
165 $options = $this->_downloader->getOptions();
166 if (isset($options['offline'])) {
167 if (PEAR::isError($origErr) && !isset($options['soft'])) {
168 foreach ($origErr->getUserInfo() as $userInfo) {
169 if (isset($userInfo['message'])) {
170 $this->_downloader->log(0, $userInfo['message']);
174 $this->_downloader->log(0, $origErr->getMessage());
177 return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
180 $err = $this->_fromUrl($param);
181 if (PEAR::isError($err) || !$this->_valid) {
182 if ($this->_type == 'url') {
183 if (PEAR::isError($err) && !isset($options['soft'])) {
184 $this->_downloader->log(0, $err->getMessage());
187 return PEAR::raiseError("Invalid or missing remote package file");
190 $err = $this->_fromString($param);
191 if (PEAR::isError($err) || !$this->_valid) {
192 if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
193 return false; // instruct the downloader to silently skip
196 if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) {
197 if (is_array($origErr->getUserInfo())) {
198 foreach ($origErr->getUserInfo() as $err) {
199 if (is_array($err)) {
200 $err = $err['message'];
203 if (!isset($options['soft'])) {
204 $this->_downloader->log(0, $err);
209 if (!isset($options['soft'])) {
210 $this->_downloader->log(0, $origErr->getMessage());
213 if (is_array($param)) {
214 $param = $this->_registry->parsedPackageNameToString($param, true);
217 if (!isset($options['soft'])) {
218 $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
221 // Passing no message back - already logged above
222 return PEAR::raiseError();
225 if (PEAR::isError($err) && !isset($options['soft'])) {
226 $this->_downloader->log(0, $err->getMessage());
229 if (is_array($param)) {
230 $param = $this->_registry->parsedPackageNameToString($param, true);
233 if (!isset($options['soft'])) {
234 $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
237 // Passing no message back - already logged above
238 return PEAR::raiseError();
246 * Retrieve any non-local packages
247 * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
251 if (isset($this->_packagefile)) {
252 return $this->_packagefile;
255 if (isset($this->_downloadURL['url'])) {
256 $this->_isvalid = false;
257 $info = $this->getParsedPackage();
258 foreach ($info as $i => $p) {
259 $info[$i] = strtolower($p);
262 $err = $this->_fromUrl($this->_downloadURL['url'],
263 $this->_registry->parsedPackageNameToString($this->_parsedname, true));
264 $newinfo = $this->getParsedPackage();
265 foreach ($newinfo as $i => $p) {
266 $newinfo[$i] = strtolower($p);
269 if ($info != $newinfo) {
271 if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') {
272 $info['channel'] = 'pear.php.net';
273 if ($info == $newinfo) {
274 // skip the channel check if a pecl package says it's a PEAR package
278 if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') {
279 $info['channel'] = 'pecl.php.net';
280 if ($info == $newinfo) {
281 // skip the channel check if a pecl package says it's a PEAR package
286 return PEAR::raiseError('CRITICAL ERROR: We are ' .
287 $this->_registry->parsedPackageNameToString($info) . ', but the file ' .
288 'downloaded claims to be ' .
289 $this->_registry->parsedPackageNameToString($this->getParsedPackage()));
293 if (PEAR::isError($err) || !$this->_valid) {
298 $this->_type = 'local';
299 return $this->_packagefile;
302 function &getPackageFile()
304 return $this->_packagefile;
307 function &getDownloader()
309 return $this->_downloader;
318 * Like {@link initialize()}, but operates on a dependency
320 function fromDepURL($dep)
322 $this->_downloadURL = $dep;
323 if (isset($dep['uri'])) {
324 $options = $this->_downloader->getOptions();
325 if (!extension_loaded("zlib") || isset($options['nocompress'])) {
331 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
332 $err = $this->_fromUrl($dep['uri'] . $ext);
333 PEAR::popErrorHandling();
334 if (PEAR::isError($err)) {
335 if (!isset($options['soft'])) {
336 $this->_downloader->log(0, $err->getMessage());
339 return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
345 'package' => $dep['info']->getPackage(),
346 'channel' => $dep['info']->getChannel(),
347 'version' => $dep['version']
349 if (!isset($dep['nodefault'])) {
350 $this->_parsedname['group'] = 'default'; // download the default dependency group
351 $this->_explicitGroup = false;
354 $this->_rawpackagefile = $dep['raw'];
358 function detectDependencies($params)
360 $options = $this->_downloader->getOptions();
361 if (isset($options['downloadonly'])) {
365 if (isset($options['offline'])) {
366 $this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
370 $pname = $this->getParsedPackage();
375 $deps = $this->getDeps();
380 if (isset($deps['required'])) { // package.xml 2.0
381 return $this->_detect2($deps, $pname, $options, $params);
384 return $this->_detect1($deps, $pname, $options, $params);
387 function setValidated()
389 $this->_validated = true;
392 function alreadyValidated()
394 return $this->_validated;
398 * Remove packages to be downloaded that are already installed
399 * @param array of PEAR_Downloader_Package objects
402 function removeInstalled(&$params)
404 if (!isset($params[0])) {
408 $options = $params[0]->_downloader->getOptions();
409 if (!isset($options['downloadonly'])) {
410 foreach ($params as $i => $param) {
411 $package = $param->getPackage();
412 $channel = $param->getChannel();
413 // remove self if already installed with this version
414 // this does not need any pecl magic - we only remove exact matches
415 if ($param->_installRegistry->packageExists($package, $channel)) {
416 $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel);
417 if (version_compare($packageVersion, $param->getVersion(), '==')) {
418 if (!isset($options['force'])) {
419 $info = $param->getParsedPackage();
420 unset($info['version']);
421 unset($info['state']);
422 if (!isset($options['soft'])) {
423 $param->_downloader->log(1, 'Skipping package "' .
424 $param->getShortName() .
425 '", already installed as version ' . $packageVersion);
429 } elseif (!isset($options['force']) && !isset($options['upgrade']) &&
430 !isset($options['soft'])) {
431 $info = $param->getParsedPackage();
432 $param->_downloader->log(1, 'Skipping package "' .
433 $param->getShortName() .
434 '", already installed as version ' . $packageVersion);
441 PEAR_Downloader_Package::removeDuplicates($params);
444 function _detect2($deps, $pname, $options, $params)
446 $this->_downloadDeps = array();
447 $groupnotfound = false;
448 foreach (array('package', 'subpackage') as $packagetype) {
449 // get required dependency group
450 if (isset($deps['required'][$packagetype])) {
451 if (isset($deps['required'][$packagetype][0])) {
452 foreach ($deps['required'][$packagetype] as $dep) {
453 if (isset($dep['conflicts'])) {
454 // skip any package that this package conflicts with
457 $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
458 if (is_array($ret)) {
459 $this->_downloadDeps[] = $ret;
460 } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
461 $this->_downloader->log(0, $ret->getMessage());
465 $dep = $deps['required'][$packagetype];
466 if (!isset($dep['conflicts'])) {
467 // skip any package that this package conflicts with
468 $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
469 if (is_array($ret)) {
470 $this->_downloadDeps[] = $ret;
471 } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
472 $this->_downloader->log(0, $ret->getMessage());
478 // get optional dependency group, if any
479 if (isset($deps['optional'][$packagetype])) {
480 $skipnames = array();
481 if (!isset($deps['optional'][$packagetype][0])) {
482 $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
485 foreach ($deps['optional'][$packagetype] as $dep) {
487 if (!isset($options['alldeps'])) {
488 $dep['package'] = $dep['name'];
489 if (!isset($options['soft'])) {
490 $this->_downloader->log(3, 'Notice: package "' .
491 $this->_registry->parsedPackageNameToString($this->getParsedPackage(),
492 true) . '" optional dependency "' .
493 $this->_registry->parsedPackageNameToString(array('package' =>
494 $dep['name'], 'channel' => 'pear.php.net'), true) .
495 '" will not be automatically downloaded');
497 $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
499 unset($dep['package']);
502 $ret = $this->_detect2Dep($dep, $pname, 'optional', $params);
503 if (PEAR::isError($ret) && !isset($options['soft'])) {
504 $this->_downloader->log(0, $ret->getMessage());
508 $dep['package'] = $dep['name'];
509 $skip = count($skipnames) ?
510 $skipnames[count($skipnames) - 1] : '';
512 $this->_registry->parsedPackageNameToString($dep, true)) {
513 array_pop($skipnames);
517 if (!$skip && is_array($ret)) {
518 $this->_downloadDeps[] = $ret;
522 if (count($skipnames)) {
523 if (!isset($options['soft'])) {
524 $this->_downloader->log(1, 'Did not download optional dependencies: ' .
525 implode(', ', $skipnames) .
526 ', use --alldeps to download automatically');
531 // get requested dependency group, if any
532 $groupname = $this->getGroup();
533 $explicit = $this->_explicitGroup;
535 if (!$this->canDefault()) {
539 $groupname = 'default'; // try the default dependency group
542 if ($groupnotfound) {
546 if (isset($deps['group'])) {
547 if (isset($deps['group']['attribs'])) {
548 if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
549 $group = $deps['group'];
550 } elseif ($explicit) {
551 if (!isset($options['soft'])) {
552 $this->_downloader->log(0, 'Warning: package "' .
553 $this->_registry->parsedPackageNameToString($pname, true) .
554 '" has no dependency ' . 'group named "' . $groupname . '"');
557 $groupnotfound = true;
562 foreach ($deps['group'] as $group) {
563 if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
571 if (!isset($options['soft'])) {
572 $this->_downloader->log(0, 'Warning: package "' .
573 $this->_registry->parsedPackageNameToString($pname, true) .
574 '" has no dependency ' . 'group named "' . $groupname . '"');
578 $groupnotfound = true;
584 if (isset($group) && isset($group[$packagetype])) {
585 if (isset($group[$packagetype][0])) {
586 foreach ($group[$packagetype] as $dep) {
587 $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
588 $group['attribs']['name'] . '"', $params);
589 if (is_array($ret)) {
590 $this->_downloadDeps[] = $ret;
591 } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
592 $this->_downloader->log(0, $ret->getMessage());
596 $ret = $this->_detect2Dep($group[$packagetype], $pname,
597 'dependency group "' .
598 $group['attribs']['name'] . '"', $params);
599 if (is_array($ret)) {
600 $this->_downloadDeps[] = $ret;
601 } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
602 $this->_downloader->log(0, $ret->getMessage());
609 function _detect2Dep($dep, $pname, $group, $params)
611 if (isset($dep['conflicts'])) {
615 $options = $this->_downloader->getOptions();
616 if (isset($dep['uri'])) {
617 return array('uri' => $dep['uri'], 'dep' => $dep);;
621 $testdep['package'] = $dep['name'];
622 if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
623 $dep['package'] = $dep['name'];
624 if (!isset($options['soft'])) {
625 $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
627 $this->_registry->parsedPackageNameToString($dep, true) .
628 '", will be installed');
633 $options = $this->_downloader->getOptions();
634 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
635 if ($this->_explicitState) {
636 $pname['state'] = $this->_explicitState;
639 $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
640 if (PEAR::isError($url)) {
641 PEAR::popErrorHandling();
645 $dep['package'] = $dep['name'];
646 $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
647 !isset($options['alldeps']), true);
648 PEAR::popErrorHandling();
649 if (PEAR::isError($ret)) {
650 if (!isset($options['soft'])) {
651 $this->_downloader->log(0, $ret->getMessage());
657 // check to see if a dep is already installed and is the same or newer
658 if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
664 // do not try to move this before getDepPackageDownloadURL
665 // we can't determine whether upgrade is necessary until we know what
666 // version would be downloaded
667 if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
668 $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']);
669 $dep['package'] = $dep['name'];
670 if (!isset($options['soft'])) {
671 $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
673 $this->_registry->parsedPackageNameToString($dep, true) .
674 '" version ' . $url['version'] . ', already installed as version ' .
681 if (isset($dep['nodefault'])) {
682 $ret['nodefault'] = true;
688 function _detect1($deps, $pname, $options, $params)
690 $this->_downloadDeps = array();
691 $skipnames = array();
692 foreach ($deps as $dep) {
694 if (isset ($dep['type']) && $dep['type'] === 'pkg') {
695 $dep['channel'] = 'pear.php.net';
696 $dep['package'] = $dep['name'];
697 switch ($dep['rel']) {
704 $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
707 if (PEAR_Downloader_Package::willDownload($dep, $params)) {
708 $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
710 $this->_registry->parsedPackageNameToString($dep, true) .
711 '", will be installed');
714 $fakedp = new PEAR_PackageFile_v1;
715 $fakedp->setPackage($dep['name']);
716 // skip internet check if we are not upgrading (bug #5810)
717 if (!isset($options['upgrade']) && $this->isInstalled(
718 $fakedp, $dep['rel'])) {
719 $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
721 $this->_registry->parsedPackageNameToString($dep, true) .
722 '", is already installed');
727 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
728 if ($this->_explicitState) {
729 $pname['state'] = $this->_explicitState;
732 $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
733 $chan = 'pear.php.net';
734 if (PEAR::isError($url)) {
735 // check to see if this is a pecl package that has jumped
736 // from pear.php.net to pecl.php.net channel
737 if (!class_exists('PEAR_Dependency2')) {
738 require_once 'PEAR/Dependency2.php';
741 $newdep = PEAR_Dependency2::normalizeDep($dep);
742 $newdep = $newdep[0];
743 $newdep['channel'] = 'pecl.php.net';
744 $chan = 'pecl.php.net';
745 $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
746 $obj = &$this->_installRegistry->getPackage($dep['name']);
747 if (PEAR::isError($url)) {
748 PEAR::popErrorHandling();
749 if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
750 $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
753 $dep['package'] = $dep['name'];
754 if (!isset($options['soft'])) {
755 $this->_downloader->log(3, $this->getShortName() .
756 ': Skipping ' . $group . ' dependency "' .
757 $this->_registry->parsedPackageNameToString($dep, true) .
758 '", already installed as version ' . $obj->getVersion());
760 $skip = count($skipnames) ?
761 $skipnames[count($skipnames) - 1] : '';
763 $this->_registry->parsedPackageNameToString($dep, true)) {
764 array_pop($skipnames);
768 if (isset($dep['optional']) && $dep['optional'] == 'yes') {
769 $this->_downloader->log(2, $this->getShortName() .
770 ': Skipping optional dependency "' .
771 $this->_registry->parsedPackageNameToString($dep, true) .
772 '", no releases exist');
781 PEAR::popErrorHandling();
782 if (!isset($options['alldeps'])) {
783 if (isset($dep['optional']) && $dep['optional'] == 'yes') {
784 if (!isset($options['soft'])) {
785 $this->_downloader->log(3, 'Notice: package "' .
786 $this->getShortName() .
787 '" optional dependency "' .
788 $this->_registry->parsedPackageNameToString(
789 array('channel' => $chan, 'package' =>
790 $dep['name']), true) .
791 '" will not be automatically downloaded');
793 $skipnames[] = $this->_registry->parsedPackageNameToString(
794 array('channel' => $chan, 'package' =>
795 $dep['name']), true);
800 if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
801 if (!isset($dep['optional']) || $dep['optional'] == 'no') {
802 if (!isset($options['soft'])) {
803 $this->_downloader->log(3, 'Notice: package "' .
804 $this->getShortName() .
805 '" required dependency "' .
806 $this->_registry->parsedPackageNameToString(
807 array('channel' => $chan, 'package' =>
808 $dep['name']), true) .
809 '" will not be automatically downloaded');
811 $skipnames[] = $this->_registry->parsedPackageNameToString(
812 array('channel' => $chan, 'package' =>
813 $dep['name']), true);
818 // check to see if a dep is already installed
819 // do not try to move this before getDepPackageDownloadURL
820 // we can't determine whether upgrade is necessary until we know what
821 // version would be downloaded
822 if (!isset($options['force']) && $this->isInstalled(
823 $url, $dep['rel'])) {
824 $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
827 $dep['package'] = $dep['name'];
828 if (isset($newdep)) {
829 $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']);
831 $version = $this->_installRegistry->packageInfo($dep['name'], 'version');
834 $dep['version'] = $url['version'];
835 if (!isset($options['soft'])) {
836 $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
838 $this->_registry->parsedPackageNameToString($dep, true) .
839 '", already installed as version ' . $version);
842 $skip = count($skipnames) ?
843 $skipnames[count($skipnames) - 1] : '';
845 $this->_registry->parsedPackageNameToString($dep, true)) {
846 array_pop($skipnames);
856 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
857 if (isset($newdep)) {
861 $dep['package'] = $dep['name'];
862 $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
863 isset($dep['optional']) && $dep['optional'] == 'yes' &&
864 !isset($options['alldeps']), true);
865 PEAR::popErrorHandling();
866 if (PEAR::isError($ret)) {
867 if (!isset($options['soft'])) {
868 $this->_downloader->log(0, $ret->getMessage());
873 $this->_downloadDeps[] = $ret;
877 if (count($skipnames)) {
878 if (!isset($options['soft'])) {
879 $this->_downloader->log(1, 'Did not download dependencies: ' .
880 implode(', ', $skipnames) .
881 ', use --alldeps or --onlyreqdeps to download automatically');
886 function setDownloadURL($pkg)
888 $this->_downloadURL = $pkg;
892 * Set the package.xml object for this downloaded package
894 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
896 function setPackageFile(&$pkg)
898 $this->_packagefile = &$pkg;
901 function getShortName()
903 return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
904 'package' => $this->getPackage()), true);
907 function getParsedPackage()
909 if (isset($this->_packagefile) || isset($this->_parsedname)) {
910 return array('channel' => $this->getChannel(),
911 'package' => $this->getPackage(),
912 'version' => $this->getVersion());
918 function getDownloadURL()
920 return $this->_downloadURL;
923 function canDefault()
925 if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) {
932 function getPackage()
934 if (isset($this->_packagefile)) {
935 return $this->_packagefile->getPackage();
936 } elseif (isset($this->_downloadURL['info'])) {
937 return $this->_downloadURL['info']->getPackage();
944 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
946 function isSubpackage(&$pf)
948 if (isset($this->_packagefile)) {
949 return $this->_packagefile->isSubpackage($pf);
950 } elseif (isset($this->_downloadURL['info'])) {
951 return $this->_downloadURL['info']->isSubpackage($pf);
957 function getPackageType()
959 if (isset($this->_packagefile)) {
960 return $this->_packagefile->getPackageType();
961 } elseif (isset($this->_downloadURL['info'])) {
962 return $this->_downloadURL['info']->getPackageType();
970 if (isset($this->_packagefile)) {
971 return $this->_packagefile->getPackageType() == 'bundle';
977 function getPackageXmlVersion()
979 if (isset($this->_packagefile)) {
980 return $this->_packagefile->getPackagexmlVersion();
981 } elseif (isset($this->_downloadURL['info'])) {
982 return $this->_downloadURL['info']->getPackagexmlVersion();
988 function getChannel()
990 if (isset($this->_packagefile)) {
991 return $this->_packagefile->getChannel();
992 } elseif (isset($this->_downloadURL['info'])) {
993 return $this->_downloadURL['info']->getChannel();
1001 if (isset($this->_packagefile)) {
1002 return $this->_packagefile->getURI();
1003 } elseif (isset($this->_downloadURL['info'])) {
1004 return $this->_downloadURL['info']->getURI();
1010 function getVersion()
1012 if (isset($this->_packagefile)) {
1013 return $this->_packagefile->getVersion();
1014 } elseif (isset($this->_downloadURL['version'])) {
1015 return $this->_downloadURL['version'];
1021 function isCompatible($pf)
1023 if (isset($this->_packagefile)) {
1024 return $this->_packagefile->isCompatible($pf);
1025 } elseif (isset($this->_downloadURL['info'])) {
1026 return $this->_downloadURL['info']->isCompatible($pf);
1032 function setGroup($group)
1034 $this->_parsedname['group'] = $group;
1039 if (isset($this->_parsedname['group'])) {
1040 return $this->_parsedname['group'];
1046 function isExtension($name)
1048 if (isset($this->_packagefile)) {
1049 return $this->_packagefile->isExtension($name);
1050 } elseif (isset($this->_downloadURL['info'])) {
1051 if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
1052 return $this->_downloadURL['info']->getProvidesExtension() == $name;
1063 if (isset($this->_packagefile)) {
1064 $ver = $this->_packagefile->getPackagexmlVersion();
1065 if (version_compare($ver, '2.0', '>=')) {
1066 return $this->_packagefile->getDeps(true);
1069 return $this->_packagefile->getDeps();
1070 } elseif (isset($this->_downloadURL['info'])) {
1071 $ver = $this->_downloadURL['info']->getPackagexmlVersion();
1072 if (version_compare($ver, '2.0', '>=')) {
1073 return $this->_downloadURL['info']->getDeps(true);
1076 return $this->_downloadURL['info']->getDeps();
1083 * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
1084 * returned from getDepDownloadURL()
1086 function isEqual($param)
1088 if (is_object($param)) {
1089 $channel = $param->getChannel();
1090 $package = $param->getPackage();
1091 if ($param->getURI()) {
1093 'channel' => $param->getChannel(),
1094 'package' => $param->getPackage(),
1095 'version' => $param->getVersion(),
1096 'uri' => $param->getURI(),
1100 'channel' => $param->getChannel(),
1101 'package' => $param->getPackage(),
1102 'version' => $param->getVersion(),
1106 if (isset($param['uri'])) {
1107 if ($this->getChannel() != '__uri') {
1110 return $param['uri'] == $this->getURI();
1113 $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage();
1114 $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel();
1115 if (isset($param['rel'])) {
1116 if (!class_exists('PEAR_Dependency2')) {
1117 require_once 'PEAR/Dependency2.php';
1120 $newdep = PEAR_Dependency2::normalizeDep($param);
1121 $newdep = $newdep[0];
1122 } elseif (isset($param['min'])) {
1127 if (isset($newdep)) {
1128 if (!isset($newdep['min'])) {
1129 $newdep['min'] = '0';
1132 if (!isset($newdep['max'])) {
1133 $newdep['max'] = '100000000000000000000';
1136 // use magic to support pecl packages suddenly jumping to the pecl channel
1137 // we need to support both dependency possibilities
1138 if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
1139 if ($package == $this->getPackage()) {
1140 $channel = 'pecl.php.net';
1143 if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
1144 if ($package == $this->getPackage()) {
1145 $channel = 'pear.php.net';
1149 return (strtolower($package) == strtolower($this->getPackage()) &&
1150 $channel == $this->getChannel() &&
1151 version_compare($newdep['min'], $this->getVersion(), '<=') &&
1152 version_compare($newdep['max'], $this->getVersion(), '>='));
1155 // use magic to support pecl packages suddenly jumping to the pecl channel
1156 if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
1157 if (strtolower($package) == strtolower($this->getPackage())) {
1158 $channel = 'pear.php.net';
1162 if (isset($param['version'])) {
1163 return (strtolower($package) == strtolower($this->getPackage()) &&
1164 $channel == $this->getChannel() &&
1165 $param['version'] == $this->getVersion());
1168 return strtolower($package) == strtolower($this->getPackage()) &&
1169 $channel == $this->getChannel();
1172 function isInstalled($dep, $oper = '==')
1178 if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
1182 if (is_object($dep)) {
1183 $package = $dep->getPackage();
1184 $channel = $dep->getChannel();
1185 if ($dep->getURI()) {
1187 'uri' => $dep->getURI(),
1188 'version' => $dep->getVersion(),
1192 'version' => $dep->getVersion(),
1196 if (isset($dep['uri'])) {
1198 $package = $dep['dep']['name'];
1200 $channel = $dep['info']->getChannel();
1201 $package = $dep['info']->getPackage();
1205 $options = $this->_downloader->getOptions();
1206 $test = $this->_installRegistry->packageExists($package, $channel);
1207 if (!$test && $channel == 'pecl.php.net') {
1208 // do magic to allow upgrading from old pecl packages to new ones
1209 $test = $this->_installRegistry->packageExists($package, 'pear.php.net');
1210 $channel = 'pear.php.net';
1214 if (isset($dep['uri'])) {
1215 if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
1220 if (isset($options['upgrade'])) {
1221 $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel);
1222 if (version_compare($packageVersion, $dep['version'], '>=')) {
1236 * Detect duplicate package names with differing versions
1238 * If a user requests to install Date 1.4.6 and Date 1.4.7,
1239 * for instance, this is a logic error. This method
1240 * detects this situation.
1242 * @param array $params array of PEAR_Downloader_Package objects
1243 * @param array $errorparams empty array
1244 * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts
1246 function detectStupidDuplicates($params, &$errorparams)
1248 $existing = array();
1249 foreach ($params as $i => $param) {
1250 $package = $param->getPackage();
1251 $channel = $param->getChannel();
1252 $group = $param->getGroup();
1253 if (!isset($existing[$channel . '/' . $package])) {
1254 $existing[$channel . '/' . $package] = array();
1257 if (!isset($existing[$channel . '/' . $package][$group])) {
1258 $existing[$channel . '/' . $package][$group] = array();
1261 $existing[$channel . '/' . $package][$group][] = $i;
1265 foreach ($existing as $package => $groups) {
1266 foreach ($groups as $group => $dupes) {
1267 if (count($dupes) > 1) {
1268 $indices = $indices + $dupes;
1273 $indices = array_unique($indices);
1274 foreach ($indices as $index) {
1275 $errorparams[] = $params[$index];
1278 return count($errorparams);
1283 * @param bool ignore install groups - for final removal of dupe packages
1286 function removeDuplicates(&$params, $ignoreGroups = false)
1289 foreach ($params as $i => $param) {
1294 if ($param->getPackage()) {
1295 $group = $ignoreGroups ? '' : $param->getGroup();
1296 $pnames[$i] = $param->getChannel() . '/' .
1297 $param->getPackage() . '-' . $param->getVersion() . '#' . $group;
1301 $pnames = array_unique($pnames);
1302 $unset = array_diff(array_keys($params), array_keys($pnames));
1303 $testp = array_flip($pnames);
1304 foreach ($params as $i => $param) {
1310 if (!is_a($param, 'PEAR_Downloader_Package')) {
1315 $group = $ignoreGroups ? '' : $param->getGroup();
1316 if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
1317 $param->getVersion() . '#' . $group])) {
1322 foreach ($unset as $i) {
1327 foreach ($params as $i => $param) {
1328 $ret[] = &$params[$i];
1332 foreach ($ret as $i => $param) {
1333 $params[] = &$ret[$i];
1337 function explicitState()
1339 return $this->_explicitState;
1342 function setExplicitState($s)
1344 $this->_explicitState = $s;
1350 function mergeDependencies(&$params)
1352 $bundles = $newparams = array();
1353 foreach ($params as $i => $param) {
1354 if (!$param->isBundle()) {
1359 $pf = &$param->getPackageFile();
1361 $contents = $pf->getBundledPackages();
1362 if (!is_array($contents)) {
1363 $contents = array($contents);
1366 foreach ($contents as $file) {
1367 $filecontents = $pf->getFileContents($file);
1368 $dl = &$param->getDownloader();
1369 $options = $dl->getOptions();
1370 if (PEAR::isError($dir = $dl->getDownloadDir())) {
1374 $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb');
1379 // FIXME do symlink check
1381 fwrite($fp, $filecontents, strlen($filecontents));
1383 if ($s = $params[$i]->explicitState()) {
1384 $obj->setExplicitState($s);
1387 $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
1388 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1389 if (PEAR::isError($dir = $dl->getDownloadDir())) {
1390 PEAR::popErrorHandling();
1394 $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file);
1395 PEAR::popErrorHandling();
1396 if (PEAR::isError($e)) {
1397 if (!isset($options['soft'])) {
1398 $dl->log(0, $e->getMessage());
1404 if (!PEAR_Downloader_Package::willDownload($j,
1405 array_merge($params, $newparams)) && !$param->isInstalled($j)) {
1411 foreach ($bundles as $i) {
1412 unset($params[$i]); // remove bundles - only their contents matter for installation
1415 PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
1416 if (count($newparams)) { // add in bundled packages for install
1417 foreach ($newparams as $i => $unused) {
1418 $params[] = &$newparams[$i];
1420 $newparams = array();
1423 foreach ($params as $i => $param) {
1425 foreach ($param->_downloadDeps as $dep) {
1426 $merge = array_merge($params, $newparams);
1427 if (!PEAR_Downloader_Package::willDownload($dep, $merge)
1428 && !$param->isInstalled($dep)
1433 // detect versioning conflicts here
1437 // convert the dependencies into PEAR_Downloader_Package objects for the next time around
1438 $params[$i]->_downloadDeps = array();
1439 foreach ($newdeps as $dep) {
1440 $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
1441 if ($s = $params[$i]->explicitState()) {
1442 $obj->setExplicitState($s);
1445 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1446 $e = $obj->fromDepURL($dep);
1447 PEAR::popErrorHandling();
1448 if (PEAR::isError($e)) {
1449 if (!isset($options['soft'])) {
1450 $obj->_downloader->log(0, $e->getMessage());
1455 $e = $obj->detectDependencies($params);
1456 if (PEAR::isError($e)) {
1457 if (!isset($options['soft'])) {
1458 $obj->_downloader->log(0, $e->getMessage());
1467 if (count($newparams)) {
1468 foreach ($newparams as $i => $unused) {
1469 $params[] = &$newparams[$i];
1481 function willDownload($param, $params)
1483 if (!is_array($params)) {
1487 foreach ($params as $obj) {
1488 if ($obj->isEqual($param)) {
1497 * For simpler unit-testing
1498 * @param PEAR_Config
1502 function &getPackagefileObject(&$c, $d)
1504 $a = &new PEAR_PackageFile($c, $d);
1509 * This will retrieve from a local file if possible, and parse out
1510 * a group name as well. The original parameter will be modified to reflect this.
1511 * @param string|array can be a parsed package name as well
1514 function _fromFile(&$param)
1516 $saveparam = $param;
1517 if (is_string($param)) {
1518 if (!@file_exists($param)) {
1519 $test = explode('#', $param);
1520 $group = array_pop($test);
1521 if (@file_exists(implode('#', $test))) {
1522 $this->setGroup($group);
1523 $param = implode('#', $test);
1524 $this->_explicitGroup = true;
1528 if (@is_file($param)) {
1529 $this->_type = 'local';
1530 $options = $this->_downloader->getOptions();
1531 $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug);
1532 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1533 $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
1534 PEAR::popErrorHandling();
1535 if (PEAR::isError($pf)) {
1536 $this->_valid = false;
1537 $param = $saveparam;
1540 $this->_packagefile = &$pf;
1541 if (!$this->getGroup()) {
1542 $this->setGroup('default'); // install the default dependency group
1544 return $this->_valid = true;
1547 $param = $saveparam;
1548 return $this->_valid = false;
1551 function _fromUrl($param, $saveparam = '')
1553 if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) {
1554 $options = $this->_downloader->getOptions();
1555 $this->_type = 'url';
1556 $callback = $this->_downloader->ui ?
1557 array(&$this->_downloader, '_downloadCallback') : null;
1558 $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
1559 if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
1560 $this->_downloader->popErrorHandling();
1564 $this->_downloader->log(3, 'Downloading "' . $param . '"');
1565 $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
1566 $dir, $callback, null, false, $this->getChannel());
1567 $this->_downloader->popErrorHandling();
1568 if (PEAR::isError($file)) {
1569 if (!empty($saveparam)) {
1570 $saveparam = ", cannot download \"$saveparam\"";
1572 $err = PEAR::raiseError('Could not download from "' . $param .
1573 '"' . $saveparam . ' (' . $file->getMessage() . ')');
1577 if ($this->_rawpackagefile) {
1578 require_once 'Archive/Tar.php';
1579 $tar = &new Archive_Tar($file);
1580 $packagexml = $tar->extractInString('package2.xml');
1582 $packagexml = $tar->extractInString('package.xml');
1585 if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
1586 str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
1587 if ($this->getChannel() != 'pear.php.net') {
1588 return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
1589 'not match value returned from xml-rpc');
1592 // be more lax for the existing PEAR packages that have not-ok
1593 // characters in their package.xml
1594 $this->_downloader->log(0, 'CRITICAL WARNING: The "' .
1595 $this->getPackage() . '" package has invalid characters in its ' .
1596 'package.xml. The next version of PEAR may not be able to install ' .
1597 'this package for security reasons. Please open a bug report at ' .
1598 'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
1602 // whew, download worked!
1603 $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
1605 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1606 $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
1607 PEAR::popErrorHandling();
1608 if (PEAR::isError($pf)) {
1609 if (is_array($pf->getUserInfo())) {
1610 foreach ($pf->getUserInfo() as $err) {
1611 if (is_array($err)) {
1612 $err = $err['message'];
1615 if (!isset($options['soft'])) {
1616 $this->_downloader->log(0, "Validation Error: $err");
1621 if (!isset($options['soft'])) {
1622 $this->_downloader->log(0, $pf->getMessage());
1625 ///FIXME need to pass back some error code that we can use to match with to cancel all further operations
1626 /// At least stop all deps of this package from being installed
1627 $out = $saveparam ? $saveparam : $param;
1628 $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive');
1629 $this->_valid = false;
1633 $this->_packagefile = &$pf;
1634 $this->setGroup('default'); // install the default dependency group
1635 return $this->_valid = true;
1638 return $this->_valid = false;
1643 * @param string|array pass in an array of format
1645 * 'package' => 'pname',
1646 * ['channel' => 'channame',]
1647 * ['version' => 'version',]
1648 * ['state' => 'state',])
1649 * or a string of format [channame/]pname[-version|-state]
1651 function _fromString($param)
1653 $options = $this->_downloader->getOptions();
1654 $channel = $this->_config->get('default_channel');
1655 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1656 $pname = $this->_registry->parsePackageName($param, $channel);
1657 PEAR::popErrorHandling();
1658 if (PEAR::isError($pname)) {
1659 if ($pname->getCode() == 'invalid') {
1660 $this->_valid = false;
1664 if ($pname->getCode() == 'channel') {
1665 $parsed = $pname->getUserInfo();
1666 if ($this->_downloader->discover($parsed['channel'])) {
1667 if ($this->_config->get('auto_discover')) {
1668 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1669 $pname = $this->_registry->parsePackageName($param, $channel);
1670 PEAR::popErrorHandling();
1672 if (!isset($options['soft'])) {
1673 $this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
1674 '" is not initialized, use ' .
1675 '"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
1676 'or pear config-set auto_discover 1');
1681 if (PEAR::isError($pname)) {
1682 if (!isset($options['soft'])) {
1683 $this->_downloader->log(0, $pname->getMessage());
1686 if (is_array($param)) {
1687 $param = $this->_registry->parsedPackageNameToString($param);
1690 $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
1691 $this->_valid = false;
1695 if (!isset($options['soft'])) {
1696 $this->_downloader->log(0, $pname->getMessage());
1699 $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
1700 $this->_valid = false;
1705 if (!isset($this->_type)) {
1706 $this->_type = 'rest';
1709 $this->_parsedname = $pname;
1710 $this->_explicitState = isset($pname['state']) ? $pname['state'] : false;
1711 $this->_explicitGroup = isset($pname['group']) ? true : false;
1713 $info = $this->_downloader->_getPackageDownloadUrl($pname);
1714 if (PEAR::isError($info)) {
1715 if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') {
1717 $pname['channel'] = 'pecl.php.net';
1718 if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
1719 if (!PEAR::isError($test)) {
1720 $info = PEAR::raiseError($info->getMessage() . ' - package ' .
1721 $this->_registry->parsedPackageNameToString($pname, true) .
1722 ' can be installed with "pecl install ' . $pname['package'] .
1725 $pname['channel'] = 'pear.php.net';
1728 $pname['channel'] = 'pear.php.net';
1735 $this->_rawpackagefile = $info['raw'];
1736 $ret = $this->_analyzeDownloadURL($info, $param, $pname);
1737 if (PEAR::isError($ret)) {
1742 $this->_downloadURL = $ret;
1743 return $this->_valid = (bool) $ret;
1748 * @param array output of package.getDownloadURL
1749 * @param string|array|object information for detecting packages to be downloaded, and
1751 * @param array name information of the package
1752 * @param array|null packages to be downloaded
1753 * @param bool is this an optional dependency?
1754 * @param bool is this any kind of dependency?
1757 function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false,
1758 $isdependency = false)
1760 if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
1764 if ($info === false) {
1765 $saveparam = !is_string($param) ? ", cannot download \"$param\"" : '';
1767 // no releases exist
1768 return PEAR::raiseError('No releases for package "' .
1769 $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
1772 if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
1774 if ($pname['channel'] == 'pecl.php.net') {
1775 if ($info['info']->getChannel() != 'pear.php.net') {
1778 } elseif ($info['info']->getChannel() == 'pecl.php.net') {
1779 if ($pname['channel'] != 'pear.php.net') {
1787 return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
1788 '" retrieved another channel\'s name for download! ("' .
1789 $info['info']->getChannel() . '")');
1793 $preferred_state = $this->_config->get('preferred_state');
1794 if (!isset($info['url'])) {
1795 $package_version = $this->_registry->packageInfo($info['info']->getPackage(),
1796 'version', $info['info']->getChannel());
1797 if ($this->isInstalled($info)) {
1798 if ($isdependency && version_compare($info['version'], $package_version, '<=')) {
1799 // ignore bogus errors of "failed to download dependency"
1800 // if it is already installed and the one that would be
1801 // downloaded is older or the same version (Bug #7219)
1806 if ($info['version'] === $package_version) {
1807 if (!isset($options['soft'])) {
1808 $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
1809 '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' .
1810 ' (' . $package_version . ') is the same as the locally installed one.');
1816 if (version_compare($info['version'], $package_version, '<=')) {
1817 if (!isset($options['soft'])) {
1818 $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
1819 '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' .
1820 ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').');
1826 $instead = ', will instead download version ' . $info['version'] .
1827 ', stability "' . $info['info']->getState() . '"';
1828 // releases exist, but we failed to get any
1829 if (isset($this->_downloader->_options['force'])) {
1830 if (isset($pname['version'])) {
1831 $vs = ', version "' . $pname['version'] . '"';
1832 } elseif (isset($pname['state'])) {
1833 $vs = ', stability "' . $pname['state'] . '"';
1834 } elseif ($param == 'dependency') {
1835 if (!class_exists('PEAR_Common')) {
1836 require_once 'PEAR/Common.php';
1839 if (!in_array($info['info']->getState(),
1840 PEAR_Common::betterStates($preferred_state, true))) {
1842 // don't spit out confusing error message
1843 return $this->_downloader->_getPackageDownloadUrl(
1844 array('package' => $pname['package'],
1845 'channel' => $pname['channel'],
1846 'version' => $info['version']));
1848 $vs = ' within preferred state "' . $preferred_state .
1851 if (!class_exists('PEAR_Dependency2')) {
1852 require_once 'PEAR/Dependency2.php';
1856 // don't spit out confusing error message
1857 return $this->_downloader->_getPackageDownloadUrl(
1858 array('package' => $pname['package'],
1859 'channel' => $pname['channel'],
1860 'version' => $info['version']));
1862 $vs = PEAR_Dependency2::_getExtraString($pname);
1866 $vs = ' within preferred state "' . $preferred_state . '"';
1869 if (!isset($options['soft'])) {
1870 $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
1871 '/' . $pname['package'] . $vs . $instead);
1874 // download the latest release
1875 return $this->_downloader->_getPackageDownloadUrl(
1876 array('package' => $pname['package'],
1877 'channel' => $pname['channel'],
1878 'version' => $info['version']));
1880 if (isset($info['php']) && $info['php']) {
1881 $err = PEAR::raiseError('Failed to download ' .
1882 $this->_registry->parsedPackageNameToString(
1883 array('channel' => $pname['channel'],
1884 'package' => $pname['package']),
1886 ', latest release is version ' . $info['php']['v'] .
1887 ', but it requires PHP version "' .
1888 $info['php']['m'] . '", use "' .
1889 $this->_registry->parsedPackageNameToString(
1890 array('channel' => $pname['channel'], 'package' => $pname['package'],
1891 'version' => $info['php']['v'])) . '" to install',
1892 PEAR_DOWNLOADER_PACKAGE_PHPVERSION);
1896 // construct helpful error message
1897 if (isset($pname['version'])) {
1898 $vs = ', version "' . $pname['version'] . '"';
1899 } elseif (isset($pname['state'])) {
1900 $vs = ', stability "' . $pname['state'] . '"';
1901 } elseif ($param == 'dependency') {
1902 if (!class_exists('PEAR_Common')) {
1903 require_once 'PEAR/Common.php';
1906 if (!in_array($info['info']->getState(),
1907 PEAR_Common::betterStates($preferred_state, true))) {
1909 // don't spit out confusing error message, and don't die on
1910 // optional dep failure!
1911 return $this->_downloader->_getPackageDownloadUrl(
1912 array('package' => $pname['package'],
1913 'channel' => $pname['channel'],
1914 'version' => $info['version']));
1916 $vs = ' within preferred state "' . $preferred_state . '"';
1918 if (!class_exists('PEAR_Dependency2')) {
1919 require_once 'PEAR/Dependency2.php';
1923 // don't spit out confusing error message, and don't die on
1924 // optional dep failure!
1925 return $this->_downloader->_getPackageDownloadUrl(
1926 array('package' => $pname['package'],
1927 'channel' => $pname['channel'],
1928 'version' => $info['version']));
1930 $vs = PEAR_Dependency2::_getExtraString($pname);
1933 $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"';
1936 $options = $this->_downloader->getOptions();
1937 // this is only set by the "download-all" command
1938 if (isset($options['ignorepreferred_state'])) {
1939 $err = PEAR::raiseError(
1940 'Failed to download ' . $this->_registry->parsedPackageNameToString(
1941 array('channel' => $pname['channel'], 'package' => $pname['package']),
1944 ', latest release is version ' . $info['version'] .
1945 ', stability "' . $info['info']->getState() . '", use "' .
1946 $this->_registry->parsedPackageNameToString(
1947 array('channel' => $pname['channel'], 'package' => $pname['package'],
1948 'version' => $info['version'])) . '" to install',
1949 PEAR_DOWNLOADER_PACKAGE_STATE);
1953 // Checks if the user has a package installed already and checks the release against
1954 // the state against the installed package, this allows upgrades for packages
1955 // with lower stability than the preferred_state
1956 $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']);
1957 if (!$this->isInstalled($info)
1958 || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true))
1960 $err = PEAR::raiseError(
1961 'Failed to download ' . $this->_registry->parsedPackageNameToString(
1962 array('channel' => $pname['channel'], 'package' => $pname['package']),
1965 ', latest release is version ' . $info['version'] .
1966 ', stability "' . $info['info']->getState() . '", use "' .
1967 $this->_registry->parsedPackageNameToString(
1968 array('channel' => $pname['channel'], 'package' => $pname['package'],
1969 'version' => $info['version'])) . '" to install');
1975 if (isset($info['deprecated']) && $info['deprecated']) {
1976 $this->_downloader->log(0,
1978 $this->_registry->parsedPackageNameToString(
1979 array('channel' => $info['info']->getChannel(),
1980 'package' => $info['info']->getPackage()), true) .
1981 '" is deprecated in favor of "' .
1982 $this->_registry->parsedPackageNameToString($info['deprecated'], true) .