[ Index ] |
|
Code source de SPIP Agora 1.4 |
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 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sat Feb 24 14:40:03 2007 | par Balluche grâce à PHPXref 0.7 |