Source for file Downloader.php
Documentation is available at Downloader.php
// +----------------------------------------------------------------------+
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available through the world-wide-web at the following url: |
// | 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 world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Stig Bakken <ssb@php.net> |
// | Tomas V.V.Cox <cox@idecnet.com> |
// | Martin Jansen <mj@php.net> |
// +----------------------------------------------------------------------+
// $Id: Downloader.php,v 1.17.2.1 2004/10/22 22:54:03 cellog Exp $
require_once 'PEAR/Common.php';
require_once 'PEAR/Registry.php';
require_once 'PEAR/Dependency.php';
require_once 'PEAR/Remote.php';
require_once 'System.php';
define('PEAR_INSTALLER_OK', 1 );
define('PEAR_INSTALLER_FAILED', 0 );
define('PEAR_INSTALLER_SKIPPED', -1 );
define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2 );
* Administration class used to download PEAR packages and maintain the
* installed package database.
* @author Greg Beaver <cellog@php.net>
class PEAR_Downloader extends PEAR_Common
* Preferred Installation State (snapshot, devel, alpha, beta, stable)
* Options from command-line passed to Install.
* Recognized options:<br />
* - onlyreqdeps : install all required dependencies as well
* - alldeps : install all dependencies, including optional
* - installroot : base relative path to install files in
* - force : force a download even if warnings would prevent it
* @see PEAR_Command_Install
* Downloaded Packages after a call to download().
* array('pkg' => 'package_name', 'file' => '/path/to/local/file',
* 'info' => array() // parsed package.xml
var $_downloadedPackages = array ();
* Packages slated for download.
* This is used to prevent downloading a package more than once should it be a dependency
* for two packages to be installed.
* array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
var $_toDownload = array ();
* Array of every package installed, with names lower-cased.
* array('package1' => 0, 'package2' => 1, );
var $_installed = array ();
var $_errorStack = array ();
function PEAR_Downloader (&$ui, $options, &$config)
$this->_options = $options;
$this->_config = &$config;
$this->_preferredState = $this->_config->get ('preferred_state');
if (!$this->_preferredState) {
// don't inadvertantly use a non-set preferred_state
$this->_preferredState = null;
$php_dir = $this->_config->get ('php_dir');
if (isset ($this->_options['installroot'])) {
if (substr($this->_options['installroot'], -1 ) == DIRECTORY_SEPARATOR ) {
$this->_options['installroot'] = substr($this->_options['installroot'], 0 , -1 );
$php_dir = $this->_prependPath ($php_dir, $this->_options['installroot']);
$this->_registry = &new PEAR_Registry ($php_dir);
$this->_remote = &new PEAR_Remote ($config);
if (isset ($this->_options['alldeps']) || isset ($this->_options['onlyreqdeps'])) {
$this->_installed = $this->_registry->listPackages ();
$this->_installed = array_flip($this->_installed);
function configSet ($key, $value, $layer = 'user')
$this->_config->set ($key, $value, $layer);
$this->_preferredState = $this->_config->get ('preferred_state');
if (!$this->_preferredState) {
// don't inadvertantly use a non-set preferred_state
$this->_preferredState = null;
function setOptions ($options)
$this->_options = $options;
* @param string filename to download
* @param string version/state
* @param string original value passed to command-line
* @param string|nullpreferred state (snapshot/devel/alpha/beta/stable)
* Defaults to configuration preferred state
* @return null|PEAR_Error|string
function _downloadFile ($pkgfile, $version, $origpkgfile, $state = null )
$state = $this->_preferredState;
// {{{ check the package filename, and whether it's already installed
if ($this->validPackageName ($pkgfile)) {
if ($this->_registry->packageExists ($pkgfile)) {
if (empty ($this->_options['upgrade']) && empty ($this->_options['force'])) {
$errors[] = " $pkgfile already installed";
$pkgfile = $this->getPackageDownloadUrl ($pkgfile, $version);
$errors[] = " Could not open the package file: $pkgfile";
$errors[] = "No package file given";
// {{{ Download package -----------------------------------------------
$downloaddir = $this->_config->get ('download_dir');
if (empty ($downloaddir)) {
$this->log (3 , '+ tmp dir created at ' . $downloaddir);
$callback = $this->ui ? array (&$this, '_downloadCallback') : null;
$file = $this->downloadHttp ($pkgfile, $this->ui, $downloaddir, $callback);
$this->popErrorHandling ();
if (PEAR ::isError ($file)) {
if ($this->validPackageName ($origpkgfile)) {
if (!PEAR ::isError ($info = $this->_remote->call ('package.info',
if (!count($info['releases'])) {
return $this->raiseError ('Package ' . $origpkgfile .
return $this->raiseError ('No releases of preferred state "'
. $state . '" exist for package ' . $origpkgfile .
'. Use ' . $origpkgfile . '-state to install another' .
' state (like ' . $origpkgfile . '-beta)',
return $this->raiseError ($file);
// {{{ getPackageDownloadUrl()
function getPackageDownloadUrl ($package, $version = null )
if ($this === null || $this->_config === null ) {
$package = " http://pear.php.net/get/$package";
$package = "http://" . $this->_config->get ('master_server') .
$package .= '?uncompress=yes';
// {{{ extractDownloadFileName($pkgfile, &$version)
function extractDownloadFileName ($pkgfile, &$version)
// regex defined in Common.php
$version = (isset ($m[3 ])) ? $m[3 ] : null;
// {{{ getDownloadedPackages()
* Retrieve a list of downloaded packages after a call to {@link download()}.
* Also resets the list of downloaded packages.
function getDownloadedPackages ()
$ret = $this->_downloadedPackages;
$this->_downloadedPackages = array ();
$this->_toDownload = array ();
* Download any files and their dependencies, if necessary
* BC-compatible method name
* @param array a mixed list of package names, local files, or package.xml
function download ($packages)
return $this->doDownload ($packages);
* Download any files and their dependencies, if necessary
* @param array a mixed list of package names, local files, or package.xml
function doDownload ($packages)
$mywillinstall = array ();
$state = $this->_preferredState;
// {{{ download files in this list if necessary
foreach($packages as $pkgfile) {
$pkgfile = $this->_downloadNonFile ($pkgfile);
if (PEAR ::isError ($pkgfile)) {
if ($pkgfile === false ) {
$tempinfo = $this->infoFromAny ($pkgfile);
$this->_toDownload[] = $tempinfo['package'];
if (isset ($this->_options['alldeps']) || isset ($this->_options['onlyreqdeps'])) {
// ignore dependencies if there are any errors
if (!PEAR ::isError ($tempinfo)) {
$mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps'];
$this->_downloadedPackages[] = array ('pkg' => $tempinfo['package'],
'file' => $pkgfile, 'info' => $tempinfo);
} // end foreach($packages)
// {{{ extract dependencies from downloaded files and then download
if (isset ($this->_options['alldeps']) || isset ($this->_options['onlyreqdeps'])) {
foreach ($mywillinstall as $package => $alldeps) {
// there are no dependencies
foreach ($alldeps as $info) {
if ($info['type'] != 'pkg') {
$ret = $this->_processDependency ($package, $info, $mywillinstall);
if (PEAR ::isError ($ret)) {
if (count($deppackages)) {
$this->doDownload ($deppackages);
} // }}} if --alldeps or --onlyreqdeps
// {{{ _downloadNonFile($pkgfile)
* @return false|PEAR_Error|stringfalse if loop should be broken out of,
* string if the file was downloaded,
* PEAR_Error on exception
function _downloadNonFile ($pkgfile)
$pkgfile = $this->extractDownloadFileName ($pkgfile, $version);
return $this->_downloadFile ($pkgfile, $version, $origpkgfile);
if (!$this->validPackageName ($pkgfile)) {
return $this->raiseError (" Package name '$pkgfile' not valid" );
// ignore packages that are installed unless we are upgrading
$curinfo = $this->_registry->packageInfo ($pkgfile);
if ($this->_registry->packageExists ($pkgfile)
&& empty ($this->_options['upgrade']) && empty ($this->_options['force'])) {
$this->log (0 , " Package '{$curinfo['package']}' already installed, skipping" );
if (in_array($pkgfile, $this->_toDownload)) {
$releases = $this->_remote->call ('package.info', $pkgfile, 'releases', true );
return $this->raiseError (" No releases found for package '$pkgfile'" );
// Want a specific version/state
if ($this->validPackageVersion ($version)) {
if (!isset ($releases[$version])) {
return $this->raiseError (" No release with version '$version' found for '$pkgfile'" );
} elseif (in_array($version, $this->getReleaseStates ())) {
foreach ($releases as $ver => $inf) {
return $this->raiseError (" No release with state '$state' found for '$pkgfile'" );
return $this->raiseError (" Invalid suffix '-$version', be sure to pass a valid PEAR ".
"version number or release state");
// Guess what to download
$states = $this->betterStates ($this->_preferredState, true );
foreach ($releases as $ver => $inf) {
$prev_hi_ver = $higher_version = $ver;
if ($version === 0 && !isset ($this->_options['force'])) {
return $this->raiseError ('No release with state equal to: \'' . implode(', ', $states) .
" ' found for '$pkgfile'" );
} elseif ($version === 0 ) {
$this->log (0 , " Warning: $pkgfile is state '" . $releases[$higher_version]['state'] . "' which is less stable " .
" than state '$this->_preferredState'" );
// Check if we haven't already the version
if (empty ($this->_options['force']) && !is_null($curinfo)) {
if ($curinfo['version'] == $version) {
$this->log (0 , " Package '{$curinfo['package']}-{$curinfo['version']}' already installed, skipping" );
$this->log (0 , " Package '{$curinfo['package']}' version '{$curinfo['version']}' " .
" is installed and {$curinfo['version']} is > requested '$version', skipping" );
$this->_toDownload[] = $pkgfile;
return $this->_downloadFile ($pkgfile, $version, $origpkgfile, $state);
// {{{ _processDependency($package, $info, $mywillinstall)
* Process a dependency, download if necessary
* @param array dependency information from PEAR_Remote call
* @param array packages that will be installed in this iteration
* @return false|string|PEAR_Error
* @todo Add test for relation 'lt'/'le' -> make sure that the dependency requested is
* in fact lower than the required value. This will be very important for BC dependencies
function _processDependency ($package, $info, $mywillinstall)
$state = $this->_preferredState;
if (!isset ($this->_options['alldeps']) && isset ($info['optional']) &&
$info['optional'] == 'yes') {
$this->log (0 , " skipping Package '$package' optional dependency '$info[name]'" );
$releases = $this->_remote->call ('package.info', $info['name'], 'releases', true );
if (PEAR ::isError ($releases)) {
if (!isset ($this->_installed[strtolower($info['name'])])) {
$this->pushError (" Package '$package' dependency '$info[name]' ".
while (count($releases) && !$found) {
if (!empty ($state) && $state != 'any') {
list ($release_version, $release) = each($releases);
if ($state != $release['state'] &&
!in_array($release['state'], $this->betterStates ($state)))
// drop this release - it ain't stable enough
if (!count($releases) && !$found) {
foreach($save as $release) {
$this->betterStates ($release['state'], true ));
$this->pushError ( " Release for '$package' dependency '$info[name]' " .
" has state '$savestate', requires '$state'" );
isset ($mywillinstall[strtolower($info['name'])])) {
// skip upgrade check for packages we will install
if (!isset ($this->_installed[strtolower($info['name'])])) {
// check to see if we can install the specific version required
if ($info['rel'] == 'eq') {
return $info['name'] . '-' . $info['version'];
// skip upgrade check for packages we don't have installed
// {{{ see if a dependency must be upgraded
$inst_version = $this->_registry->packageInfo ($info['name'], 'version');
if (!isset ($info['version'])) {
// this is a rel='has' dependency, check against latest
// installed version is up-to-date
// {{{ _downloadCallback()
function _downloadCallback ($msg, $params = null )
$this->log (1 , " downloading $params ..." );
$this->log (1 , '...done: ' . number_format($params, 0 , '', ',') . ' bytes');
$this->log (1 , '.', false );
$this->log (1 , " Starting to download {$params[0]} (". number_format($params[1 ], 0 , '', ','). " bytes)");
$this->ui->_downloadCallback ($msg, $params);
// {{{ _prependPath($path, $prepend)
function _prependPath ($path, $prepend)
$path = $prepend . substr($path, 2 );
$path = $prepend . $path;
// {{{ pushError($errmsg, $code)
function pushError ($errmsg, $code = -1 )
array_push($this->_errorStack, array ($errmsg, $code));
$errs = $this->_errorStack;
foreach ($errs as $err) {
$this->_errorStack = array ();
Documentation generated on Mon, 11 Mar 2019 14:23:57 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|