[ Index ]
 

Code source de SPIP Agora 1.4

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/Pear/PEAR/ -> Downloader.php (source)

   1  <?php
   2  //
   3  // +----------------------------------------------------------------------+
   4  // | PHP Version 5                                                        |
   5  // +----------------------------------------------------------------------+
   6  // | Copyright (c) 1997-2004 The PHP Group                                |
   7  // +----------------------------------------------------------------------+
   8  // | This source file is subject to version 3.0 of the PHP license,       |
   9  // | that is bundled with this package in the file LICENSE, and is        |
  10  // | available through the world-wide-web at the following url:           |
  11  // | http://www.php.net/license/3_0.txt.                                  |
  12  // | If you did not receive a copy of the PHP license and are unable to   |
  13  // | obtain it through the world-wide-web, please send a note to          |
  14  // | license@php.net so we can mail you a copy immediately.               |
  15  // +----------------------------------------------------------------------+
  16  // | Authors: Stig Bakken <ssb@php.net>                                   |
  17  // |          Tomas V.V.Cox <cox@idecnet.com>                             |
  18  // |          Martin Jansen <mj@php.net>                                  |
  19  // +----------------------------------------------------------------------+
  20  //
  21  // $Id: Downloader.php,v 1.17.2.1 2004/10/22 22:54:03 cellog Exp $
  22  
  23  require_once 'PEAR/Common.php';
  24  require_once 'PEAR/Registry.php';
  25  require_once 'PEAR/Dependency.php';
  26  require_once 'PEAR/Remote.php';
  27  require_once 'System.php';
  28  
  29  
  30  define('PEAR_INSTALLER_OK',       1);
  31  define('PEAR_INSTALLER_FAILED',   0);
  32  define('PEAR_INSTALLER_SKIPPED', -1);
  33  define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
  34  
  35  /**
  36   * Administration class used to download PEAR packages and maintain the
  37   * installed package database.
  38   *
  39   * @since PEAR 1.4
  40   * @author Greg Beaver <cellog@php.net>
  41   */
  42  class PEAR_Downloader extends PEAR_Common
  43  {
  44      /**
  45       * @var PEAR_Config
  46       * @access private
  47       */
  48      var $_config;
  49  
  50      /**
  51       * @var PEAR_Registry
  52       * @access private
  53       */
  54      var $_registry;
  55  
  56      /**
  57       * @var PEAR_Remote
  58       * @access private
  59       */
  60      var $_remote;
  61  
  62      /**
  63       * Preferred Installation State (snapshot, devel, alpha, beta, stable)
  64       * @var string|null
  65       * @access private
  66       */
  67      var $_preferredState;
  68  
  69      /**
  70       * Options from command-line passed to Install.
  71       *
  72       * Recognized options:<br />
  73       *  - onlyreqdeps   : install all required dependencies as well
  74       *  - alldeps       : install all dependencies, including optional
  75       *  - installroot   : base relative path to install files in
  76       *  - force         : force a download even if warnings would prevent it
  77       * @see PEAR_Command_Install
  78       * @access private
  79       * @var array
  80       */
  81      var $_options;
  82  
  83      /**
  84       * Downloaded Packages after a call to download().
  85       *
  86       * Format of each entry:
  87       *
  88       * <code>
  89       * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
  90       *    'info' => array() // parsed package.xml
  91       * );
  92       * </code>
  93       * @access private
  94       * @var array
  95       */
  96      var $_downloadedPackages = array();
  97  
  98      /**
  99       * Packages slated for download.
 100       *
 101       * This is used to prevent downloading a package more than once should it be a dependency
 102       * for two packages to be installed.
 103       * Format of each entry:
 104       *
 105       * <pre>
 106       * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
 107       * );
 108       * </pre>
 109       * @access private
 110       * @var array
 111       */
 112      var $_toDownload = array();
 113  
 114      /**
 115       * Array of every package installed, with names lower-cased.
 116       *
 117       * Format:
 118       * <code>
 119       * array('package1' => 0, 'package2' => 1, );
 120       * </code>
 121       * @var array
 122       */
 123      var $_installed = array();
 124  
 125      /**
 126       * @var array
 127       * @access private
 128       */
 129      var $_errorStack = array();
 130  
 131      // {{{ PEAR_Downloader()
 132  
 133      function PEAR_Downloader(&$ui, $options, &$config)
 134      {
 135          $this->_options = $options;
 136          $this->_config = &$config;
 137          $this->_preferredState = $this->_config->get('preferred_state');
 138          $this->ui = &$ui;
 139          if (!$this->_preferredState) {
 140              // don't inadvertantly use a non-set preferred_state
 141              $this->_preferredState = null;
 142          }
 143  
 144          $php_dir = $this->_config->get('php_dir');
 145          if (isset($this->_options['installroot'])) {
 146              if (substr($this->_options['installroot'], -1) == DIRECTORY_SEPARATOR) {
 147                  $this->_options['installroot'] = substr($this->_options['installroot'], 0, -1);
 148              }
 149              $php_dir = $this->_prependPath($php_dir, $this->_options['installroot']);
 150          }
 151          $this->_registry = &new PEAR_Registry($php_dir);
 152          $this->_remote = &new PEAR_Remote($config);
 153  
 154          if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
 155              $this->_installed = $this->_registry->listPackages();
 156              array_walk($this->_installed, create_function('&$v,$k','$v = strtolower($v);'));
 157              $this->_installed = array_flip($this->_installed);
 158          }
 159          parent::PEAR_Common();
 160      }
 161  
 162      // }}}
 163      // {{{ configSet()
 164      function configSet($key, $value, $layer = 'user')
 165      {
 166          $this->_config->set($key, $value, $layer);
 167          $this->_preferredState = $this->_config->get('preferred_state');
 168          if (!$this->_preferredState) {
 169              // don't inadvertantly use a non-set preferred_state
 170              $this->_preferredState = null;
 171          }
 172      }
 173  
 174      // }}}
 175      // {{{ setOptions()
 176      function setOptions($options)
 177      {
 178          $this->_options = $options;
 179      }
 180  
 181      // }}}
 182      // {{{ _downloadFile()
 183      /**
 184       * @param string filename to download
 185       * @param string version/state
 186       * @param string original value passed to command-line
 187       * @param string|null preferred state (snapshot/devel/alpha/beta/stable)
 188       *                    Defaults to configuration preferred state
 189       * @return null|PEAR_Error|string
 190       * @access private
 191       */
 192      function _downloadFile($pkgfile, $version, $origpkgfile, $state = null)
 193      {
 194          if (is_null($state)) {
 195              $state = $this->_preferredState;
 196          }
 197          // {{{ check the package filename, and whether it's already installed
 198          $need_download = false;
 199          if (preg_match('#^(http|ftp)://#', $pkgfile)) {
 200              $need_download = true;
 201          } elseif (!@is_file($pkgfile)) {
 202              if ($this->validPackageName($pkgfile)) {
 203                  if ($this->_registry->packageExists($pkgfile)) {
 204                      if (empty($this->_options['upgrade']) && empty($this->_options['force'])) {
 205                          $errors[] = "$pkgfile already installed";
 206                          return;
 207                      }
 208                  }
 209                  $pkgfile = $this->getPackageDownloadUrl($pkgfile, $version);
 210                  $need_download = true;
 211              } else {
 212                  if (strlen($pkgfile)) {
 213                      $errors[] = "Could not open the package file: $pkgfile";
 214                  } else {
 215                      $errors[] = "No package file given";
 216                  }
 217                  return;
 218              }
 219          }
 220          // }}}
 221  
 222          // {{{ Download package -----------------------------------------------
 223          if ($need_download) {
 224              $downloaddir = $this->_config->get('download_dir');
 225              if (empty($downloaddir)) {
 226                  if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
 227                      return $downloaddir;
 228                  }
 229                  $this->log(3, '+ tmp dir created at ' . $downloaddir);
 230              }
 231              $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
 232              $this->pushErrorHandling(PEAR_ERROR_RETURN);
 233              $file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
 234              $this->popErrorHandling();
 235              if (PEAR::isError($file)) {
 236                  if ($this->validPackageName($origpkgfile)) {
 237                      if (!PEAR::isError($info = $this->_remote->call('package.info',
 238                            $origpkgfile))) {
 239                          if (!count($info['releases'])) {
 240                              return $this->raiseError('Package ' . $origpkgfile .
 241                              ' has no releases');
 242                          } else {
 243                              return $this->raiseError('No releases of preferred state "'
 244                              . $state . '" exist for package ' . $origpkgfile .
 245                              '.  Use ' . $origpkgfile . '-state to install another' .
 246                              ' state (like ' . $origpkgfile .'-beta)',
 247                              PEAR_INSTALLER_ERROR_NO_PREF_STATE);
 248                          }
 249                      } else {
 250                          return $pkgfile;
 251                      }
 252                  } else {
 253                      return $this->raiseError($file);
 254                  }
 255              }
 256              $pkgfile = $file;
 257          }
 258          // }}}
 259          return $pkgfile;
 260      }
 261      // }}}
 262      // {{{ getPackageDownloadUrl()
 263  
 264      function getPackageDownloadUrl($package, $version = null)
 265      {
 266          if ($version) {
 267              $package .= "-$version";
 268          }
 269          if ($this === null || $this->_config === null) {
 270              $package = "http://pear.php.net/get/$package";
 271          } else {
 272              $package = "http://" . $this->_config->get('master_server') .
 273                  "/get/$package";
 274          }
 275          if (!extension_loaded("zlib")) {
 276              $package .= '?uncompress=yes';
 277          }
 278          return $package;
 279      }
 280  
 281      // }}}
 282      // {{{ extractDownloadFileName($pkgfile, &$version)
 283  
 284      function extractDownloadFileName($pkgfile, &$version)
 285      {
 286          if (@is_file($pkgfile)) {
 287              return $pkgfile;
 288          }
 289          // regex defined in Common.php
 290          if (preg_match(PEAR_COMMON_PACKAGE_DOWNLOAD_PREG, $pkgfile, $m)) {
 291              $version = (isset($m[3])) ? $m[3] : null;
 292              return $m[1];
 293          }
 294          $version = null;
 295          return $pkgfile;
 296      }
 297  
 298      // }}}
 299  
 300      // }}}
 301      // {{{ getDownloadedPackages()
 302  
 303      /**
 304       * Retrieve a list of downloaded packages after a call to {@link download()}.
 305       *
 306       * Also resets the list of downloaded packages.
 307       * @return array
 308       */
 309      function getDownloadedPackages()
 310      {
 311          $ret = $this->_downloadedPackages;
 312          $this->_downloadedPackages = array();
 313          $this->_toDownload = array();
 314          return $ret;
 315      }
 316  
 317      // }}}
 318      // {{{ download()
 319  
 320      /**
 321       * Download any files and their dependencies, if necessary
 322       *
 323       * BC-compatible method name
 324       * @param array a mixed list of package names, local files, or package.xml
 325       */
 326      function download($packages)
 327      {
 328          return $this->doDownload($packages);
 329      }
 330  
 331      // }}}
 332      // {{{ doDownload()
 333  
 334      /**
 335       * Download any files and their dependencies, if necessary
 336       *
 337       * @param array a mixed list of package names, local files, or package.xml
 338       */
 339      function doDownload($packages)
 340      {
 341          $mywillinstall = array();
 342          $state = $this->_preferredState;
 343  
 344          // {{{ download files in this list if necessary
 345          foreach($packages as $pkgfile) {
 346              $need_download = false;
 347              if (!is_file($pkgfile)) {
 348                  if (preg_match('#^(http|ftp)://#', $pkgfile)) {
 349                      $need_download = true;
 350                  }
 351                  $pkgfile = $this->_downloadNonFile($pkgfile);
 352                  if (PEAR::isError($pkgfile)) {
 353                      return $pkgfile;
 354                  }
 355                  if ($pkgfile === false) {
 356                      continue;
 357                  }
 358              } // end is_file()
 359  
 360              $tempinfo = $this->infoFromAny($pkgfile);
 361              if ($need_download) {
 362                  $this->_toDownload[] = $tempinfo['package'];
 363              }
 364              if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
 365                  // ignore dependencies if there are any errors
 366                  if (!PEAR::isError($tempinfo)) {
 367                      $mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps'];
 368                  }
 369              }
 370              $this->_downloadedPackages[] = array('pkg' => $tempinfo['package'],
 371                                         'file' => $pkgfile, 'info' => $tempinfo);
 372          } // end foreach($packages)
 373          // }}}
 374  
 375          // {{{ extract dependencies from downloaded files and then download
 376          // them if necessary
 377          if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
 378              $deppackages = array();
 379              foreach ($mywillinstall as $package => $alldeps) {
 380                  if (!is_array($alldeps)) {
 381                      // there are no dependencies
 382                      continue;
 383                  }
 384                  $fail = false;
 385                  foreach ($alldeps as $info) {
 386                      if ($info['type'] != 'pkg') {
 387                          continue;
 388                      }
 389                      $ret = $this->_processDependency($package, $info, $mywillinstall);
 390                      if ($ret === false) {
 391                          continue;
 392                      }
 393                      if ($ret === 0) {
 394                          $fail = true;
 395                          continue;
 396                      }
 397                      if (PEAR::isError($ret)) {
 398                          return $ret;
 399                      }
 400                      $deppackages[] = $ret;
 401                  } // foreach($alldeps
 402                  if ($fail) {
 403                      $deppackages = array();
 404                  }
 405              }
 406  
 407              if (count($deppackages)) {
 408                  $this->doDownload($deppackages);
 409              }
 410          } // }}} if --alldeps or --onlyreqdeps
 411      }
 412  
 413      // }}}
 414      // {{{ _downloadNonFile($pkgfile)
 415  
 416      /**
 417       * @return false|PEAR_Error|string false if loop should be broken out of,
 418       *                                 string if the file was downloaded,
 419       *                                 PEAR_Error on exception
 420       * @access private
 421       */
 422      function _downloadNonFile($pkgfile)
 423      {
 424          $origpkgfile = $pkgfile;
 425          $state = null;
 426          $pkgfile = $this->extractDownloadFileName($pkgfile, $version);
 427          if (preg_match('#^(http|ftp)://#', $pkgfile)) {
 428              return $this->_downloadFile($pkgfile, $version, $origpkgfile);
 429          }
 430          if (!$this->validPackageName($pkgfile)) {
 431              return $this->raiseError("Package name '$pkgfile' not valid");
 432          }
 433          // ignore packages that are installed unless we are upgrading
 434          $curinfo = $this->_registry->packageInfo($pkgfile);
 435          if ($this->_registry->packageExists($pkgfile)
 436                && empty($this->_options['upgrade']) && empty($this->_options['force'])) {
 437              $this->log(0, "Package '{$curinfo['package']}' already installed, skipping");
 438              return false;
 439          }
 440          if (in_array($pkgfile, $this->_toDownload)) {
 441              return false;
 442          }
 443          $releases = $this->_remote->call('package.info', $pkgfile, 'releases', true);
 444          if (!count($releases)) {
 445              return $this->raiseError("No releases found for package '$pkgfile'");
 446          }
 447          // Want a specific version/state
 448          if ($version !== null) {
 449              // Passed Foo-1.2
 450              if ($this->validPackageVersion($version)) {
 451                  if (!isset($releases[$version])) {
 452                      return $this->raiseError("No release with version '$version' found for '$pkgfile'");
 453                  }
 454              // Passed Foo-alpha
 455              } elseif (in_array($version, $this->getReleaseStates())) {
 456                  $state = $version;
 457                  $version = 0;
 458                  foreach ($releases as $ver => $inf) {
 459                      if ($inf['state'] == $state && version_compare("$version", "$ver") < 0) {
 460                          $version = $ver;
 461                          break;
 462                      }
 463                  }
 464                  if ($version == 0) {
 465                      return $this->raiseError("No release with state '$state' found for '$pkgfile'");
 466                  }
 467              // invalid suffix passed
 468              } else {
 469                  return $this->raiseError("Invalid suffix '-$version', be sure to pass a valid PEAR ".
 470                                           "version number or release state");
 471              }
 472          // Guess what to download
 473          } else {
 474              $states = $this->betterStates($this->_preferredState, true);
 475              $possible = false;
 476              $version = 0;
 477              $higher_version = 0;
 478              $prev_hi_ver = 0;
 479              foreach ($releases as $ver => $inf) {
 480                  if (in_array($inf['state'], $states) && version_compare("$version", "$ver") < 0) {
 481                      $version = $ver;
 482                      break;
 483                  } else {
 484                      $ver = (string)$ver;
 485                      if (version_compare($prev_hi_ver, $ver) < 0) {
 486                          $prev_hi_ver = $higher_version = $ver;
 487                      }
 488                  }
 489              }
 490              if ($version === 0 && !isset($this->_options['force'])) {
 491                  return $this->raiseError('No release with state equal to: \'' . implode(', ', $states) .
 492                                           "' found for '$pkgfile'");
 493              } elseif ($version === 0) {
 494                  $this->log(0, "Warning: $pkgfile is state '" . $releases[$higher_version]['state'] . "' which is less stable " .
 495                                "than state '$this->_preferredState'");
 496              }
 497          }
 498          // Check if we haven't already the version
 499          if (empty($this->_options['force']) && !is_null($curinfo)) {
 500              if ($curinfo['version'] == $version) {
 501                  $this->log(0, "Package '{$curinfo['package']}-{$curinfo['version']}' already installed, skipping");
 502                  return false;
 503              } elseif (version_compare("$version", "{$curinfo['version']}") < 0) {
 504                  $this->log(0, "Package '{$curinfo['package']}' version '{$curinfo['version']}' " .
 505                                " is installed and {$curinfo['version']} is > requested '$version', skipping");
 506                  return false;
 507              }
 508          }
 509          $this->_toDownload[] = $pkgfile;
 510          return $this->_downloadFile($pkgfile, $version, $origpkgfile, $state);
 511      }
 512  
 513      // }}}
 514      // {{{ _processDependency($package, $info, $mywillinstall)
 515  
 516      /**
 517       * Process a dependency, download if necessary
 518       * @param array dependency information from PEAR_Remote call
 519       * @param array packages that will be installed in this iteration
 520       * @return false|string|PEAR_Error
 521       * @access private
 522       * @todo Add test for relation 'lt'/'le' -> make sure that the dependency requested is
 523       *       in fact lower than the required value.  This will be very important for BC dependencies
 524       */
 525      function _processDependency($package, $info, $mywillinstall)
 526      {
 527          $state = $this->_preferredState;
 528          if (!isset($this->_options['alldeps']) && isset($info['optional']) &&
 529                $info['optional'] == 'yes') {
 530              // skip optional deps
 531              $this->log(0, "skipping Package '$package' optional dependency '$info[name]'");
 532              return false;
 533          }
 534          // {{{ get releases
 535          $releases = $this->_remote->call('package.info', $info['name'], 'releases', true);
 536          if (PEAR::isError($releases)) {
 537              return $releases;
 538          }
 539          if (!count($releases)) {
 540              if (!isset($this->_installed[strtolower($info['name'])])) {
 541                  $this->pushError("Package '$package' dependency '$info[name]' ".
 542                              "has no releases");
 543              }
 544              return false;
 545          }
 546          $found = false;
 547          $save = $releases;
 548          while(count($releases) && !$found) {
 549              if (!empty($state) && $state != 'any') {
 550                  list($release_version, $release) = each($releases);
 551                  if ($state != $release['state'] &&
 552                      !in_array($release['state'], $this->betterStates($state)))
 553                  {
 554                      // drop this release - it ain't stable enough
 555                      array_shift($releases);
 556                  } else {
 557                      $found = true;
 558                  }
 559              } else {
 560                  $found = true;
 561              }
 562          }
 563          if (!count($releases) && !$found) {
 564              $get = array();
 565              foreach($save as $release) {
 566                  $get = array_merge($get,
 567                      $this->betterStates($release['state'], true));
 568              }
 569              $savestate = array_shift($get);
 570              $this->pushError( "Release for '$package' dependency '$info[name]' " .
 571                  "has state '$savestate', requires '$state'");
 572              return 0;
 573          }
 574          if (in_array(strtolower($info['name']), $this->_toDownload) ||
 575                isset($mywillinstall[strtolower($info['name'])])) {
 576              // skip upgrade check for packages we will install
 577              return false;
 578          }
 579          if (!isset($this->_installed[strtolower($info['name'])])) {
 580              // check to see if we can install the specific version required
 581              if ($info['rel'] == 'eq') {
 582                  return $info['name'] . '-' . $info['version'];
 583              }
 584              // skip upgrade check for packages we don't have installed
 585              return $info['name'];
 586          }
 587          // }}}
 588  
 589          // {{{ see if a dependency must be upgraded
 590          $inst_version = $this->_registry->packageInfo($info['name'], 'version');
 591          if (!isset($info['version'])) {
 592              // this is a rel='has' dependency, check against latest
 593              if (version_compare($release_version, $inst_version, 'le')) {
 594                  return false;
 595              } else {
 596                  return $info['name'];
 597              }
 598          }
 599          if (version_compare($info['version'], $inst_version, 'le')) {
 600              // installed version is up-to-date
 601              return false;
 602          }
 603          return $info['name'];
 604      }
 605  
 606      // }}}
 607      // {{{ _downloadCallback()
 608  
 609      function _downloadCallback($msg, $params = null)
 610      {
 611          switch ($msg) {
 612              case 'saveas':
 613                  $this->log(1, "downloading $params ...");
 614                  break;
 615              case 'done':
 616                  $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
 617                  break;
 618              case 'bytesread':
 619                  static $bytes;
 620                  if (empty($bytes)) {
 621                      $bytes = 0;
 622                  }
 623                  if (!($bytes % 10240)) {
 624                      $this->log(1, '.', false);
 625                  }
 626                  $bytes += $params;
 627                  break;
 628              case 'start':
 629                  $this->log(1, "Starting to download {$params[0]} (".number_format($params[1], 0, '', ',')." bytes)");
 630                  break;
 631          }
 632          if (method_exists($this->ui, '_downloadCallback'))
 633              $this->ui->_downloadCallback($msg, $params);
 634      }
 635  
 636      // }}}
 637      // {{{ _prependPath($path, $prepend)
 638  
 639      function _prependPath($path, $prepend)
 640      {
 641          if (strlen($prepend) > 0) {
 642              if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
 643                  $path = $prepend . substr($path, 2);
 644              } else {
 645                  $path = $prepend . $path;
 646              }
 647          }
 648          return $path;
 649      }
 650      // }}}
 651      // {{{ pushError($errmsg, $code)
 652  
 653      /**
 654       * @param string
 655       * @param integer
 656       */
 657      function pushError($errmsg, $code = -1)
 658      {
 659          array_push($this->_errorStack, array($errmsg, $code));
 660      }
 661  
 662      // }}}
 663      // {{{ getErrorMsgs()
 664  
 665      function getErrorMsgs()
 666      {
 667          $msgs = array();
 668          $errs = $this->_errorStack;
 669          foreach ($errs as $err) {
 670              $msgs[] = $err[0];
 671          }
 672          $this->_errorStack = array();
 673          return $msgs;
 674      }
 675  
 676      // }}}
 677  }
 678  // }}}
 679  
 680  ?>


Généré le : Sat Feb 24 14:40:03 2007 par Balluche grâce à PHPXref 0.7