[ 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: Installer.php,v 1.150.2.2 2005/02/17 17:47:55 cellog Exp $ 22 23 require_once 'PEAR/Downloader.php'; 24 25 /** 26 * Administration class used to install PEAR packages and maintain the 27 * installed package database. 28 * 29 * TODO: 30 * - Check dependencies break on package uninstall (when no force given) 31 * - add a guessInstallDest() method with the code from _installFile() and 32 * use that method in Registry::_rebuildFileMap() & Command_Registry::doList(), 33 * others.. 34 * 35 * @since PHP 4.0.2 36 * @author Stig Bakken <ssb@php.net> 37 * @author Martin Jansen <mj@php.net> 38 * @author Greg Beaver <cellog@php.net> 39 */ 40 class PEAR_Installer extends PEAR_Downloader 41 { 42 // {{{ properties 43 44 /** name of the package directory, for example Foo-1.0 45 * @var string 46 */ 47 var $pkgdir; 48 49 /** directory where PHP code files go 50 * @var string 51 */ 52 var $phpdir; 53 54 /** directory where PHP extension files go 55 * @var string 56 */ 57 var $extdir; 58 59 /** directory where documentation goes 60 * @var string 61 */ 62 var $docdir; 63 64 /** installation root directory (ala PHP's INSTALL_ROOT or 65 * automake's DESTDIR 66 * @var string 67 */ 68 var $installroot = ''; 69 70 /** debug level 71 * @var int 72 */ 73 var $debug = 1; 74 75 /** temporary directory 76 * @var string 77 */ 78 var $tmpdir; 79 80 /** PEAR_Registry object used by the installer 81 * @var object 82 */ 83 var $registry; 84 85 /** List of file transactions queued for an install/upgrade/uninstall. 86 * 87 * Format: 88 * array( 89 * 0 => array("rename => array("from-file", "to-file")), 90 * 1 => array("delete" => array("file-to-delete")), 91 * ... 92 * ) 93 * 94 * @var array 95 */ 96 var $file_operations = array(); 97 98 // }}} 99 100 // {{{ constructor 101 102 /** 103 * PEAR_Installer constructor. 104 * 105 * @param object $ui user interface object (instance of PEAR_Frontend_*) 106 * 107 * @access public 108 */ 109 function PEAR_Installer(&$ui) 110 { 111 parent::PEAR_Common(); 112 $this->setFrontendObject($ui); 113 $this->debug = $this->config->get('verbose'); 114 //$this->registry = &new PEAR_Registry($this->config->get('php_dir')); 115 } 116 117 // }}} 118 119 // {{{ _deletePackageFiles() 120 121 /** 122 * Delete a package's installed files, does not remove empty directories. 123 * 124 * @param string $package package name 125 * 126 * @return bool TRUE on success, or a PEAR error on failure 127 * 128 * @access private 129 */ 130 function _deletePackageFiles($package) 131 { 132 if (!strlen($package)) { 133 return $this->raiseError("No package to uninstall given"); 134 } 135 $filelist = $this->registry->packageInfo($package, 'filelist'); 136 if ($filelist == null) { 137 return $this->raiseError("$package not installed"); 138 } 139 foreach ($filelist as $file => $props) { 140 if (empty($props['installed_as'])) { 141 continue; 142 } 143 $path = $this->_prependPath($props['installed_as'], $this->installroot); 144 $this->addFileOperation('delete', array($path)); 145 } 146 return true; 147 } 148 149 // }}} 150 // {{{ _installFile() 151 152 /** 153 * @param string filename 154 * @param array attributes from <file> tag in package.xml 155 * @param string path to install the file in 156 * @param array options from command-line 157 * @access private 158 */ 159 function _installFile($file, $atts, $tmp_path, $options) 160 { 161 // {{{ return if this file is meant for another platform 162 static $os; 163 if (isset($atts['platform'])) { 164 if (empty($os)) { 165 include_once "OS/Guess.php"; 166 $os = new OS_Guess(); 167 } 168 if (strlen($atts['platform']) && $atts['platform']{0} == '!') { 169 $negate = true; 170 $platform = substr($atts['platform'], 1); 171 } else { 172 $negate = false; 173 $platform = $atts['platform']; 174 } 175 if ((bool) $os->matchSignature($platform) === $negate) { 176 $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")"); 177 return PEAR_INSTALLER_SKIPPED; 178 } 179 } 180 // }}} 181 182 // {{{ assemble the destination paths 183 switch ($atts['role']) { 184 case 'doc': 185 case 'data': 186 case 'test': 187 $dest_dir = $this->config->get($atts['role'] . '_dir') . 188 DIRECTORY_SEPARATOR . $this->pkginfo['package']; 189 unset($atts['baseinstalldir']); 190 break; 191 case 'ext': 192 case 'php': 193 $dest_dir = $this->config->get($atts['role'] . '_dir'); 194 break; 195 case 'script': 196 $dest_dir = $this->config->get('bin_dir'); 197 break; 198 case 'src': 199 case 'extsrc': 200 $this->source_files++; 201 return; 202 default: 203 return $this->raiseError("Invalid role `$atts[role]' for file $file"); 204 } 205 $save_destdir = $dest_dir; 206 if (!empty($atts['baseinstalldir'])) { 207 $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; 208 } 209 if (dirname($file) != '.' && empty($atts['install-as'])) { 210 $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); 211 } 212 if (empty($atts['install-as'])) { 213 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); 214 } else { 215 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; 216 } 217 $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; 218 219 // Clean up the DIRECTORY_SEPARATOR mess 220 $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; 221 list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), 222 DIRECTORY_SEPARATOR, 223 array($dest_file, $orig_file)); 224 $installed_as = $dest_file; 225 $final_dest_file = $this->_prependPath($dest_file, $this->installroot); 226 $dest_dir = dirname($final_dest_file); 227 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); 228 // }}} 229 230 if (!@is_dir($dest_dir)) { 231 if (!$this->mkDirHier($dest_dir)) { 232 return $this->raiseError("failed to mkdir $dest_dir", 233 PEAR_INSTALLER_FAILED); 234 } 235 $this->log(3, "+ mkdir $dest_dir"); 236 } 237 if (empty($atts['replacements'])) { 238 if (!file_exists($orig_file)) { 239 return $this->raiseError("file does not exist", 240 PEAR_INSTALLER_FAILED); 241 } 242 if (!@copy($orig_file, $dest_file)) { 243 return $this->raiseError("failed to write $dest_file", 244 PEAR_INSTALLER_FAILED); 245 } 246 $this->log(3, "+ cp $orig_file $dest_file"); 247 if (isset($atts['md5sum'])) { 248 $md5sum = md5_file($dest_file); 249 } 250 } else { 251 // {{{ file with replacements 252 if (!file_exists($orig_file)) { 253 return $this->raiseError("file does not exist", 254 PEAR_INSTALLER_FAILED); 255 } 256 $fp = fopen($orig_file, "r"); 257 $contents = fread($fp, filesize($orig_file)); 258 fclose($fp); 259 if (isset($atts['md5sum'])) { 260 $md5sum = md5($contents); 261 } 262 $subst_from = $subst_to = array(); 263 foreach ($atts['replacements'] as $a) { 264 $to = ''; 265 if ($a['type'] == 'php-const') { 266 if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) { 267 eval("\$to = $a[to];"); 268 } else { 269 $this->log(0, "invalid php-const replacement: $a[to]"); 270 continue; 271 } 272 } elseif ($a['type'] == 'pear-config') { 273 $to = $this->config->get($a['to']); 274 if (is_null($to)) { 275 $this->log(0, "invalid pear-config replacement: $a[to]"); 276 continue; 277 } 278 } elseif ($a['type'] == 'package-info') { 279 if (isset($this->pkginfo[$a['to']]) && is_string($this->pkginfo[$a['to']])) { 280 $to = $this->pkginfo[$a['to']]; 281 } else { 282 $this->log(0, "invalid package-info replacement: $a[to]"); 283 continue; 284 } 285 } 286 if (!is_null($to)) { 287 $subst_from[] = $a['from']; 288 $subst_to[] = $to; 289 } 290 } 291 $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file"); 292 if (sizeof($subst_from)) { 293 $contents = str_replace($subst_from, $subst_to, $contents); 294 } 295 $wp = @fopen($dest_file, "wb"); 296 if (!is_resource($wp)) { 297 return $this->raiseError("failed to create $dest_file: $php_errormsg", 298 PEAR_INSTALLER_FAILED); 299 } 300 if (!fwrite($wp, $contents)) { 301 return $this->raiseError("failed writing to $dest_file: $php_errormsg", 302 PEAR_INSTALLER_FAILED); 303 } 304 fclose($wp); 305 // }}} 306 } 307 // {{{ check the md5 308 if (isset($md5sum)) { 309 if (strtolower($md5sum) == strtolower($atts['md5sum'])) { 310 $this->log(2, "md5sum ok: $final_dest_file"); 311 } else { 312 if (empty($options['force'])) { 313 // delete the file 314 @unlink($dest_file); 315 return $this->raiseError("bad md5sum for file $final_dest_file", 316 PEAR_INSTALLER_FAILED); 317 } else { 318 $this->log(0, "warning : bad md5sum for file $final_dest_file"); 319 } 320 } 321 } 322 // }}} 323 // {{{ set file permissions 324 if (!OS_WINDOWS) { 325 if ($atts['role'] == 'script') { 326 $mode = 0777 & ~(int)octdec($this->config->get('umask')); 327 $this->log(3, "+ chmod +x $dest_file"); 328 } else { 329 $mode = 0666 & ~(int)octdec($this->config->get('umask')); 330 } 331 $this->addFileOperation("chmod", array($mode, $dest_file)); 332 if (!@chmod($dest_file, $mode)) { 333 $this->log(0, "failed to change mode of $dest_file"); 334 } 335 } 336 // }}} 337 $this->addFileOperation("rename", array($dest_file, $final_dest_file)); 338 // Store the full path where the file was installed for easy unistall 339 $this->addFileOperation("installed_as", array($file, $installed_as, 340 $save_destdir, dirname(substr($dest_file, strlen($save_destdir))))); 341 342 //$this->log(2, "installed: $dest_file"); 343 return PEAR_INSTALLER_OK; 344 } 345 346 // }}} 347 // {{{ addFileOperation() 348 349 /** 350 * Add a file operation to the current file transaction. 351 * 352 * @see startFileTransaction() 353 * @var string $type This can be one of: 354 * - rename: rename a file ($data has 2 values) 355 * - chmod: change permissions on a file ($data has 2 values) 356 * - delete: delete a file ($data has 1 value) 357 * - rmdir: delete a directory if empty ($data has 1 value) 358 * - installed_as: mark a file as installed ($data has 4 values). 359 * @var array $data For all file operations, this array must contain the 360 * full path to the file or directory that is being operated on. For 361 * the rename command, the first parameter must be the file to rename, 362 * the second its new name. 363 * 364 * The installed_as operation contains 4 elements in this order: 365 * 1. Filename as listed in the filelist element from package.xml 366 * 2. Full path to the installed file 367 * 3. Full path from the php_dir configuration variable used in this 368 * installation 369 * 4. Relative path from the php_dir that this file is installed in 370 */ 371 function addFileOperation($type, $data) 372 { 373 if (!is_array($data)) { 374 return $this->raiseError('Internal Error: $data in addFileOperation' 375 . ' must be an array, was ' . gettype($data)); 376 } 377 if ($type == 'chmod') { 378 $octmode = decoct($data[0]); 379 $this->log(3, "adding to transaction: $type $octmode $data[1]"); 380 } else { 381 $this->log(3, "adding to transaction: $type " . implode(" ", $data)); 382 } 383 $this->file_operations[] = array($type, $data); 384 } 385 386 // }}} 387 // {{{ startFileTransaction() 388 389 function startFileTransaction($rollback_in_case = false) 390 { 391 if (count($this->file_operations) && $rollback_in_case) { 392 $this->rollbackFileTransaction(); 393 } 394 $this->file_operations = array(); 395 } 396 397 // }}} 398 // {{{ commitFileTransaction() 399 400 function commitFileTransaction() 401 { 402 $n = count($this->file_operations); 403 $this->log(2, "about to commit $n file operations"); 404 // {{{ first, check permissions and such manually 405 $errors = array(); 406 foreach ($this->file_operations as $tr) { 407 list($type, $data) = $tr; 408 switch ($type) { 409 case 'rename': 410 if (!file_exists($data[0])) { 411 $errors[] = "cannot rename file $data[0], doesn't exist"; 412 } 413 // check that dest dir. is writable 414 if (!is_writable(dirname($data[1]))) { 415 $errors[] = "permission denied ($type): $data[1]"; 416 } 417 break; 418 case 'chmod': 419 // check that file is writable 420 if (!is_writable($data[1])) { 421 $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]); 422 } 423 break; 424 case 'delete': 425 if (!file_exists($data[0])) { 426 $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted"); 427 } 428 // check that directory is writable 429 if (file_exists($data[0]) && !is_writable(dirname($data[0]))) { 430 $errors[] = "permission denied ($type): $data[0]"; 431 } 432 break; 433 } 434 435 } 436 // }}} 437 $m = sizeof($errors); 438 if ($m > 0) { 439 foreach ($errors as $error) { 440 $this->log(1, $error); 441 } 442 return false; 443 } 444 // {{{ really commit the transaction 445 foreach ($this->file_operations as $tr) { 446 list($type, $data) = $tr; 447 switch ($type) { 448 case 'rename': 449 @unlink($data[1]); 450 @rename($data[0], $data[1]); 451 $this->log(3, "+ mv $data[0] $data[1]"); 452 break; 453 case 'chmod': 454 @chmod($data[1], $data[0]); 455 $octmode = decoct($data[0]); 456 $this->log(3, "+ chmod $octmode $data[1]"); 457 break; 458 case 'delete': 459 @unlink($data[0]); 460 $this->log(3, "+ rm $data[0]"); 461 break; 462 case 'rmdir': 463 @rmdir($data[0]); 464 $this->log(3, "+ rmdir $data[0]"); 465 break; 466 case 'installed_as': 467 $this->pkginfo['filelist'][$data[0]]['installed_as'] = $data[1]; 468 if (!isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) { 469 $this->pkginfo['filelist']['dirtree'][dirname($data[1])] = true; 470 while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\' 471 && $data[3] != '.') { 472 $this->pkginfo['filelist']['dirtree'] 473 [$this->_prependPath($data[3], $data[2])] = true; 474 $data[3] = dirname($data[3]); 475 } 476 } 477 break; 478 } 479 } 480 // }}} 481 $this->log(2, "successfully committed $n file operations"); 482 $this->file_operations = array(); 483 return true; 484 } 485 486 // }}} 487 // {{{ rollbackFileTransaction() 488 489 function rollbackFileTransaction() 490 { 491 $n = count($this->file_operations); 492 $this->log(2, "rolling back $n file operations"); 493 foreach ($this->file_operations as $tr) { 494 list($type, $data) = $tr; 495 switch ($type) { 496 case 'rename': 497 @unlink($data[0]); 498 $this->log(3, "+ rm $data[0]"); 499 break; 500 case 'mkdir': 501 @rmdir($data[0]); 502 $this->log(3, "+ rmdir $data[0]"); 503 break; 504 case 'chmod': 505 break; 506 case 'delete': 507 break; 508 case 'installed_as': 509 if (isset($this->pkginfo['filelist'])) { 510 unset($this->pkginfo['filelist'][$data[0]]['installed_as']); 511 } 512 if (isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) { 513 unset($this->pkginfo['filelist']['dirtree'][dirname($data[1])]); 514 while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\' 515 && $data[3] != '.') { 516 unset($this->pkginfo['filelist']['dirtree'] 517 [$this->_prependPath($data[3], $data[2])]); 518 $data[3] = dirname($data[3]); 519 } 520 } 521 if (isset($this->pkginfo['filelist']['dirtree']) 522 && !count($this->pkginfo['filelist']['dirtree'])) { 523 unset($this->pkginfo['filelist']['dirtree']); 524 } 525 break; 526 } 527 } 528 $this->file_operations = array(); 529 } 530 531 // }}} 532 // {{{ mkDirHier($dir) 533 534 function mkDirHier($dir) 535 { 536 $this->addFileOperation('mkdir', array($dir)); 537 return parent::mkDirHier($dir); 538 } 539 540 // }}} 541 // {{{ download() 542 543 /** 544 * Download any files and their dependencies, if necessary 545 * 546 * @param array a mixed list of package names, local files, or package.xml 547 * @param PEAR_Config 548 * @param array options from the command line 549 * @param array this is the array that will be populated with packages to 550 * install. Format of each entry: 551 * 552 * <code> 553 * array('pkg' => 'package_name', 'file' => '/path/to/local/file', 554 * 'info' => array() // parsed package.xml 555 * ); 556 * </code> 557 * @param array this will be populated with any error messages 558 * @param false private recursion variable 559 * @param false private recursion variable 560 * @param false private recursion variable 561 * @deprecated in favor of PEAR_Downloader 562 */ 563 function download($packages, $options, &$config, &$installpackages, 564 &$errors, $installed = false, $willinstall = false, $state = false) 565 { 566 // trickiness: initialize here 567 parent::PEAR_Downloader($this->ui, $options, $config); 568 $ret = parent::download($packages); 569 $errors = $this->getErrorMsgs(); 570 $installpackages = $this->getDownloadedPackages(); 571 trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " . 572 "in favor of PEAR_Downloader class", E_USER_WARNING); 573 return $ret; 574 } 575 576 // }}} 577 // {{{ install() 578 579 /** 580 * Installs the files within the package file specified. 581 * 582 * @param string $pkgfile path to the package file 583 * @param array $options 584 * recognized options: 585 * - installroot : optional prefix directory for installation 586 * - force : force installation 587 * - register-only : update registry but don't install files 588 * - upgrade : upgrade existing install 589 * - soft : fail silently 590 * - nodeps : ignore dependency conflicts/missing dependencies 591 * - alldeps : install all dependencies 592 * - onlyreqdeps : install only required dependencies 593 * 594 * @return array|PEAR_Error package info if successful 595 */ 596 597 function install($pkgfile, $options = array()) 598 { 599 $php_dir = $this->config->get('php_dir'); 600 if (isset($options['installroot'])) { 601 if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) { 602 $options['installroot'] = substr($options['installroot'], 0, -1); 603 } 604 $php_dir = $this->_prependPath($php_dir, $options['installroot']); 605 $this->installroot = $options['installroot']; 606 } else { 607 $this->installroot = ''; 608 } 609 $this->registry = &new PEAR_Registry($php_dir); 610 // ==> XXX should be removed later on 611 $flag_old_format = false; 612 613 if (substr($pkgfile, -4) == '.xml') { 614 $descfile = $pkgfile; 615 } else { 616 // {{{ Decompress pack in tmp dir ------------------------------------- 617 618 // To allow relative package file names 619 $pkgfile = realpath($pkgfile); 620 621 if (PEAR::isError($tmpdir = System::mktemp('-d'))) { 622 return $tmpdir; 623 } 624 $this->log(3, '+ tmp dir created at ' . $tmpdir); 625 626 $tar = new Archive_Tar($pkgfile); 627 if (!@$tar->extract($tmpdir)) { 628 return $this->raiseError("unable to unpack $pkgfile"); 629 } 630 631 // {{{ Look for existing package file 632 $descfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml'; 633 634 if (!is_file($descfile)) { 635 // ----- Look for old package archive format 636 // In this format the package.xml file was inside the 637 // Package-n.n directory 638 $dp = opendir($tmpdir); 639 do { 640 $pkgdir = readdir($dp); 641 } while ($pkgdir{0} == '.'); 642 643 $descfile = $tmpdir . DIRECTORY_SEPARATOR . $pkgdir . DIRECTORY_SEPARATOR . 'package.xml'; 644 $flag_old_format = true; 645 $this->log(0, "warning : you are using an archive with an old format"); 646 } 647 // }}} 648 // <== XXX This part should be removed later on 649 // }}} 650 } 651 652 if (!is_file($descfile)) { 653 return $this->raiseError("no package.xml file after extracting the archive"); 654 } 655 656 // Parse xml file ----------------------------------------------- 657 $pkginfo = $this->infoFromDescriptionFile($descfile); 658 if (PEAR::isError($pkginfo)) { 659 return $pkginfo; 660 } 661 $this->validatePackageInfo($pkginfo, $errors, $warnings); 662 // XXX We allow warnings, do we have to do it? 663 if (count($errors)) { 664 if (empty($options['force'])) { 665 return $this->raiseError("The following errors where found (use force option to install anyway):\n". 666 implode("\n", $errors)); 667 } else { 668 $this->log(0, "warning : the following errors were found:\n". 669 implode("\n", $errors)); 670 } 671 } 672 673 $pkgname = $pkginfo['package']; 674 675 // {{{ Check dependencies ------------------------------------------- 676 if (isset($pkginfo['release_deps']) && empty($options['nodeps'])) { 677 $dep_errors = ''; 678 $error = $this->checkDeps($pkginfo, $dep_errors); 679 if ($error == true) { 680 if (empty($options['soft'])) { 681 $this->log(0, substr($dep_errors, 1)); 682 } 683 return $this->raiseError("$pkgname: Dependencies failed"); 684 } else if (!empty($dep_errors)) { 685 // Print optional dependencies 686 if (empty($options['soft'])) { 687 $this->log(0, $dep_errors); 688 } 689 } 690 } 691 // }}} 692 693 // {{{ checks to do when not in "force" mode 694 if (empty($options['force'])) { 695 $test = $this->registry->checkFileMap($pkginfo); 696 if (sizeof($test)) { 697 $tmp = $test; 698 foreach ($tmp as $file => $pkg) { 699 if ($pkg == $pkgname) { 700 unset($test[$file]); 701 } 702 } 703 if (sizeof($test)) { 704 $msg = "$pkgname: conflicting files found:\n"; 705 $longest = max(array_map("strlen", array_keys($test))); 706 $fmt = "%$longest}s (%s)\n"; 707 foreach ($test as $file => $pkg) { 708 $msg .= sprintf($fmt, $file, $pkg); 709 } 710 return $this->raiseError($msg); 711 } 712 } 713 } 714 // }}} 715 716 $this->startFileTransaction(); 717 718 if (empty($options['upgrade'])) { 719 // checks to do only when installing new packages 720 if (empty($options['force']) && $this->registry->packageExists($pkgname)) { 721 return $this->raiseError("$pkgname already installed"); 722 } 723 } else { 724 if ($this->registry->packageExists($pkgname)) { 725 $v1 = $this->registry->packageInfo($pkgname, 'version'); 726 $v2 = $pkginfo['version']; 727 $cmp = version_compare("$v1", "$v2", 'gt'); 728 if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) { 729 return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)"); 730 } 731 if (empty($options['register-only'])) { 732 // when upgrading, remove old release's files first: 733 if (PEAR::isError($err = $this->_deletePackageFiles($pkgname))) { 734 return $this->raiseError($err); 735 } 736 } 737 } 738 } 739 740 // {{{ Copy files to dest dir --------------------------------------- 741 742 // info from the package it self we want to access from _installFile 743 $this->pkginfo = &$pkginfo; 744 // used to determine whether we should build any C code 745 $this->source_files = 0; 746 747 if (empty($options['register-only'])) { 748 if (!is_dir($php_dir)) { 749 return $this->raiseError("no script destination directory\n", 750 null, PEAR_ERROR_DIE); 751 } 752 753 $tmp_path = dirname($descfile); 754 if (substr($pkgfile, -4) != '.xml') { 755 $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkginfo['version']; 756 } 757 758 // ==> XXX This part should be removed later on 759 if ($flag_old_format) { 760 $tmp_path = dirname($descfile); 761 } 762 // <== XXX This part should be removed later on 763 764 // {{{ install files 765 foreach ($pkginfo['filelist'] as $file => $atts) { 766 $this->expectError(PEAR_INSTALLER_FAILED); 767 $res = $this->_installFile($file, $atts, $tmp_path, $options); 768 $this->popExpect(); 769 if (PEAR::isError($res)) { 770 if (empty($options['ignore-errors'])) { 771 $this->rollbackFileTransaction(); 772 if ($res->getMessage() == "file does not exist") { 773 $this->raiseError("file $file in package.xml does not exist"); 774 } 775 return $this->raiseError($res); 776 } else { 777 $this->log(0, "Warning: " . $res->getMessage()); 778 } 779 } 780 if ($res != PEAR_INSTALLER_OK) { 781 // Do not register files that were not installed 782 unset($pkginfo['filelist'][$file]); 783 } 784 } 785 // }}} 786 787 // {{{ compile and install source files 788 if ($this->source_files > 0 && empty($options['nobuild'])) { 789 $this->log(1, "$this->source_files source files, building"); 790 $bob = &new PEAR_Builder($this->ui); 791 $bob->debug = $this->debug; 792 $built = $bob->build($descfile, array(&$this, '_buildCallback')); 793 if (PEAR::isError($built)) { 794 $this->rollbackFileTransaction(); 795 return $built; 796 } 797 $this->log(1, "\nBuild process completed successfully"); 798 foreach ($built as $ext) { 799 $bn = basename($ext['file']); 800 list($_ext_name, $_ext_suff) = explode('.', $bn); 801 if ($_ext_suff == '.so' || $_ext_suff == '.dll') { 802 if (extension_loaded($_ext_name)) { 803 $this->raiseError("Extension '$_ext_name' already loaded. " . 804 'Please unload it in your php.ini file ' . 805 'prior to install or upgrade'); 806 } 807 $role = 'ext'; 808 } else { 809 $role = 'src'; 810 } 811 $dest = $ext['dest']; 812 $this->log(1, "Installing '$ext[file]'"); 813 $copyto = $this->_prependPath($dest, $this->installroot); 814 $copydir = dirname($copyto); 815 if (!@is_dir($copydir)) { 816 if (!$this->mkDirHier($copydir)) { 817 return $this->raiseError("failed to mkdir $copydir", 818 PEAR_INSTALLER_FAILED); 819 } 820 $this->log(3, "+ mkdir $copydir"); 821 } 822 if (!@copy($ext['file'], $copyto)) { 823 return $this->raiseError("failed to write $copyto", PEAR_INSTALLER_FAILED); 824 } 825 $this->log(3, "+ cp $ext[file] $copyto"); 826 if (!OS_WINDOWS) { 827 $mode = 0666 & ~(int)octdec($this->config->get('umask')); 828 $this->addFileOperation('chmod', array($mode, $copyto)); 829 if (!@chmod($copyto, $mode)) { 830 $this->log(0, "failed to change mode of $copyto"); 831 } 832 } 833 $this->addFileOperation('rename', array($ext['file'], $copyto)); 834 835 $pkginfo['filelist'][$bn] = array( 836 'role' => $role, 837 'installed_as' => $dest, 838 'php_api' => $ext['php_api'], 839 'zend_mod_api' => $ext['zend_mod_api'], 840 'zend_ext_api' => $ext['zend_ext_api'], 841 ); 842 } 843 } 844 // }}} 845 } 846 847 if (!$this->commitFileTransaction()) { 848 $this->rollbackFileTransaction(); 849 return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED); 850 } 851 // }}} 852 853 $ret = false; 854 // {{{ Register that the package is installed ----------------------- 855 if (empty($options['upgrade'])) { 856 // if 'force' is used, replace the info in registry 857 if (!empty($options['force']) && $this->registry->packageExists($pkgname)) { 858 $this->registry->deletePackage($pkgname); 859 } 860 $ret = $this->registry->addPackage($pkgname, $pkginfo); 861 } else { 862 // new: upgrade installs a package if it isn't installed 863 if (!$this->registry->packageExists($pkgname)) { 864 $ret = $this->registry->addPackage($pkgname, $pkginfo); 865 } else { 866 $ret = $this->registry->updatePackage($pkgname, $pkginfo, false); 867 } 868 } 869 if (!$ret) { 870 return $this->raiseError("Adding package $pkgname to registry failed"); 871 } 872 // }}} 873 return $pkginfo; 874 } 875 876 // }}} 877 // {{{ uninstall() 878 879 /** 880 * Uninstall a package 881 * 882 * This method removes all files installed by the application, and then 883 * removes any empty directories. 884 * @param string package name 885 * @param array Command-line options. Possibilities include: 886 * 887 * - installroot: base installation dir, if not the default 888 * - nodeps: do not process dependencies of other packages to ensure 889 * uninstallation does not break things 890 */ 891 function uninstall($package, $options = array()) 892 { 893 $php_dir = $this->config->get('php_dir'); 894 if (isset($options['installroot'])) { 895 if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) { 896 $options['installroot'] = substr($options['installroot'], 0, -1); 897 } 898 $this->installroot = $options['installroot']; 899 $php_dir = $this->_prependPath($php_dir, $this->installroot); 900 } else { 901 $this->installroot = ''; 902 } 903 $this->registry = &new PEAR_Registry($php_dir); 904 $filelist = $this->registry->packageInfo($package, 'filelist'); 905 if ($filelist == null) { 906 return $this->raiseError("$package not installed"); 907 } 908 if (empty($options['nodeps'])) { 909 $depchecker = &new PEAR_Dependency($this->registry); 910 $error = $depchecker->checkPackageUninstall($errors, $warning, $package); 911 if ($error) { 912 return $this->raiseError($errors . 'uninstall failed'); 913 } 914 if ($warning) { 915 $this->log(0, $warning); 916 } 917 } 918 // {{{ Delete the files 919 $this->startFileTransaction(); 920 if (PEAR::isError($err = $this->_deletePackageFiles($package))) { 921 $this->rollbackFileTransaction(); 922 return $this->raiseError($err); 923 } 924 if (!$this->commitFileTransaction()) { 925 $this->rollbackFileTransaction(); 926 return $this->raiseError("uninstall failed"); 927 } else { 928 $this->startFileTransaction(); 929 if (!isset($filelist['dirtree']) || !count($filelist['dirtree'])) { 930 return $this->registry->deletePackage($package); 931 } 932 // attempt to delete empty directories 933 uksort($filelist['dirtree'], array($this, '_sortDirs')); 934 foreach($filelist['dirtree'] as $dir => $notused) { 935 $this->addFileOperation('rmdir', array($dir)); 936 } 937 if (!$this->commitFileTransaction()) { 938 $this->rollbackFileTransaction(); 939 } 940 } 941 // }}} 942 943 // Register that the package is no longer installed 944 return $this->registry->deletePackage($package); 945 } 946 947 // }}} 948 // {{{ _sortDirs() 949 function _sortDirs($a, $b) 950 { 951 if (strnatcmp($a, $b) == -1) return 1; 952 if (strnatcmp($a, $b) == 1) return -1; 953 return 0; 954 } 955 956 // }}} 957 // {{{ checkDeps() 958 959 /** 960 * Check if the package meets all dependencies 961 * 962 * @param array Package information (passed by reference) 963 * @param string Error message (passed by reference) 964 * @return boolean False when no error occured, otherwise true 965 */ 966 function checkDeps(&$pkginfo, &$errors) 967 { 968 if (empty($this->registry)) { 969 $this->registry = &new PEAR_Registry($this->config->get('php_dir')); 970 } 971 $depchecker = &new PEAR_Dependency($this->registry); 972 $error = $errors = ''; 973 $failed_deps = $optional_deps = array(); 974 if (is_array($pkginfo['release_deps'])) { 975 foreach($pkginfo['release_deps'] as $dep) { 976 $code = $depchecker->callCheckMethod($error, $dep); 977 if ($code) { 978 if (isset($dep['optional']) && $dep['optional'] == 'yes') { 979 $optional_deps[] = array($dep, $code, $error); 980 } else { 981 $failed_deps[] = array($dep, $code, $error); 982 } 983 } 984 } 985 // {{{ failed dependencies 986 $n = count($failed_deps); 987 if ($n > 0) { 988 for ($i = 0; $i < $n; $i++) { 989 if (isset($failed_deps[$i]['type'])) { 990 $type = $failed_deps[$i]['type']; 991 } else { 992 $type = 'pkg'; 993 } 994 switch ($failed_deps[$i][1]) { 995 case PEAR_DEPENDENCY_MISSING: 996 if ($type == 'pkg') { 997 // install 998 } 999 $errors .= "\n" . $failed_deps[$i][2]; 1000 break; 1001 case PEAR_DEPENDENCY_UPGRADE_MINOR: 1002 if ($type == 'pkg') { 1003 // upgrade 1004 } 1005 $errors .= "\n" . $failed_deps[$i][2]; 1006 break; 1007 default: 1008 $errors .= "\n" . $failed_deps[$i][2]; 1009 break; 1010 } 1011 } 1012 return true; 1013 } 1014 // }}} 1015 1016 // {{{ optional dependencies 1017 $count_optional = count($optional_deps); 1018 if ($count_optional > 0) { 1019 $errors = "Optional dependencies:"; 1020 1021 for ($i = 0; $i < $count_optional; $i++) { 1022 if (isset($optional_deps[$i]['type'])) { 1023 $type = $optional_deps[$i]['type']; 1024 } else { 1025 $type = 'pkg'; 1026 } 1027 switch ($optional_deps[$i][1]) { 1028 case PEAR_DEPENDENCY_MISSING: 1029 case PEAR_DEPENDENCY_UPGRADE_MINOR: 1030 default: 1031 $errors .= "\n" . $optional_deps[$i][2]; 1032 break; 1033 } 1034 } 1035 return false; 1036 } 1037 // }}} 1038 } 1039 return false; 1040 } 1041 1042 // }}} 1043 // {{{ _buildCallback() 1044 1045 function _buildCallback($what, $data) 1046 { 1047 if (($what == 'cmdoutput' && $this->debug > 1) || 1048 ($what == 'output' && $this->debug > 0)) { 1049 $this->ui->outputData(rtrim($data), 'build'); 1050 } 1051 } 1052 1053 // }}} 1054 } 1055 1056 // {{{ md5_file() utility function 1057 if (!function_exists("md5_file")) { 1058 function md5_file($filename) { 1059 $fp = fopen($filename, "r"); 1060 if (!$fp) return null; 1061 $contents = fread($fp, filesize($filename)); 1062 fclose($fp); 1063 return md5($contents); 1064 } 1065 } 1066 // }}} 1067 1068 ?>
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 |