| [ PHPXref.com ] | [ Generated: Sun Jul 20 19:16:47 2008 ] | [ P4A 1.0.0 ] |
| [ Index ] [ Variables ] [ Functions ] [ Classes ] [ Constants ] [ Statistics ] | ||
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * PEAR_Downloader, the PEAR Installer's download utility class 4 * 5 * PHP versions 4 and 5 6 * 7 * LICENSE: This source file is subject to version 3.0 of the PHP license 8 * that is available through the world-wide-web at the following URI: 9 * http://www.php.net/license/3_0.txt. If you did not receive a copy of 10 * the PHP License and are unable to obtain it through the web, please 11 * send a note to license@php.net so we can mail you a copy immediately. 12 * 13 * @category pear 14 * @package PEAR 15 * @author Greg Beaver <cellog@php.net> 16 * @author Stig Bakken <ssb@php.net> 17 * @author Tomas V. V. Cox <cox@idecnet.com> 18 * @author Martin Jansen <mj@php.net> 19 * @copyright 1997-2006 The PHP Group 20 * @license http://www.php.net/license/3_0.txt PHP License 3.0 21 * @version CVS: $Id: Downloader.php,v 1.99 2006/03/02 18:14:13 cellog Exp $ 22 * @link http://pear.php.net/package/PEAR 23 * @since File available since Release 1.3.0 24 */ 25 26 /** 27 * Needed for constants, extending 28 */ 29 require_once 'PEAR/Common.php'; 30 31 define('PEAR_INSTALLER_OK', 1); 32 define('PEAR_INSTALLER_FAILED', 0); 33 define('PEAR_INSTALLER_SKIPPED', -1); 34 define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2); 35 36 /** 37 * Administration class used to download anything from the internet (PEAR Packages, 38 * static URLs, xml files) 39 * 40 * @category pear 41 * @package PEAR 42 * @author Greg Beaver <cellog@php.net> 43 * @author Stig Bakken <ssb@php.net> 44 * @author Tomas V. V. Cox <cox@idecnet.com> 45 * @author Martin Jansen <mj@php.net> 46 * @copyright 1997-2006 The PHP Group 47 * @license http://www.php.net/license/3_0.txt PHP License 3.0 48 * @version Release: 1.4.9 49 * @link http://pear.php.net/package/PEAR 50 * @since Class available since Release 1.3.0 51 */ 52 class PEAR_Downloader extends PEAR_Common 53 { 54 /** 55 * @var PEAR_Registry 56 * @access private 57 */ 58 var $_registry; 59 60 /** 61 * @var PEAR_Remote 62 * @access private 63 */ 64 var $_remote; 65 66 /** 67 * Preferred Installation State (snapshot, devel, alpha, beta, stable) 68 * @var string|null 69 * @access private 70 */ 71 var $_preferredState; 72 73 /** 74 * Options from command-line passed to Install. 75 * 76 * Recognized options:<br /> 77 * - onlyreqdeps : install all required dependencies as well 78 * - alldeps : install all dependencies, including optional 79 * - installroot : base relative path to install files in 80 * - force : force a download even if warnings would prevent it 81 * - nocompress : download uncompressed tarballs 82 * @see PEAR_Command_Install 83 * @access private 84 * @var array 85 */ 86 var $_options; 87 88 /** 89 * Downloaded Packages after a call to download(). 90 * 91 * Format of each entry: 92 * 93 * <code> 94 * array('pkg' => 'package_name', 'file' => '/path/to/local/file', 95 * 'info' => array() // parsed package.xml 96 * ); 97 * </code> 98 * @access private 99 * @var array 100 */ 101 var $_downloadedPackages = array(); 102 103 /** 104 * Packages slated for download. 105 * 106 * This is used to prevent downloading a package more than once should it be a dependency 107 * for two packages to be installed. 108 * Format of each entry: 109 * 110 * <pre> 111 * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml, 112 * ); 113 * </pre> 114 * @access private 115 * @var array 116 */ 117 var $_toDownload = array(); 118 119 /** 120 * Array of every package installed, with names lower-cased. 121 * 122 * Format: 123 * <code> 124 * array('package1' => 0, 'package2' => 1, ); 125 * </code> 126 * @var array 127 */ 128 var $_installed = array(); 129 130 /** 131 * @var array 132 * @access private 133 */ 134 var $_errorStack = array(); 135 136 /** 137 * @var boolean 138 * @access private 139 */ 140 var $_internalDownload = false; 141 142 /** 143 * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()} 144 * @var array 145 * @access private 146 */ 147 var $_packageSortTree; 148 149 /** 150 * Temporary directory, or configuration value where downloads will occur 151 * @var string 152 */ 153 var $_downloadDir; 154 // {{{ PEAR_Downloader() 155 156 /** 157 * @param PEAR_Frontend_* 158 * @param array 159 * @param PEAR_Config 160 */ 161 function PEAR_Downloader(&$ui, $options, &$config) 162 { 163 parent::PEAR_Common(); 164 $this->_options = $options; 165 $this->config = &$config; 166 $this->_preferredState = $this->config->get('preferred_state'); 167 $this->ui = &$ui; 168 if (!$this->_preferredState) { 169 // don't inadvertantly use a non-set preferred_state 170 $this->_preferredState = null; 171 } 172 173 if (isset($this->_options['installroot'])) { 174 $this->config->setInstallRoot($this->_options['installroot']); 175 } 176 $this->_registry = &$config->getRegistry(); 177 $this->_remote = &$config->getRemote(); 178 179 if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { 180 $this->_installed = $this->_registry->listAllPackages(); 181 foreach ($this->_installed as $key => $unused) { 182 if (!count($unused)) { 183 continue; 184 } 185 @array_walk($this->_installed[$key], 'strtolower'); 186 } 187 } 188 } 189 190 /** 191 * Attempt to discover a channel's remote capabilities from 192 * its server name 193 * @param string 194 * @return boolean 195 */ 196 function discover($channel) 197 { 198 $this->log(1, 'Attempting to discover channel "' . $channel . '"...'); 199 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 200 $callback = $this->ui ? array(&$this, '_downloadCallback') : null; 201 if (!class_exists('System')) { 202 require_once 'System.php'; 203 } 204 $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, 205 System::mktemp(array('-d')), $callback, false); 206 PEAR::popErrorHandling(); 207 if (PEAR::isError($a)) { 208 return false; 209 } 210 list($a, $lastmodified) = $a; 211 if (!class_exists('PEAR/ChannelFile.php')) { 212 require_once 'PEAR/ChannelFile.php'; 213 } 214 $b = new PEAR_ChannelFile; 215 if ($b->fromXmlFile($a)) { 216 @unlink($a); 217 if ($this->config->get('auto_discover')) { 218 $this->_registry->addChannel($b, $lastmodified); 219 $alias = $b->getName(); 220 if ($b->getName() == $this->_registry->channelName($b->getAlias())) { 221 $alias = $b->getAlias(); 222 } 223 $this->log(1, 'Auto-discovered channel "' . $channel . 224 '", alias "' . $alias . '", adding to registry'); 225 } 226 return true; 227 } 228 @unlink($a); 229 return false; 230 } 231 232 /** 233 * For simpler unit-testing 234 * @param PEAR_Downloader 235 * @return PEAR_Downloader_Package 236 */ 237 function &newDownloaderPackage(&$t) 238 { 239 if (!class_exists('PEAR_Downloader_Package')) { 240 require_once 'PEAR/Downloader/Package.php'; 241 } 242 $a = &new PEAR_Downloader_Package($t); 243 return $a; 244 } 245 246 /** 247 * For simpler unit-testing 248 * @param PEAR_Config 249 * @param array 250 * @param array 251 * @param int 252 */ 253 function &getDependency2Object(&$c, $i, $p, $s) 254 { 255 if (!class_exists('PEAR/Dependency2.php')) { 256 require_once 'PEAR/Dependency2.php'; 257 } 258 $z = &new PEAR_Dependency2($c, $i, $p, $s); 259 return $z; 260 } 261 262 function &download($params) 263 { 264 if (!count($params)) { 265 $a = array(); 266 return $a; 267 } 268 if (!isset($this->_registry)) { 269 $this->_registry = &$this->config->getRegistry(); 270 } 271 if (!isset($this->_remote)) { 272 $this->_remote = &$this->config->getRemote(); 273 } 274 $channelschecked = array(); 275 // convert all parameters into PEAR_Downloader_Package objects 276 foreach ($params as $i => $param) { 277 $params[$i] = &$this->newDownloaderPackage($this); 278 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 279 $err = $params[$i]->initialize($param); 280 PEAR::staticPopErrorHandling(); 281 if (!$err) { 282 // skip parameters that were missed by preferred_state 283 continue; 284 } 285 if (PEAR::isError($err)) { 286 if (!isset($this->_options['soft'])) { 287 $this->log(0, $err->getMessage()); 288 } 289 $params[$i] = false; 290 if (is_object($param)) { 291 $param = $param->getChannel() . '/' . $param->getPackage(); 292 } 293 $this->pushError('Package "' . $param . '" is not valid', 294 PEAR_INSTALLER_SKIPPED); 295 } else { 296 if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) && 297 !isset($this->_options['offline'])) { 298 $channelschecked[$params[$i]->getChannel()] = true; 299 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 300 if (!class_exists('System')) { 301 require_once 'System.php'; 302 } 303 $curchannel = &$this->_registry->getChannel($params[$i]->getChannel()); 304 if (PEAR::isError($curchannel)) { 305 PEAR::staticPopErrorHandling(); 306 return $this->raiseError($curchannel); 307 } 308 $a = $this->downloadHttp('http://' . $params[$i]->getChannel() . 309 '/channel.xml', $this->ui, 310 System::mktemp(array('-d')), null, $curchannel->lastModified()); 311 PEAR::staticPopErrorHandling(); 312 if (PEAR::isError($a) || !$a) { 313 continue; 314 } 315 $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' . 316 'updated its protocols, use "channel-update ' . $params[$i]->getChannel() . 317 '" to update'); 318 } 319 if ($params[$i] && !isset($this->_options['downloadonly'])) { 320 if (isset($this->_options['packagingroot'])) { 321 $checkdir = $this->_prependPath( 322 $this->config->get('php_dir', null, $params[$i]->getChannel()), 323 $this->_options['packagingroot']); 324 } else { 325 $checkdir = $this->config->get('php_dir', 326 null, $params[$i]->getChannel()); 327 } 328 while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) { 329 $checkdir = dirname($checkdir); 330 } 331 if ($checkdir == '.') { 332 $checkdir = '/'; 333 } 334 if (!@is_writeable($checkdir)) { 335 return PEAR::raiseError('Cannot install, php_dir for channel "' . 336 $params[$i]->getChannel() . '" is not writeable by the current user'); 337 } 338 } 339 } 340 } 341 unset($channelschecked); 342 PEAR_Downloader_Package::removeDuplicates($params); 343 if (!count($params)) { 344 $a = array(); 345 return $a; 346 } 347 if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) { 348 $reverify = true; 349 while ($reverify) { 350 $reverify = false; 351 foreach ($params as $i => $param) { 352 $ret = $params[$i]->detectDependencies($params); 353 if (PEAR::isError($ret)) { 354 $reverify = true; 355 $params[$i] = false; 356 PEAR_Downloader_Package::removeDuplicates($params); 357 if (!isset($this->_options['soft'])) { 358 $this->log(0, $ret->getMessage()); 359 } 360 continue 2; 361 } 362 } 363 } 364 } 365 if (isset($this->_options['offline'])) { 366 $this->log(3, 'Skipping dependency download check, --offline specified'); 367 } 368 if (!count($params)) { 369 $a = array(); 370 return $a; 371 } 372 while (PEAR_Downloader_Package::mergeDependencies($params)); 373 PEAR_Downloader_Package::removeInstalled($params); 374 if (!count($params)) { 375 $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); 376 $a = array(); 377 return $a; 378 } 379 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 380 $err = $this->analyzeDependencies($params); 381 PEAR::popErrorHandling(); 382 if (!count($params)) { 383 $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); 384 $a = array(); 385 return $a; 386 } 387 $ret = array(); 388 $newparams = array(); 389 if (isset($this->_options['pretend'])) { 390 return $params; 391 } 392 foreach ($params as $i => $package) { 393 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 394 $pf = &$params[$i]->download(); 395 PEAR::staticPopErrorHandling(); 396 if (PEAR::isError($pf)) { 397 if (!isset($this->_options['soft'])) { 398 $this->log(1, $pf->getMessage()); 399 $this->log(0, 'Error: cannot download "' . 400 $this->_registry->parsedPackageNameToString($package->getParsedPackage(), 401 true) . 402 '"'); 403 } 404 continue; 405 } 406 $newparams[] = &$params[$i]; 407 $ret[] = array('file' => $pf->getArchiveFile(), 408 'info' => &$pf, 409 'pkg' => $pf->getPackage()); 410 } 411 $this->_downloadedPackages = $ret; 412 return $newparams; 413 } 414 415 /** 416 * @param array all packages to be installed 417 */ 418 function analyzeDependencies(&$params) 419 { 420 $hasfailed = $failed = false; 421 if (isset($this->_options['downloadonly'])) { 422 return; 423 } 424 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 425 $redo = true; 426 $reset = false; 427 while ($redo) { 428 $redo = false; 429 foreach ($params as $i => $param) { 430 $deps = $param->getDeps(); 431 if (!$deps) { 432 $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), 433 $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); 434 if ($param->getType() == 'xmlrpc') { 435 $send = $param->getDownloadURL(); 436 } else { 437 $send = $param->getPackageFile(); 438 } 439 $installcheck = $depchecker->validatePackage($send, $this, $params); 440 if (PEAR::isError($installcheck)) { 441 if (!isset($this->_options['soft'])) { 442 $this->log(0, $installcheck->getMessage()); 443 } 444 $hasfailed = true; 445 $params[$i] = false; 446 $reset = true; 447 $redo = true; 448 $failed = false; 449 PEAR_Downloader_Package::removeDuplicates($params); 450 continue 2; 451 } 452 continue; 453 } 454 if (!$reset && $param->alreadyValidated()) { 455 continue; 456 } 457 if (count($deps)) { 458 $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), 459 $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); 460 if ($param->getType() == 'xmlrpc') { 461 $send = $param->getDownloadURL(); 462 } else { 463 $send = $param->getPackageFile(); 464 } 465 $installcheck = $depchecker->