1724 lines
68 KiB
PHP
1724 lines
68 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* PEAR_Downloader_Package
|
||
|
*
|
||
|
* PHP versions 4 and 5
|
||
|
*
|
||
|
* LICENSE: This source file is subject to version 3.0 of the PHP license
|
||
|
* that is available through the world-wide-web at the following URI:
|
||
|
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
|
||
|
* the PHP License and are unable to obtain it through the web, please
|
||
|
* send a note to license@php.net so we can mail you a copy immediately.
|
||
|
*
|
||
|
* @category pear
|
||
|
* @package PEAR
|
||
|
* @author Greg Beaver <cellog@php.net>
|
||
|
* @copyright 1997-2006 The PHP Group
|
||
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||
|
* @version CVS: $Id: Package.php,v 1.91.2.1 2006/05/25 22:00:05 cellog Exp $
|
||
|
* @link http://pear.php.net/package/PEAR
|
||
|
* @since File available since Release 1.4.0a1
|
||
|
*/
|
||
|
/**
|
||
|
* Error code when parameter initialization fails because no releases
|
||
|
* exist within preferred_state, but releases do exist
|
||
|
*/
|
||
|
define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
|
||
|
/**
|
||
|
* Coordinates download parameters and manages their dependencies
|
||
|
* prior to downloading them.
|
||
|
*
|
||
|
* Input can come from three sources:
|
||
|
*
|
||
|
* - local files (archives or package.xml)
|
||
|
* - remote files (downloadable urls)
|
||
|
* - abstract package names
|
||
|
*
|
||
|
* The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
|
||
|
* accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
|
||
|
* format returned of dependencies is slightly different from that used in package.xml.
|
||
|
*
|
||
|
* This class hides the differences between these elements, and makes automatic
|
||
|
* dependency resolution a piece of cake. It also manages conflicts when
|
||
|
* two classes depend on incompatible dependencies, or differing versions of the same
|
||
|
* package dependency. In addition, download will not be attempted if the php version is
|
||
|
* not supported, PEAR installer version is not supported, or non-PECL extensions are not
|
||
|
* installed.
|
||
|
* @category pear
|
||
|
* @package PEAR
|
||
|
* @author Greg Beaver <cellog@php.net>
|
||
|
* @copyright 1997-2006 The PHP Group
|
||
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||
|
* @version Release: 1.4.11
|
||
|
* @link http://pear.php.net/package/PEAR
|
||
|
* @since Class available since Release 1.4.0a1
|
||
|
*/
|
||
|
class PEAR_Downloader_Package
|
||
|
{
|
||
|
/**
|
||
|
* @var PEAR_Downloader
|
||
|
*/
|
||
|
var $_downloader;
|
||
|
/**
|
||
|
* @var PEAR_Config
|
||
|
*/
|
||
|
var $_config;
|
||
|
/**
|
||
|
* @var PEAR_Registry
|
||
|
*/
|
||
|
var $_registry;
|
||
|
/**
|
||
|
* Used to implement packagingroot properly
|
||
|
* @var PEAR_Registry
|
||
|
*/
|
||
|
var $_installRegistry;
|
||
|
/**
|
||
|
* @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
|
||
|
*/
|
||
|
var $_packagefile;
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
var $_parsedname;
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
var $_downloadURL;
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
var $_downloadDeps = array();
|
||
|
/**
|
||
|
* @var boolean
|
||
|
*/
|
||
|
var $_valid = false;
|
||
|
/**
|
||
|
* @var boolean
|
||
|
*/
|
||
|
var $_analyzed = false;
|
||
|
/**
|
||
|
* if this or a parent package was invoked with Package-state, this is set to the
|
||
|
* state variable.
|
||
|
*
|
||
|
* This allows temporary reassignment of preferred_state for a parent package and all of
|
||
|
* its dependencies.
|
||
|
* @var string|false
|
||
|
*/
|
||
|
var $_explicitState = false;
|
||
|
/**
|
||
|
* If this package is invoked with Package#group, this variable will be true
|
||
|
*/
|
||
|
var $_explicitGroup = false;
|
||
|
/**
|
||
|
* Package type local|url|xmlrpc
|
||
|
* @var string
|
||
|
*/
|
||
|
var $_type;
|
||
|
/**
|
||
|
* Contents of package.xml, if downloaded from a remote channel
|
||
|
* @var string|false
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_rawpackagefile;
|
||
|
/**
|
||
|
* @var boolean
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_validated = false;
|
||
|
|
||
|
/**
|
||
|
* @param PEAR_Downloader
|
||
|
*/
|
||
|
function PEAR_Downloader_Package(&$downloader)
|
||
|
{
|
||
|
$this->_downloader = &$downloader;
|
||
|
$this->_config = &$this->_downloader->config;
|
||
|
$this->_registry = &$this->_config->getRegistry();
|
||
|
$options = $downloader->getOptions();
|
||
|
if (isset($options['packagingroot'])) {
|
||
|
$this->_config->setInstallRoot($options['packagingroot']);
|
||
|
$this->_installRegistry = &$this->_config->getRegistry();
|
||
|
$this->_config->setInstallRoot(false);
|
||
|
} else {
|
||
|
$this->_installRegistry = &$this->_registry;
|
||
|
}
|
||
|
$this->_valid = $this->_analyzed = false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parse the input and determine whether this is a local file, a remote uri, or an
|
||
|
* abstract package name.
|
||
|
*
|
||
|
* This is the heart of the PEAR_Downloader_Package(), and is used in
|
||
|
* {@link PEAR_Downloader::download()}
|
||
|
* @param string
|
||
|
* @return bool|PEAR_Error
|
||
|
*/
|
||
|
function initialize($param)
|
||
|
{
|
||
|
$origErr = $this->_fromFile($param);
|
||
|
if (!$this->_valid) {
|
||
|
$options = $this->_downloader->getOptions();
|
||
|
if (isset($options['offline'])) {
|
||
|
if (PEAR::isError($origErr)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $origErr->getMessage());
|
||
|
}
|
||
|
}
|
||
|
return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
|
||
|
}
|
||
|
$err = $this->_fromUrl($param);
|
||
|
if (PEAR::isError($err) || !$this->_valid) {
|
||
|
if ($this->_type == 'url') {
|
||
|
if (PEAR::isError($err)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $err->getMessage());
|
||
|
}
|
||
|
}
|
||
|
return PEAR::raiseError("Invalid or missing remote package file");
|
||
|
}
|
||
|
$err = $this->_fromString($param);
|
||
|
if (PEAR::isError($err) || !$this->_valid) {
|
||
|
if (PEAR::isError($err) &&
|
||
|
$err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
|
||
|
return false; // instruct the downloader to silently skip
|
||
|
}
|
||
|
if (isset($this->_type) && $this->_type == 'local' &&
|
||
|
PEAR::isError($origErr)) {
|
||
|
if (is_array($origErr->getUserInfo())) {
|
||
|
foreach ($origErr->getUserInfo() as $err) {
|
||
|
if (is_array($err)) {
|
||
|
$err = $err['message'];
|
||
|
}
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $err);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $origErr->getMessage());
|
||
|
}
|
||
|
if (is_array($param)) {
|
||
|
$param = $this->_registry->parsedPackageNameToString($param,
|
||
|
true);
|
||
|
}
|
||
|
return PEAR::raiseError(
|
||
|
"Cannot initialize '$param', invalid or missing package file");
|
||
|
}
|
||
|
if (PEAR::isError($err)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $err->getMessage());
|
||
|
}
|
||
|
}
|
||
|
if (is_array($param)) {
|
||
|
$param = $this->_registry->parsedPackageNameToString($param, true);
|
||
|
}
|
||
|
return PEAR::raiseError(
|
||
|
"Cannot initialize '$param', invalid or missing package file");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve any non-local packages
|
||
|
* @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
|
||
|
*/
|
||
|
function &download()
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile;
|
||
|
}
|
||
|
if (isset($this->_downloadURL['url'])) {
|
||
|
$this->_isvalid = false;
|
||
|
$info = $this->getParsedPackage();
|
||
|
foreach ($info as $i => $p) {
|
||
|
$info[$i] = strtolower($p);
|
||
|
}
|
||
|
$err = $this->_fromUrl($this->_downloadURL['url'],
|
||
|
$this->_registry->parsedPackageNameToString($this->_parsedname, true));
|
||
|
$newinfo = $this->getParsedPackage();
|
||
|
foreach ($newinfo as $i => $p) {
|
||
|
$newinfo[$i] = strtolower($p);
|
||
|
}
|
||
|
if ($info != $newinfo) {
|
||
|
do {
|
||
|
if ($info['package'] == 'pecl.php.net' && $newinfo['package'] == 'pear.php.net') {
|
||
|
$info['package'] = 'pear.php.net';
|
||
|
if ($info == $newinfo) {
|
||
|
// skip the channel check if a pecl package says it's a PEAR package
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return PEAR::raiseError('CRITICAL ERROR: We are ' .
|
||
|
$this->_registry->parsedPackageNameToString($info) . ', but the file ' .
|
||
|
'downloaded claims to be ' .
|
||
|
$this->_registry->parsedPackageNameToString($this->getParsedPackage()));
|
||
|
} while (false);
|
||
|
}
|
||
|
if (PEAR::isError($err) || !$this->_valid) {
|
||
|
return $err;
|
||
|
}
|
||
|
}
|
||
|
$this->_type = 'local';
|
||
|
return $this->_packagefile;
|
||
|
}
|
||
|
|
||
|
function &getPackageFile()
|
||
|
{
|
||
|
return $this->_packagefile;
|
||
|
}
|
||
|
|
||
|
function &getDownloader()
|
||
|
{
|
||
|
return $this->_downloader;
|
||
|
}
|
||
|
|
||
|
function getType()
|
||
|
{
|
||
|
return $this->_type;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Like {@link initialize()}, but operates on a dependency
|
||
|
*/
|
||
|
function fromDepURL($dep)
|
||
|
{
|
||
|
$this->_downloadURL = $dep;
|
||
|
if (isset($dep['uri'])) {
|
||
|
$options = $this->_downloader->getOptions();
|
||
|
if (!extension_loaded("zlib") || isset($options['nocompress'])) {
|
||
|
$ext = '.tar';
|
||
|
} else {
|
||
|
$ext = '.tgz';
|
||
|
}
|
||
|
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
$err = $this->_fromUrl($dep['uri'] . $ext);
|
||
|
PEAR::popErrorHandling();
|
||
|
if (PEAR::isError($err)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $err->getMessage());
|
||
|
}
|
||
|
return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
|
||
|
'cannot download');
|
||
|
}
|
||
|
} else {
|
||
|
$this->_parsedname =
|
||
|
array(
|
||
|
'package' => $dep['info']->getPackage(),
|
||
|
'channel' => $dep['info']->getChannel(),
|
||
|
'version' => $dep['version']
|
||
|
);
|
||
|
if (!isset($dep['nodefault'])) {
|
||
|
$this->_parsedname['group'] = 'default'; // download the default dependency group
|
||
|
$this->_explicitGroup = false;
|
||
|
}
|
||
|
$this->_rawpackagefile = $dep['raw'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function detectDependencies($params)
|
||
|
{
|
||
|
$options = $this->_downloader->getOptions();
|
||
|
if (isset($options['downloadonly'])) {
|
||
|
return;
|
||
|
}
|
||
|
if (isset($options['offline'])) {
|
||
|
$this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
|
||
|
return;
|
||
|
}
|
||
|
$pname = $this->getParsedPackage();
|
||
|
if (!$pname) {
|
||
|
return;
|
||
|
}
|
||
|
$deps = $this->getDeps();
|
||
|
if (!$deps) {
|
||
|
return;
|
||
|
}
|
||
|
if (isset($deps['required'])) { // package.xml 2.0
|
||
|
return $this->_detect2($deps, $pname, $options, $params);
|
||
|
} else {
|
||
|
return $this->_detect1($deps, $pname, $options, $params);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function setValidated()
|
||
|
{
|
||
|
$this->_validated = true;
|
||
|
}
|
||
|
|
||
|
function alreadyValidated()
|
||
|
{
|
||
|
return $this->_validated;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove packages to be downloaded that are already installed
|
||
|
* @param array of PEAR_Downloader_Package objects
|
||
|
* @static
|
||
|
*/
|
||
|
function removeInstalled(&$params)
|
||
|
{
|
||
|
if (!isset($params[0])) {
|
||
|
return;
|
||
|
}
|
||
|
$options = $params[0]->_downloader->getOptions();
|
||
|
if (!isset($options['downloadonly'])) {
|
||
|
foreach ($params as $i => $param) {
|
||
|
// remove self if already installed with this version
|
||
|
// this does not need any pecl magic - we only remove exact matches
|
||
|
if ($param->_installRegistry->packageExists($param->getPackage(), $param->getChannel())) {
|
||
|
if (version_compare($param->_installRegistry->packageInfo($param->getPackage(), 'version',
|
||
|
$param->getChannel()), $param->getVersion(), '==')) {
|
||
|
if (!isset($options['force'])) {
|
||
|
$info = $param->getParsedPackage();
|
||
|
unset($info['version']);
|
||
|
unset($info['state']);
|
||
|
if (!isset($options['soft'])) {
|
||
|
$param->_downloader->log(1, 'Skipping package "' .
|
||
|
$param->getShortName() .
|
||
|
'", already installed as version ' .
|
||
|
$param->_installRegistry->packageInfo($param->getPackage(),
|
||
|
'version', $param->getChannel()));
|
||
|
}
|
||
|
$params[$i] = false;
|
||
|
}
|
||
|
} elseif (!isset($options['force']) && !isset($options['upgrade']) &&
|
||
|
!isset($options['soft'])) {
|
||
|
$info = $param->getParsedPackage();
|
||
|
$param->_downloader->log(1, 'Skipping package "' .
|
||
|
$param->getShortName() .
|
||
|
'", already installed as version ' .
|
||
|
$param->_installRegistry->packageInfo($param->getPackage(), 'version',
|
||
|
$param->getChannel()));
|
||
|
$params[$i] = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
PEAR_Downloader_Package::removeDuplicates($params);
|
||
|
}
|
||
|
|
||
|
function _detect2($deps, $pname, $options, $params)
|
||
|
{
|
||
|
$this->_downloadDeps = array();
|
||
|
$groupnotfound = false;
|
||
|
foreach (array('package', 'subpackage') as $packagetype) {
|
||
|
// get required dependency group
|
||
|
if (isset($deps['required'][$packagetype])) {
|
||
|
if (isset($deps['required'][$packagetype][0])) {
|
||
|
foreach ($deps['required'][$packagetype] as $dep) {
|
||
|
if (isset($dep['conflicts'])) {
|
||
|
// skip any package that this package conflicts with
|
||
|
continue;
|
||
|
}
|
||
|
$ret = $this->_detect2Dep($dep, $pname, 'required', $params);
|
||
|
if (is_array($ret)) {
|
||
|
$this->_downloadDeps[] = $ret;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
$dep = $deps['required'][$packagetype];
|
||
|
if (!isset($dep['conflicts'])) {
|
||
|
// skip any package that this package conflicts with
|
||
|
$ret = $this->_detect2Dep($dep, $pname, 'required', $params);
|
||
|
if (is_array($ret)) {
|
||
|
$this->_downloadDeps[] = $ret;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// get optional dependency group, if any
|
||
|
if (isset($deps['optional'][$packagetype])) {
|
||
|
$skipnames = array();
|
||
|
if (!isset($deps['optional'][$packagetype][0])) {
|
||
|
$deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
|
||
|
}
|
||
|
foreach ($deps['optional'][$packagetype] as $dep) {
|
||
|
$skip = false;
|
||
|
if (!isset($options['alldeps'])) {
|
||
|
$dep['package'] = $dep['name'];
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(3, 'Notice: package "' .
|
||
|
$this->_registry->parsedPackageNameToString($this->getParsedPackage(),
|
||
|
true) . '" optional dependency "' .
|
||
|
$this->_registry->parsedPackageNameToString(array('package' =>
|
||
|
$dep['name'], 'channel' => 'pear.php.net'), true) .
|
||
|
'" will not be automatically downloaded');
|
||
|
}
|
||
|
$skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
|
||
|
$skip = true;
|
||
|
unset($dep['package']);
|
||
|
}
|
||
|
if (!($ret = $this->_detect2Dep($dep, $pname, 'optional', $params))) {
|
||
|
$dep['package'] = $dep['name'];
|
||
|
if (@$skipnames[count($skipnames) - 1] ==
|
||
|
$this->_registry->parsedPackageNameToString($dep, true)) {
|
||
|
array_pop($skipnames);
|
||
|
}
|
||
|
}
|
||
|
if (!$skip && is_array($ret)) {
|
||
|
$this->_downloadDeps[] = $ret;
|
||
|
}
|
||
|
}
|
||
|
if (count($skipnames)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(1, 'Did not download optional dependencies: ' .
|
||
|
implode(', ', $skipnames) .
|
||
|
', use --alldeps to download automatically');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// get requested dependency group, if any
|
||
|
$groupname = $this->getGroup();
|
||
|
$explicit = $this->_explicitGroup;
|
||
|
if (!$groupname) {
|
||
|
if ($this->canDefault()) {
|
||
|
$groupname = 'default'; // try the default dependency group
|
||
|
} else {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
if ($groupnotfound) {
|
||
|
continue;
|
||
|
}
|
||
|
if (isset($deps['group'])) {
|
||
|
if (isset($deps['group']['attribs'])) {
|
||
|
if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
|
||
|
$group = $deps['group'];
|
||
|
} elseif ($explicit) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, 'Warning: package "' .
|
||
|
$this->_registry->parsedPackageNameToString($pname, true) .
|
||
|
'" has no dependency ' . 'group named "' . $groupname . '"');
|
||
|
}
|
||
|
$groupnotfound = true;
|
||
|
continue;
|
||
|
}
|
||
|
} else {
|
||
|
$found = false;
|
||
|
foreach ($deps['group'] as $group) {
|
||
|
if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
|
||
|
$found = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!$found) {
|
||
|
if ($explicit) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, 'Warning: package "' .
|
||
|
$this->_registry->parsedPackageNameToString($pname, true) .
|
||
|
'" has no dependency ' . 'group named "' . $groupname . '"');
|
||
|
}
|
||
|
}
|
||
|
$groupnotfound = true;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (isset($group)) {
|
||
|
if (isset($group[$packagetype])) {
|
||
|
if (isset($group[$packagetype][0])) {
|
||
|
foreach ($group[$packagetype] as $dep) {
|
||
|
$ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
|
||
|
$group['attribs']['name'] . '"', $params);
|
||
|
if (is_array($ret)) {
|
||
|
$this->_downloadDeps[] = $ret;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
$ret = $this->_detect2Dep($group[$packagetype], $pname,
|
||
|
'dependency group "' .
|
||
|
$group['attribs']['name'] . '"', $params);
|
||
|
if (is_array($ret)) {
|
||
|
$this->_downloadDeps[] = $ret;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function _detect2Dep($dep, $pname, $group, $params)
|
||
|
{
|
||
|
if (isset($dep['conflicts'])) {
|
||
|
return true;
|
||
|
}
|
||
|
$options = $this->_downloader->getOptions();
|
||
|
if (isset($dep['uri'])) {
|
||
|
return array('uri' => $dep['uri'], 'dep' => $dep);;
|
||
|
}
|
||
|
$testdep = $dep;
|
||
|
$testdep['package'] = $dep['name'];
|
||
|
if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
|
||
|
$dep['package'] = $dep['name'];
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
|
||
|
' dependency "' .
|
||
|
$this->_registry->parsedPackageNameToString($dep, true) .
|
||
|
'", will be installed');
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
$options = $this->_downloader->getOptions();
|
||
|
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
if ($this->_explicitState) {
|
||
|
$pname['state'] = $this->_explicitState;
|
||
|
}
|
||
|
$url =
|
||
|
$this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
|
||
|
if (PEAR::isError($url)) {
|
||
|
PEAR::popErrorHandling();
|
||
|
return $url;
|
||
|
}
|
||
|
$dep['package'] = $dep['name'];
|
||
|
$ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
|
||
|
!isset($options['alldeps']));
|
||
|
PEAR::popErrorHandling();
|
||
|
if (PEAR::isError($ret)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $ret->getMessage());
|
||
|
}
|
||
|
return false;
|
||
|
} else {
|
||
|
// check to see if a dep is already installed and is the same or newer
|
||
|
if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
|
||
|
$oper = 'has';
|
||
|
} else {
|
||
|
$oper = 'gt';
|
||
|
}
|
||
|
// do not try to move this before getDepPackageDownloadURL
|
||
|
// we can't determine whether upgrade is necessary until we know what
|
||
|
// version would be downloaded
|
||
|
if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
|
||
|
$version = $this->_installRegistry->packageInfo($dep['name'], 'version',
|
||
|
$dep['channel']);
|
||
|
$dep['package'] = $dep['name'];
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
|
||
|
' dependency "' .
|
||
|
$this->_registry->parsedPackageNameToString($dep, true) .
|
||
|
'" version ' . $url['version'] . ', already installed as version ' .
|
||
|
$version);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
if (isset($dep['nodefault'])) {
|
||
|
$ret['nodefault'] = true;
|
||
|
}
|
||
|
return $ret;
|
||
|
}
|
||
|
|
||
|
function _detect1($deps, $pname, $options, $params)
|
||
|
{
|
||
|
$this->_downloadDeps = array();
|
||
|
$skipnames = array();
|
||
|
foreach ($deps as $dep) {
|
||
|
$nodownload = false;
|
||
|
if ($dep['type'] == 'pkg') {
|
||
|
$dep['channel'] = 'pear.php.net';
|
||
|
$dep['package'] = $dep['name'];
|
||
|
switch ($dep['rel']) {
|
||
|
case 'not' :
|
||
|
continue 2;
|
||
|
case 'ge' :
|
||
|
case 'eq' :
|
||
|
case 'gt' :
|
||
|
case 'has' :
|
||
|
$group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
|
||
|
'required' :
|
||
|
'optional';
|
||
|
if (PEAR_Downloader_Package::willDownload($dep, $params)) {
|
||
|
$this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
|
||
|
. ' dependency "' .
|
||
|
$this->_registry->parsedPackageNameToString($dep, true) .
|
||
|
'", will be installed');
|
||
|
continue 2;
|
||
|
}
|
||
|
$fakedp = new PEAR_PackageFile_v1;
|
||
|
$fakedp->setPackage($dep['name']);
|
||
|
// skip internet check if we are not upgrading (bug #5810)
|
||
|
if (!isset($options['upgrade']) && $this->isInstalled(
|
||
|
$fakedp, $dep['rel'])) {
|
||
|
$this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
|
||
|
. ' dependency "' .
|
||
|
$this->_registry->parsedPackageNameToString($dep, true) .
|
||
|
'", is already installed');
|
||
|
continue 2;
|
||
|
}
|
||
|
}
|
||
|
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
if ($this->_explicitState) {
|
||
|
$pname['state'] = $this->_explicitState;
|
||
|
}
|
||
|
$url =
|
||
|
$this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
|
||
|
$chan = 'pear.php.net';
|
||
|
if (PEAR::isError($url)) {
|
||
|
// check to see if this is a pecl package that has jumped
|
||
|
// from pear.php.net to pecl.php.net channel
|
||
|
if (!class_exists('PEAR_Dependency2')) {
|
||
|
require_once 'PEAR/Dependency2.php';
|
||
|
}
|
||
|
$newdep = PEAR_Dependency2::normalizeDep($dep);
|
||
|
$newdep = $newdep[0];
|
||
|
$newdep['channel'] = 'pecl.php.net';
|
||
|
$chan = 'pecl.php.net';
|
||
|
$url =
|
||
|
$this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
|
||
|
$obj = &$this->_installRegistry->getPackage($dep['name']);
|
||
|
if (PEAR::isError($url)) {
|
||
|
PEAR::popErrorHandling();
|
||
|
if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
|
||
|
$group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
|
||
|
'required' :
|
||
|
'optional';
|
||
|
$dep['package'] = $dep['name'];
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(3, $this->getShortName() .
|
||
|
': Skipping ' . $group . ' dependency "' .
|
||
|
$this->_registry->parsedPackageNameToString($dep, true) .
|
||
|
'", already installed as version ' . $obj->getVersion());
|
||
|
}
|
||
|
if (@$skipnames[count($skipnames) - 1] ==
|
||
|
$this->_registry->parsedPackageNameToString($dep, true)) {
|
||
|
array_pop($skipnames);
|
||
|
}
|
||
|
continue;
|
||
|
} else {
|
||
|
if (isset($dep['optional']) && $dep['optional'] == 'yes') {
|
||
|
$this->_downloader->log(2, $this->getShortName() .
|
||
|
': Skipping ' . $group
|
||
|
. ' dependency "' .
|
||
|
$this->_registry->parsedPackageNameToString($dep, true) .
|
||
|
'", no releases exist');
|
||
|
continue;
|
||
|
} else {
|
||
|
return $url;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
PEAR::popErrorHandling();
|
||
|
if (!isset($options['alldeps'])) {
|
||
|
if (isset($dep['optional']) && $dep['optional'] == 'yes') {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(3, 'Notice: package "' .
|
||
|
$this->getShortName() .
|
||
|
'" optional dependency "' .
|
||
|
$this->_registry->parsedPackageNameToString(
|
||
|
array('channel' => $chan, 'package' =>
|
||
|
$dep['name']), true) .
|
||
|
'" will not be automatically downloaded');
|
||
|
}
|
||
|
$skipnames[] = $this->_registry->parsedPackageNameToString(
|
||
|
array('channel' => $chan, 'package' =>
|
||
|
$dep['name']), true);
|
||
|
$nodownload = true;
|
||
|
}
|
||
|
}
|
||
|
if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
|
||
|
if (!isset($dep['optional']) || $dep['optional'] == 'no') {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(3, 'Notice: package "' .
|
||
|
$this->getShortName() .
|
||
|
'" required dependency "' .
|
||
|
$this->_registry->parsedPackageNameToString(
|
||
|
array('channel' => $chan, 'package' =>
|
||
|
$dep['name']), true) .
|
||
|
'" will not be automatically downloaded');
|
||
|
}
|
||
|
$skipnames[] = $this->_registry->parsedPackageNameToString(
|
||
|
array('channel' => $chan, 'package' =>
|
||
|
$dep['name']), true);
|
||
|
$nodownload = true;
|
||
|
}
|
||
|
}
|
||
|
// check to see if a dep is already installed
|
||
|
// do not try to move this before getDepPackageDownloadURL
|
||
|
// we can't determine whether upgrade is necessary until we know what
|
||
|
// version would be downloaded
|
||
|
if (!isset($options['force']) && $this->isInstalled(
|
||
|
$url, $dep['rel'])) {
|
||
|
$group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
|
||
|
'required' :
|
||
|
'optional';
|
||
|
$dep['package'] = $dep['name'];
|
||
|
if (isset($newdep)) {
|
||
|
$version = $this->_installRegistry->packageInfo($newdep['name'], 'version',
|
||
|
$newdep['channel']);
|
||
|
} else {
|
||
|
$version = $this->_installRegistry->packageInfo($dep['name'], 'version');
|
||
|
}
|
||
|
$dep['version'] = $url['version'];
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
|
||
|
' dependency "' .
|
||
|
$this->_registry->parsedPackageNameToString($dep, true) .
|
||
|
'", already installed as version ' . $version);
|
||
|
}
|
||
|
if (@$skipnames[count($skipnames) - 1] ==
|
||
|
$this->_registry->parsedPackageNameToString($dep, true)) {
|
||
|
array_pop($skipnames);
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
if ($nodownload) {
|
||
|
continue;
|
||
|
}
|
||
|
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
if (isset($newdep)) {
|
||
|
$dep = $newdep;
|
||
|
}
|
||
|
$dep['package'] = $dep['name'];
|
||
|
$ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
|
||
|
isset($dep['optional']) && $dep['optional'] == 'yes' &&
|
||
|
!isset($options['alldeps']));
|
||
|
PEAR::popErrorHandling();
|
||
|
if (PEAR::isError($ret)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $ret->getMessage());
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
$this->_downloadDeps[] = $ret;
|
||
|
}
|
||
|
}
|
||
|
if (count($skipnames)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(1, 'Did not download dependencies: ' .
|
||
|
implode(', ', $skipnames) .
|
||
|
', use --alldeps or --onlyreqdeps to download automatically');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function setDownloadURL($pkg)
|
||
|
{
|
||
|
$this->_downloadURL = $pkg;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the package.xml object for this downloaded package
|
||
|
*
|
||
|
* @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
|
||
|
*/
|
||
|
function setPackageFile(&$pkg)
|
||
|
{
|
||
|
$this->_packagefile = &$pkg;
|
||
|
}
|
||
|
|
||
|
function getShortName()
|
||
|
{
|
||
|
return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
|
||
|
'package' => $this->getPackage()), true);
|
||
|
}
|
||
|
|
||
|
function getParsedPackage()
|
||
|
{
|
||
|
if (isset($this->_packagefile) || isset($this->_parsedname)) {
|
||
|
return array('channel' => $this->getChannel(),
|
||
|
'package' => $this->getPackage(),
|
||
|
'version' => $this->getVersion());
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function getDownloadURL()
|
||
|
{
|
||
|
return $this->_downloadURL;
|
||
|
}
|
||
|
|
||
|
function canDefault()
|
||
|
{
|
||
|
if (isset($this->_downloadURL)) {
|
||
|
if (isset($this->_downloadURL['nodefault'])) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
function getPackage()
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile->getPackage();
|
||
|
} elseif (isset($this->_downloadURL['info'])) {
|
||
|
return $this->_downloadURL['info']->getPackage();
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
|
||
|
*/
|
||
|
function isSubpackage(&$pf)
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile->isSubpackage($pf);
|
||
|
} elseif (isset($this->_downloadURL['info'])) {
|
||
|
return $this->_downloadURL['info']->isSubpackage($pf);
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getPackageType()
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile->getPackageType();
|
||
|
} elseif (isset($this->_downloadURL['info'])) {
|
||
|
return $this->_downloadURL['info']->getPackageType();
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function isBundle()
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile->getPackageType() == 'bundle';
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getPackageXmlVersion()
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile->getPackagexmlVersion();
|
||
|
} elseif (isset($this->_downloadURL['info'])) {
|
||
|
return $this->_downloadURL['info']->getPackagexmlVersion();
|
||
|
} else {
|
||
|
return '1.0';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getChannel()
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile->getChannel();
|
||
|
} elseif (isset($this->_downloadURL['info'])) {
|
||
|
return $this->_downloadURL['info']->getChannel();
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getURI()
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile->getURI();
|
||
|
} elseif (isset($this->_downloadURL['info'])) {
|
||
|
return $this->_downloadURL['info']->getURI();
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getVersion()
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile->getVersion();
|
||
|
} elseif (isset($this->_downloadURL['version'])) {
|
||
|
return $this->_downloadURL['version'];
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function isCompatible($pf)
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile->isCompatible($pf);
|
||
|
} elseif (isset($this->_downloadURL['info'])) {
|
||
|
return $this->_downloadURL['info']->isCompatible($pf);
|
||
|
} else {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function setGroup($group)
|
||
|
{
|
||
|
$this->_parsedname['group'] = $group;
|
||
|
}
|
||
|
|
||
|
function getGroup()
|
||
|
{
|
||
|
if (isset($this->_parsedname['group'])) {
|
||
|
return $this->_parsedname['group'];
|
||
|
} else {
|
||
|
return '';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function isExtension($name)
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
return $this->_packagefile->isExtension($name);
|
||
|
} elseif (isset($this->_downloadURL['info'])) {
|
||
|
return $this->_downloadURL['info']->getProvidesExtension() == $name;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getDeps()
|
||
|
{
|
||
|
if (isset($this->_packagefile)) {
|
||
|
if ($this->_packagefile->getPackagexmlVersion() == '2.0') {
|
||
|
return $this->_packagefile->getDeps(true);
|
||
|
} else {
|
||
|
return $this->_packagefile->getDeps();
|
||
|
}
|
||
|
} elseif (isset($this->_downloadURL['info'])) {
|
||
|
if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
|
||
|
return $this->_downloadURL['info']->getDeps(true);
|
||
|
} else {
|
||
|
return $this->_downloadURL['info']->getDeps();
|
||
|
}
|
||
|
} else {
|
||
|
return array();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
|
||
|
* returned from getDepDownloadURL()
|
||
|
*/
|
||
|
function isEqual($param)
|
||
|
{
|
||
|
if (is_object($param)) {
|
||
|
$channel = $param->getChannel();
|
||
|
$package = $param->getPackage();
|
||
|
if ($param->getURI()) {
|
||
|
$param = array(
|
||
|
'channel' => $param->getChannel(),
|
||
|
'package' => $param->getPackage(),
|
||
|
'version' => $param->getVersion(),
|
||
|
'uri' => $param->getURI(),
|
||
|
);
|
||
|
} else {
|
||
|
$param = array(
|
||
|
'channel' => $param->getChannel(),
|
||
|
'package' => $param->getPackage(),
|
||
|
'version' => $param->getVersion(),
|
||
|
);
|
||
|
}
|
||
|
} else {
|
||
|
if (isset($param['uri'])) {
|
||
|
if ($this->getChannel() != '__uri') {
|
||
|
return false;
|
||
|
}
|
||
|
return $param['uri'] == $this->getURI();
|
||
|
}
|
||
|
$package = isset($param['package']) ? $param['package'] :
|
||
|
$param['info']->getPackage();
|
||
|
$channel = isset($param['channel']) ? $param['channel'] :
|
||
|
$param['info']->getChannel();
|
||
|
if (isset($param['rel'])) {
|
||
|
if (!class_exists('PEAR_Dependency2')) {
|
||
|
require_once 'PEAR/Dependency2.php';
|
||
|
}
|
||
|
$newdep = PEAR_Dependency2::normalizeDep($param);
|
||
|
$newdep = $newdep[0];
|
||
|
} elseif (isset($param['min'])) {
|
||
|
$newdep = $param;
|
||
|
}
|
||
|
}
|
||
|
if (isset($newdep)) {
|
||
|
if (!isset($newdep['min'])) {
|
||
|
$newdep['min'] = '0';
|
||
|
}
|
||
|
if (!isset($newdep['max'])) {
|
||
|
$newdep['max'] = '100000000000000000000';
|
||
|
}
|
||
|
// use magic to support pecl packages suddenly jumping to the pecl channel
|
||
|
// we need to support both dependency possibilities
|
||
|
if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
|
||
|
if ($package == $this->getPackage()) {
|
||
|
$channel = 'pecl.php.net';
|
||
|
}
|
||
|
}
|
||
|
if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
|
||
|
if ($package == $this->getPackage()) {
|
||
|
$channel = 'pear.php.net';
|
||
|
}
|
||
|
}
|
||
|
return (strtolower($package) == strtolower($this->getPackage()) &&
|
||
|
$channel == $this->getChannel() &&
|
||
|
version_compare($newdep['min'], $this->getVersion(), '<=') &&
|
||
|
version_compare($newdep['max'], $this->getVersion(), '>='));
|
||
|
}
|
||
|
// use magic to support pecl packages suddenly jumping to the pecl channel
|
||
|
if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
|
||
|
if (strtolower($package) == strtolower($this->getPackage())) {
|
||
|
$channel = 'pear.php.net';
|
||
|
}
|
||
|
}
|
||
|
if (isset($param['version'])) {
|
||
|
return (strtolower($package) == strtolower($this->getPackage()) &&
|
||
|
$channel == $this->getChannel() &&
|
||
|
$param['version'] == $this->getVersion());
|
||
|
} else {
|
||
|
return strtolower($package) == strtolower($this->getPackage()) &&
|
||
|
$channel == $this->getChannel();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function isInstalled($dep, $oper = '==')
|
||
|
{
|
||
|
if (!$dep) {
|
||
|
return false;
|
||
|
}
|
||
|
if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
|
||
|
return false;
|
||
|
}
|
||
|
if (is_object($dep)) {
|
||
|
$package = $dep->getPackage();
|
||
|
$channel = $dep->getChannel();
|
||
|
if ($dep->getURI()) {
|
||
|
$dep = array(
|
||
|
'uri' => $dep->getURI(),
|
||
|
'version' => $dep->getVersion(),
|
||
|
);
|
||
|
} else {
|
||
|
$dep = array(
|
||
|
'version' => $dep->getVersion(),
|
||
|
);
|
||
|
}
|
||
|
} else {
|
||
|
if (isset($dep['uri'])) {
|
||
|
$channel = '__uri';
|
||
|
$package = $dep['dep']['name'];
|
||
|
} else {
|
||
|
$channel = $dep['info']->getChannel();
|
||
|
$package = $dep['info']->getPackage();
|
||
|
}
|
||
|
}
|
||
|
$options = $this->_downloader->getOptions();
|
||
|
$test = $this->_installRegistry->packageExists($package, $channel);
|
||
|
if (!$test && $channel == 'pecl.php.net') {
|
||
|
// do magic to allow upgrading from old pecl packages to new ones
|
||
|
$test = $this->_installRegistry->packageExists($package, 'pear.php.net');
|
||
|
$channel = 'pear.php.net';
|
||
|
}
|
||
|
if ($test) {
|
||
|
if (isset($dep['uri'])) {
|
||
|
if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
if (isset($options['upgrade'])) {
|
||
|
if ($oper == 'has') {
|
||
|
if (version_compare($this->_installRegistry->packageInfo(
|
||
|
$package, 'version', $channel),
|
||
|
$dep['version'], '>=')) {
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
if (version_compare($this->_installRegistry->packageInfo(
|
||
|
$package, 'version', $channel),
|
||
|
$dep['version'], '>=')) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array
|
||
|
* @static
|
||
|
*/
|
||
|
function removeDuplicates(&$params)
|
||
|
{
|
||
|
$pnames = array();
|
||
|
foreach ($params as $i => $param) {
|
||
|
if (!$param) {
|
||
|
continue;
|
||
|
}
|
||
|
if ($param->getPackage()) {
|
||
|
$pnames[$i] = $param->getChannel() . '/' .
|
||
|
$param->getPackage() . '-' . $param->getVersion() . '#' . $param->getGroup();
|
||
|
}
|
||
|
}
|
||
|
$pnames = array_unique($pnames);
|
||
|
$unset = array_diff(array_keys($params), array_keys($pnames));
|
||
|
$testp = array_flip($pnames);
|
||
|
foreach ($params as $i => $param) {
|
||
|
if (!$param) {
|
||
|
$unset[] = $i;
|
||
|
continue;
|
||
|
}
|
||
|
if (!is_a($param, 'PEAR_Downloader_Package')) {
|
||
|
$unset[] = $i;
|
||
|
continue;
|
||
|
}
|
||
|
if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
|
||
|
$param->getVersion() . '#' . $param->getGroup()])) {
|
||
|
$unset[] = $i;
|
||
|
}
|
||
|
}
|
||
|
foreach ($unset as $i) {
|
||
|
unset($params[$i]);
|
||
|
}
|
||
|
$ret = array();
|
||
|
foreach ($params as $i => $param) {
|
||
|
$ret[] = &$params[$i];
|
||
|
}
|
||
|
$params = array();
|
||
|
foreach ($ret as $i => $param) {
|
||
|
$params[] = &$ret[$i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function explicitState()
|
||
|
{
|
||
|
return $this->_explicitState;
|
||
|
}
|
||
|
|
||
|
function setExplicitState($s)
|
||
|
{
|
||
|
$this->_explicitState = $s;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @static
|
||
|
*/
|
||
|
function mergeDependencies(&$params)
|
||
|
{
|
||
|
$newparams = array();
|
||
|
$bundles = array();
|
||
|
foreach ($params as $i => $param) {
|
||
|
if (!$param->isBundle()) {
|
||
|
continue;
|
||
|
}
|
||
|
$bundles[] = $i;
|
||
|
$pf = &$param->getPackageFile();
|
||
|
$newdeps = array();
|
||
|
$contents = $pf->getBundledPackages();
|
||
|
if (!is_array($contents)) {
|
||
|
$contents = array($contents);
|
||
|
}
|
||
|
foreach ($contents as $file) {
|
||
|
$filecontents = $pf->getFileContents($file);
|
||
|
$dl = &$param->getDownloader();
|
||
|
$options = $dl->getOptions();
|
||
|
$fp = @fopen($dl->getDownloadDir() . DIRECTORY_SEPARATOR . $file, 'wb');
|
||
|
if (!$fp) {
|
||
|
continue;
|
||
|
}
|
||
|
fwrite($fp, $filecontents, strlen($filecontents));
|
||
|
fclose($fp);
|
||
|
if ($s = $params[$i]->explicitState()) {
|
||
|
$obj->setExplicitState($s);
|
||
|
}
|
||
|
$obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
|
||
|
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
$e = $obj->_fromFile($a = $dl->getDownloadDir() . DIRECTORY_SEPARATOR . $file);
|
||
|
PEAR::popErrorHandling();
|
||
|
if (PEAR::isError($e)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$dl->log(0, $e->getMessage());
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
$j = &$obj;
|
||
|
if (!PEAR_Downloader_Package::willDownload($j,
|
||
|
array_merge($params, $newparams)) && !$param->isInstalled($j)) {
|
||
|
$newparams[] = &$j;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
foreach ($bundles as $i) {
|
||
|
unset($params[$i]); // remove bundles - only their contents matter for installation
|
||
|
}
|
||
|
PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
|
||
|
if (count($newparams)) { // add in bundled packages for install
|
||
|
foreach ($newparams as $i => $unused) {
|
||
|
$params[] = &$newparams[$i];
|
||
|
}
|
||
|
$newparams = array();
|
||
|
}
|
||
|
foreach ($params as $i => $param) {
|
||
|
$newdeps = array();
|
||
|
foreach ($param->_downloadDeps as $dep) {
|
||
|
if (!PEAR_Downloader_Package::willDownload($dep,
|
||
|
array_merge($params, $newparams)) && !$param->isInstalled($dep)) {
|
||
|
$newdeps[] = $dep;
|
||
|
} else {
|
||
|
// detect versioning conflicts here
|
||
|
}
|
||
|
}
|
||
|
// convert the dependencies into PEAR_Downloader_Package objects for the next time
|
||
|
// around
|
||
|
$params[$i]->_downloadDeps = array();
|
||
|
foreach ($newdeps as $dep) {
|
||
|
$obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
|
||
|
if ($s = $params[$i]->explicitState()) {
|
||
|
$obj->setExplicitState($s);
|
||
|
}
|
||
|
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
$e = $obj->fromDepURL($dep);
|
||
|
PEAR::popErrorHandling();
|
||
|
if (PEAR::isError($e)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$obj->_downloader->log(0, $e->getMessage());
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
$e = $obj->detectDependencies($params);
|
||
|
if (PEAR::isError($e)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$obj->_downloader->log(0, $e->getMessage());
|
||
|
}
|
||
|
}
|
||
|
$j = &$obj;
|
||
|
$newparams[] = &$j;
|
||
|
}
|
||
|
}
|
||
|
if (count($newparams)) {
|
||
|
foreach ($newparams as $i => $unused) {
|
||
|
$params[] = &$newparams[$i];
|
||
|
}
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @static
|
||
|
*/
|
||
|
function willDownload($param, $params)
|
||
|
{
|
||
|
if (!is_array($params)) {
|
||
|
return false;
|
||
|
}
|
||
|
foreach ($params as $obj) {
|
||
|
if ($obj->isEqual($param)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* For simpler unit-testing
|
||
|
* @param PEAR_Config
|
||
|
* @param int
|
||
|
* @param string
|
||
|
*/
|
||
|
function &getPackagefileObject(&$c, $d, $t = false)
|
||
|
{
|
||
|
$a = &new PEAR_PackageFile($c, $d, $t);
|
||
|
return $a;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* This will retrieve from a local file if possible, and parse out
|
||
|
* a group name as well. The original parameter will be modified to reflect this.
|
||
|
* @param string|array can be a parsed package name as well
|
||
|
* @access private
|
||
|
*/
|
||
|
function _fromFile(&$param)
|
||
|
{
|
||
|
if (is_string($param) && !@is_file($param)) {
|
||
|
$test = explode('#', $param);
|
||
|
$group = array_pop($test);
|
||
|
if (@is_file(implode('#', $test))) {
|
||
|
$this->setGroup($group);
|
||
|
$param = implode('#', $test);
|
||
|
$this->_explicitGroup = true;
|
||
|
}
|
||
|
}
|
||
|
if (@is_file($param)) {
|
||
|
$this->_type = 'local';
|
||
|
$options = $this->_downloader->getOptions();
|
||
|
if (isset($options['downloadonly'])) {
|
||
|
$pkg = &$this->getPackagefileObject($this->_config,
|
||
|
$this->_downloader->_debug);
|
||
|
} else {
|
||
|
$pkg = &$this->getPackagefileObject($this->_config,
|
||
|
$this->_downloader->_debug, $this->_downloader->getDownloadDir());
|
||
|
}
|
||
|
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
$pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
|
||
|
PEAR::popErrorHandling();
|
||
|
if (PEAR::isError($pf)) {
|
||
|
$this->_valid = false;
|
||
|
return $pf;
|
||
|
}
|
||
|
$this->_packagefile = &$pf;
|
||
|
if (!$this->getGroup()) {
|
||
|
$this->setGroup('default'); // install the default dependency group
|
||
|
}
|
||
|
return $this->_valid = true;
|
||
|
}
|
||
|
return $this->_valid = false;
|
||
|
}
|
||
|
|
||
|
function _fromUrl($param, $saveparam = '')
|
||
|
{
|
||
|
if (!is_array($param) &&
|
||
|
(preg_match('#^(http|ftp)://#', $param))) {
|
||
|
$options = $this->_downloader->getOptions();
|
||
|
$this->_type = 'url';
|
||
|
$callback = $this->_downloader->ui ?
|
||
|
array(&$this->_downloader, '_downloadCallback') : null;
|
||
|
$this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
$file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
|
||
|
$this->_downloader->getDownloadDir(), $callback);
|
||
|
$this->_downloader->popErrorHandling();
|
||
|
if (PEAR::isError($file)) {
|
||
|
if (!empty($saveparam)) {
|
||
|
$saveparam = ", cannot download \"$saveparam\"";
|
||
|
}
|
||
|
$err = PEAR::raiseError('Could not download from "' . $param .
|
||
|
'"' . $saveparam);
|
||
|
return $err;
|
||
|
}
|
||
|
if ($this->_rawpackagefile) {
|
||
|
require_once 'Archive/Tar.php';
|
||
|
$tar = &new Archive_Tar($file);
|
||
|
$packagexml = $tar->extractInString('package2.xml');
|
||
|
if (!$packagexml) {
|
||
|
$packagexml = $tar->extractInString('package.xml');
|
||
|
}
|
||
|
if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
|
||
|
str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
|
||
|
if ($this->getChannel() == 'pear.php.net') {
|
||
|
// be more lax for the existing PEAR packages that have not-ok
|
||
|
// characters in their package.xml
|
||
|
$this->_downloader->log(0, 'CRITICAL WARNING: The "' .
|
||
|
$this->getPackage() . '" package has invalid characters in its ' .
|
||
|
'package.xml. The next version of PEAR may not be able to install ' .
|
||
|
'this package for security reasons. Please open a bug report at ' .
|
||
|
'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
|
||
|
} else {
|
||
|
return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
|
||
|
'not match value returned from xml-rpc');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// whew, download worked!
|
||
|
if (isset($options['downloadonly'])) {
|
||
|
$pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
|
||
|
} else {
|
||
|
$pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug,
|
||
|
$this->_downloader->getDownloadDir());
|
||
|
}
|
||
|
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
$pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
|
||
|
PEAR::popErrorHandling();
|
||
|
if (PEAR::isError($pf)) {
|
||
|
if (is_array($pf->getUserInfo())) {
|
||
|
foreach ($pf->getUserInfo() as $err) {
|
||
|
if (is_array($err)) {
|
||
|
$err = $err['message'];
|
||
|
}
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, "Validation Error: $err");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $pf->getMessage());
|
||
|
}
|
||
|
$err = PEAR::raiseError('Download of "' . ($saveparam ? $saveparam :
|
||
|
$param) . '" succeeded, but it is not a valid package archive');
|
||
|
$this->_valid = false;
|
||
|
return $err;
|
||
|
}
|
||
|
$this->_packagefile = &$pf;
|
||
|
$this->setGroup('default'); // install the default dependency group
|
||
|
return $this->_valid = true;
|
||
|
}
|
||
|
return $this->_valid = false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param string|array pass in an array of format
|
||
|
* array(
|
||
|
* 'package' => 'pname',
|
||
|
* ['channel' => 'channame',]
|
||
|
* ['version' => 'version',]
|
||
|
* ['state' => 'state',])
|
||
|
* or a string of format [channame/]pname[-version|-state]
|
||
|
*/
|
||
|
function _fromString($param)
|
||
|
{
|
||
|
$options = $this->_downloader->getOptions();
|
||
|
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
$pname = $this->_registry->parsePackageName($param,
|
||
|
$this->_config->get('default_channel'));
|
||
|
PEAR::popErrorHandling();
|
||
|
if (PEAR::isError($pname)) {
|
||
|
if ($pname->getCode() == 'invalid') {
|
||
|
$this->_valid = false;
|
||
|
return false;
|
||
|
}
|
||
|
if ($pname->getCode() == 'channel') {
|
||
|
$parsed = $pname->getUserInfo();
|
||
|
if ($this->_downloader->discover($parsed['channel'])) {
|
||
|
if ($this->_config->get('auto_discover')) {
|
||
|
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
|
||
|
$pname = $this->_registry->parsePackageName($param,
|
||
|
$this->_config->get('default_channel'));
|
||
|
PEAR::popErrorHandling();
|
||
|
} else {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
|
||
|
'" is not initialized, use ' .
|
||
|
'"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
|
||
|
'or pear config-set auto_discover 1');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (PEAR::isError($pname)) {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $pname->getMessage());
|
||
|
}
|
||
|
if (is_array($param)) {
|
||
|
$param = $this->_registry->parsedPackageNameToString($param);
|
||
|
}
|
||
|
$err = PEAR::raiseError('invalid package name/package file "' .
|
||
|
$param . '"');
|
||
|
$this->_valid = false;
|
||
|
return $err;
|
||
|
}
|
||
|
} else {
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(0, $pname->getMessage());
|
||
|
}
|
||
|
$err = PEAR::raiseError('invalid package name/package file "' .
|
||
|
$param . '"');
|
||
|
$this->_valid = false;
|
||
|
return $err;
|
||
|
}
|
||
|
}
|
||
|
if (!isset($this->_type)) {
|
||
|
$this->_type = 'xmlrpc';
|
||
|
}
|
||
|
$this->_parsedname = $pname;
|
||
|
if (isset($pname['state'])) {
|
||
|
$this->_explicitState = $pname['state'];
|
||
|
} else {
|
||
|
$this->_explicitState = false;
|
||
|
}
|
||
|
if (isset($pname['group'])) {
|
||
|
$this->_explicitGroup = true;
|
||
|
} else {
|
||
|
$this->_explicitGroup = false;
|
||
|
}
|
||
|
$info = $this->_downloader->_getPackageDownloadUrl($pname);
|
||
|
if (PEAR::isError($info)) {
|
||
|
if ($pname['channel'] == 'pear.php.net') {
|
||
|
// try pecl
|
||
|
$pname['channel'] = 'pecl.php.net';
|
||
|
if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
|
||
|
if (!PEAR::isError($test)) {
|
||
|
$info = PEAR::raiseError($info->getMessage() . ' - package ' .
|
||
|
$this->_registry->parsedPackageNameToString($pname, true) .
|
||
|
' can be installed with "pecl install ' . $pname['package'] .
|
||
|
'"');
|
||
|
} else {
|
||
|
$pname['channel'] = 'pear.php.net';
|
||
|
}
|
||
|
} else {
|
||
|
$pname['channel'] = 'pear.php.net';
|
||
|
}
|
||
|
}
|
||
|
return $info;
|
||
|
}
|
||
|
$this->_rawpackagefile = $info['raw'];
|
||
|
$ret = $this->_analyzeDownloadURL($info, $param, $pname);
|
||
|
if (PEAR::isError($ret)) {
|
||
|
return $ret;
|
||
|
}
|
||
|
if ($ret) {
|
||
|
$this->_downloadURL = $ret;
|
||
|
return $this->_valid = (bool) $ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array output of package.getDownloadURL
|
||
|
* @param string|array|object information for detecting packages to be downloaded, and
|
||
|
* for errors
|
||
|
* @param array name information of the package
|
||
|
* @param array|null packages to be downloaded
|
||
|
* @param bool is this an optional dependency?
|
||
|
* @access private
|
||
|
*/
|
||
|
function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false)
|
||
|
{
|
||
|
if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
|
||
|
return false;
|
||
|
}
|
||
|
if (!$info) {
|
||
|
if (!is_string($param)) {
|
||
|
$saveparam = ", cannot download \"$param\"";
|
||
|
} else {
|
||
|
$saveparam = '';
|
||
|
}
|
||
|
// no releases exist
|
||
|
return PEAR::raiseError('No releases for package "' .
|
||
|
$this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
|
||
|
}
|
||
|
if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
|
||
|
$err = false;
|
||
|
if ($pname['channel'] == 'pecl.php.net') {
|
||
|
if ($info['info']->getChannel() != 'pear.php.net') {
|
||
|
$err = true;
|
||
|
}
|
||
|
} elseif ($info['info']->getChannel() == 'pecl.php.net') {
|
||
|
if ($pname['channel'] != 'pear.php.net') {
|
||
|
$err = true;
|
||
|
}
|
||
|
} else {
|
||
|
$err = true;
|
||
|
}
|
||
|
if ($err) {
|
||
|
return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
|
||
|
'" retrieved another channel\'s name for download! ("' .
|
||
|
$info['info']->getChannel() . '")');
|
||
|
}
|
||
|
}
|
||
|
if (!isset($info['url'])) {
|
||
|
$instead = ', will instead download version ' . $info['version'] .
|
||
|
', stability "' . $info['info']->getState() . '"';
|
||
|
// releases exist, but we failed to get any
|
||
|
if (isset($this->_downloader->_options['force'])) {
|
||
|
if (isset($pname['version'])) {
|
||
|
$vs = ', version "' . $pname['version'] . '"';
|
||
|
} elseif (isset($pname['state'])) {
|
||
|
$vs = ', stability "' . $pname['state'] . '"';
|
||
|
} elseif ($param == 'dependency') {
|
||
|
if (!class_exists('PEAR_Common')) {
|
||
|
require_once 'PEAR/Common.php';
|
||
|
}
|
||
|
if (!in_array($info['info']->getState(),
|
||
|
PEAR_Common::betterStates($this->_config->get('preferred_state'), true))) {
|
||
|
if ($optional) {
|
||
|
// don't spit out confusing error message
|
||
|
return $this->_downloader->_getPackageDownloadUrl(
|
||
|
array('package' => $pname['package'],
|
||
|
'channel' => $pname['channel'],
|
||
|
'version' => $info['version']));
|
||
|
}
|
||
|
$vs = ' within preferred state "' . $this->_config->get('preferred_state') .
|
||
|
'"';
|
||
|
} else {
|
||
|
if (!class_exists('PEAR_Dependency2')) {
|
||
|
require_once 'PEAR/Dependency2.php';
|
||
|
}
|
||
|
if ($optional) {
|
||
|
// don't spit out confusing error message
|
||
|
return $this->_downloader->_getPackageDownloadUrl(
|
||
|
array('package' => $pname['package'],
|
||
|
'channel' => $pname['channel'],
|
||
|
'version' => $info['version']));
|
||
|
}
|
||
|
$vs = PEAR_Dependency2::_getExtraString($pname);
|
||
|
$instead = '';
|
||
|
}
|
||
|
} else {
|
||
|
$vs = ' within preferred state "' . $this->_config->get(
|
||
|
'preferred_state') . '"';
|
||
|
}
|
||
|
if (!isset($options['soft'])) {
|
||
|
$this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
|
||
|
'/' . $pname['package'] . $vs . $instead);
|
||
|
}
|
||
|
// download the latest release
|
||
|
return $this->_downloader->_getPackageDownloadUrl(
|
||
|
array('package' => $pname['package'],
|
||
|
'channel' => $pname['channel'],
|
||
|
'version' => $info['version']));
|
||
|
} else {
|
||
|
// construct helpful error message
|
||
|
if (isset($pname['version'])) {
|
||
|
$vs = ', version "' . $pname['version'] . '"';
|
||
|
} elseif (isset($pname['state'])) {
|
||
|
$vs = ', stability "' . $pname['state'] . '"';
|
||
|
} elseif ($param == 'dependency') {
|
||
|
if (!class_exists('PEAR_Common')) {
|
||
|
require_once 'PEAR/Common.php';
|
||
|
}
|
||
|
if (!in_array($info['info']->getState(),
|
||
|
PEAR_Common::betterStates($this->_config->get('preferred_state'), true))) {
|
||
|
if ($optional) {
|
||
|
// don't spit out confusing error message, and don't die on
|
||
|
// optional dep failure!
|
||
|
return $this->_downloader->_getPackageDownloadUrl(
|
||
|
array('package' => $pname['package'],
|
||
|
'channel' => $pname['channel'],
|
||
|
'version' => $info['version']));
|
||
|
}
|
||
|
$vs = ' within preferred state "' . $this->_config->get('preferred_state') .
|
||
|
'"';
|
||
|
} else {
|
||
|
if (!class_exists('PEAR_Dependency2')) {
|
||
|
require_once 'PEAR/Dependency2.php';
|
||
|
}
|
||
|
if ($optional) {
|
||
|
// don't spit out confusing error message, and don't die on
|
||
|
// optional dep failure!
|
||
|
return $this->_downloader->_getPackageDownloadUrl(
|
||
|
array('package' => $pname['package'],
|
||
|
'channel' => $pname['channel'],
|
||
|
'version' => $info['version']));
|
||
|
}
|
||
|
$vs = PEAR_Dependency2::_getExtraString($pname);
|
||
|
}
|
||
|
} else {
|
||
|
$vs = ' within preferred state "' . $this->_downloader->config->get(
|
||
|
'preferred_state') . '"';
|
||
|
}
|
||
|
$options = $this->_downloader->getOptions();
|
||
|
// this is only set by the "download-all" command
|
||
|
if (isset($options['ignorepreferred_state'])) {
|
||
|
$err = PEAR::raiseError(
|
||
|
'Failed to download ' . $this->_registry->parsedPackageNameToString(
|
||
|
array('channel' => $pname['channel'], 'package' => $pname['package']),
|
||
|
true)
|
||
|
. $vs .
|
||
|
', latest release is version ' . $info['version'] .
|
||
|
', stability "' . $info['info']->getState() . '", use "' .
|
||
|
$this->_registry->parsedPackageNameToString(
|
||
|
array('channel' => $pname['channel'], 'package' => $pname['package'],
|
||
|
'version' => $info['version'])) . '" to install',
|
||
|
PEAR_DOWNLOADER_PACKAGE_STATE);
|
||
|
return $err;
|
||
|
}
|
||
|
$err = PEAR::raiseError(
|
||
|
'Failed to download ' . $this->_registry->parsedPackageNameToString(
|
||
|
array('channel' => $pname['channel'], 'package' => $pname['package']),
|
||
|
true)
|
||
|
. $vs .
|
||
|
', latest release is version ' . $info['version'] .
|
||
|
', stability "' . $info['info']->getState() . '", use "' .
|
||
|
$this->_registry->parsedPackageNameToString(
|
||
|
array('channel' => $pname['channel'], 'package' => $pname['package'],
|
||
|
'version' => $info['version'])) . '" to install');
|
||
|
return $err;
|
||
|
}
|
||
|
}
|
||
|
return $info;
|
||
|
}
|
||
|
}
|
||
|
?>
|