| [ Index ] |
|
Code source de GeekLog 1.4.1 |
1 <?php 2 /** 3 * PEAR_Installer 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 Stig Bakken <ssb@php.net> 16 * @author Tomas V.V. Cox <cox@idecnet.com> 17 * @author Martin Jansen <mj@php.net> 18 * @author Greg Beaver <cellog@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: Installer.php,v 1.228.2.1 2006/03/05 20:07:52 cellog Exp $ 22 * @link http://pear.php.net/package/PEAR 23 * @since File available since Release 0.1 24 */ 25 26 /** 27 * Used for installation groups in package.xml 2.0 and platform exceptions 28 */ 29 require_once 'OS/Guess.php'; 30 require_once 'PEAR/Downloader.php'; 31 32 define('PEAR_INSTALLER_NOBINARY', -240); 33 /** 34 * Administration class used to install PEAR packages and maintain the 35 * installed package database. 36 * 37 * @category pear 38 * @package PEAR 39 * @author Stig Bakken <ssb@php.net> 40 * @author Tomas V.V. Cox <cox@idecnet.com> 41 * @author Martin Jansen <mj@php.net> 42 * @author Greg Beaver <cellog@php.net> 43 * @copyright 1997-2006 The PHP Group 44 * @license http://www.php.net/license/3_0.txt PHP License 3.0 45 * @version Release: 1.4.11 46 * @link http://pear.php.net/package/PEAR 47 * @since Class available since Release 0.1 48 */ 49 class PEAR_Installer extends PEAR_Downloader 50 { 51 // {{{ properties 52 53 /** name of the package directory, for example Foo-1.0 54 * @var string 55 */ 56 var $pkgdir; 57 58 /** directory where PHP code files go 59 * @var string 60 */ 61 var $phpdir; 62 63 /** directory where PHP extension files go 64 * @var string 65 */ 66 var $extdir; 67 68 /** directory where documentation goes 69 * @var string 70 */ 71 var $docdir; 72 73 /** installation root directory (ala PHP's INSTALL_ROOT or 74 * automake's DESTDIR 75 * @var string 76 */ 77 var $installroot = ''; 78 79 /** debug level 80 * @var int 81 */ 82 var $debug = 1; 83 84 /** temporary directory 85 * @var string 86 */ 87 var $tmpdir; 88 89 /** 90 * PEAR_Registry object used by the installer 91 * @var PEAR_Registry 92 */ 93 var $registry; 94 95 /** 96 * array of PEAR_Downloader_Packages 97 * @var array 98 */ 99 var $_downloadedPackages; 100 101 /** List of file transactions queued for an install/upgrade/uninstall. 102 * 103 * Format: 104 * array( 105 * 0 => array("rename => array("from-file", "to-file")), 106 * 1 => array("delete" => array("file-to-delete")), 107 * ... 108 * ) 109 * 110 * @var array 111 */ 112 var $file_operations = array(); 113 114 // }}} 115 116 // {{{ constructor 117 118 /** 119 * PEAR_Installer constructor. 120 * 121 * @param object $ui user interface object (instance of PEAR_Frontend_*) 122 * 123 * @access public 124 */ 125 function PEAR_Installer(&$ui) 126 { 127 parent::PEAR_Common(); 128 $this->setFrontendObject($ui); 129 $this->debug = $this->config->get('verbose'); 130 } 131 132 function setOptions($options) 133 { 134 $this->_options = $options; 135 } 136 137 function setConfig(&$config) 138 { 139 $this->config = &$config; 140 $this->_registry = &$config->getRegistry(); 141 } 142 143 // }}} 144 145 function _removeBackups($files) 146 { 147 foreach ($files as $path) { 148 $this->addFileOperation('removebackup', array($path)); 149 } 150 } 151 152 // {{{ _deletePackageFiles() 153 154 /** 155 * Delete a package's installed files, does not remove empty directories. 156 * 157 * @param string package name 158 * @param string channel name 159 * @param bool if true, then files are backed up first 160 * @return bool TRUE on success, or a PEAR error on failure 161 * @access protected 162 */ 163 function _deletePackageFiles($package, $channel = false, $backup = false) 164 { 165 if (!$channel) { 166 $channel = 'pear.php.net'; 167 } 168 if (!strlen($package)) { 169 return $this->raiseError("No package to uninstall given"); 170 } 171 if (strtolower($package) == 'pear' && $channel == 'pear.php.net') { 172 // to avoid race conditions, include all possible needed files 173 require_once 'PEAR/Task/Common.php'; 174 require_once 'PEAR/Task/Replace.php'; 175 require_once 'PEAR/Task/Unixeol.php'; 176 require_once 'PEAR/Task/Windowseol.php'; 177 require_once 'PEAR/PackageFile/v1.php'; 178 require_once 'PEAR/PackageFile/v2.php'; 179 require_once 'PEAR/PackageFile/Generator/v1.php'; 180 require_once 'PEAR/PackageFile/Generator/v2.php'; 181 } 182 $filelist = $this->_registry->packageInfo($package, 'filelist', $channel); 183 if ($filelist == null) { 184 return $this->raiseError("$channel/$package not installed"); 185 } 186 $ret = array(); 187 foreach ($filelist as $file => $props) { 188 if (empty($props['installed_as'])) { 189 continue; 190 } 191 $path = $props['installed_as']; 192 if ($backup) { 193 $this->addFileOperation('backup', array($path)); 194 $ret[] = $path; 195 } 196 $this->addFileOperation('delete', array($path)); 197 } 198 if ($backup) { 199 return $ret; 200 } 201 return true; 202 } 203 204 // }}} 205 // {{{ _installFile() 206 207 /** 208 * @param string filename 209 * @param array attributes from <file> tag in package.xml 210 * @param string path to install the file in 211 * @param array options from command-line 212 * @access private 213 */ 214 function _installFile($file, $atts, $tmp_path, $options) 215 { 216 // {{{ return if this file is meant for another platform 217 static $os; 218 if (!isset($this->_registry)) { 219 $this->_registry = &$this->config->getRegistry(); 220 } 221 if (isset($atts['platform'])) { 222 if (empty($os)) { 223 $os = new OS_Guess(); 224 } 225 if (strlen($atts['platform']) && $atts['platform']{0} == '!') { 226 $negate = true; 227 $platform = substr($atts['platform'], 1); 228 } else { 229 $negate = false; 230 $platform = $atts['platform']; 231 } 232 if ((bool) $os->matchSignature($platform) === $negate) { 233 $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")"); 234 return PEAR_INSTALLER_SKIPPED; 235 } 236 } 237 // }}} 238 239 $channel = $this->pkginfo->getChannel(); 240 // {{{ assemble the destination paths 241 switch ($atts['role']) { 242 case 'doc': 243 case 'data': 244 case 'test': 245 $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) . 246 DIRECTORY_SEPARATOR . $this->pkginfo->getPackage(); 247 unset($atts['baseinstalldir']); 248 break; 249 case 'ext': 250 case 'php': 251 $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel); 252 break; 253 case 'script': 254 $dest_dir = $this->config->get('bin_dir', null, $channel); 255 break; 256 case 'src': 257 case 'extsrc': 258 $this->source_files++; 259 return; 260 default: 261 return $this->raiseError("Invalid role `$atts[role]' for file $file"); 262 } 263 $save_destdir = $dest_dir; 264 if (!empty($atts['baseinstalldir'])) { 265 $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; 266 } 267 if (dirname($file) != '.' && empty($atts['install-as'])) { 268 $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); 269 } 270 if (empty($atts['install-as'])) { 271 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); 272 } else { 273 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; 274 } 275 $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; 276 277 // Clean up the DIRECTORY_SEPARATOR mess 278 $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; 279 list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), 280 array(DIRECTORY_SEPARATOR, 281 DIRECTORY_SEPARATOR, 282 DIRECTORY_SEPARATOR), 283 array($dest_file, $orig_file)); 284 $final_dest_file = $installed_as = $dest_file; 285 if (isset($this->_options['packagingroot'])) { 286 $installedas_dest_dir = dirname($final_dest_file); 287 $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); 288 $final_dest_file = $this->_prependPath($final_dest_file, 289 $this->_options['packagingroot']); 290 } else { 291 $installedas_dest_dir = dirname($final_dest_file); 292 $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); 293 } 294 $dest_dir = dirname($final_dest_file); 295 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); 296 // }}} 297 298 if (empty($this->_options['register-only']) && !@is_dir($dest_dir)) { 299 if (!$this->mkDirHier($dest_dir)) { 300 return $this->raiseError("failed to mkdir $dest_dir", 301 PEAR_INSTALLER_FAILED); 302 } 303 $this->log(3, "+ mkdir $dest_dir"); 304 } 305 // pretty much nothing happens if we are only registering the install 306 if (empty($this->_options['register-only'])) { 307 if (empty($atts['replacements'])) { 308 if (!file_exists($orig_file)) { 309 return $this->raiseError("file $orig_file does not exist", 310 PEAR_INSTALLER_FAILED); 311 } 312 if (!@copy($orig_file, $dest_file)) { 313 return $this->raiseError("failed to write $dest_file", 314 PEAR_INSTALLER_FAILED); 315 } 316 $this->log(3, "+ cp $orig_file $dest_file"); 317 if (isset($atts['md5sum'])) { 318 $md5sum = md5_file($dest_file); 319 } 320 } else { 321 // {{{ file with replacements 322 if (!file_exists($orig_file)) { 323 return $this->raiseError("file does not exist", 324 PEAR_INSTALLER_FAILED); 325 } 326 if (function_exists('file_get_contents')) { 327 $contents = file_get_contents($orig_file); 328 } else { 329 $fp = fopen($orig_file, "r"); 330 $contents = @fread($fp, filesize($orig_file)); 331 fclose($fp); 332 } 333 if ($contents === false) { 334 $contents = ''; 335 } 336 if (isset($atts['md5sum'])) { 337 $md5sum = md5($contents); 338 } 339 $subst_from = $subst_to = array(); 340 foreach ($atts['replacements'] as $a) { 341 $to = ''; 342 if ($a['type'] == 'php-const') { 343 if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) { 344 eval("\$to = $a[to];"); 345 } else { 346 if (!isset($options['soft'])) { 347 $this->log(0, "invalid php-const replacement: $a[to]"); 348 } 349 continue; 350 } 351 } elseif ($a['type'] == 'pear-config') { 352 if ($a['to'] == 'master_server') { 353 $chan = $this->_registry->getChannel($channel); 354 if (!PEAR::isError($chan)) { 355 $to = $chan->getServer(); 356 } else { 357 $to = $this->config->get($a['to'], null, $channel); 358 } 359 } else { 360 $to = $this->config->get($a['to'], null, $channel); 361 } 362 if (is_null($to)) { 363 if (!isset($options['soft'])) { 364 $this->log(0, "invalid pear-config replacement: $a[to]"); 365 } 366 continue; 367 } 368 } elseif ($a['type'] == 'package-info') { 369 if ($t = $this->pkginfo->packageInfo($a['to'])) { 370 $to = $t; 371 } else { 372 if (!isset($options['soft'])) { 373 $this->log(0, "invalid package-info replacement: $a[to]"); 374 } 375 continue; 376 } 377 } 378 if (!is_null($to)) { 379 $subst_from[] = $a['from']; 380 $subst_to[] = $to; 381 } 382 } 383 $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file"); 384 if (sizeof($subst_from)) { 385 $contents = str_replace($subst_from, $subst_to, $contents); 386 } 387 $wp = @fopen($dest_file, "wb"); 388 if (!is_resource($wp)) { 389 return $this->raiseError("failed to create $dest_file: $php_errormsg", 390 PEAR_INSTALLER_FAILED); 391 } 392 if (fwrite($wp, $contents) === false) { 393 return $this->raiseError("failed writing to $dest_file: $php_errormsg", 394 PEAR_INSTALLER_FAILED); 395 } 396 fclose($wp); 397 // }}} 398 } 399 // {{{ check the md5 400 if (isset($md5sum)) { 401 if (strtolower($md5sum) == strtolower($atts['md5sum'])) { 402 $this->log(2, "md5sum ok: $final_dest_file"); 403 } else { 404 if (empty($options['force'])) { 405 // delete the file 406 @unlink($dest_file); 407 if (!isset($options['ignore-errors'])) { 408 return $this->raiseError("bad md5sum for file $final_dest_file", 409 PEAR_INSTALLER_FAILED); 410 } else { 411 if (!isset($options['soft'])) { 412 $this->log(0, "warning : bad md5sum for file $final_dest_file"); 413 } 414 } 415 } else { 416 if (!isset($options['soft'])) { 417 $this->log(0, "warning : bad md5sum for file $final_dest_file"); 418 } 419 } 420 } 421 } 422 // }}} 423 // {{{ set file permissions 424 if (!OS_WINDOWS) { 425 if ($atts['role'] == 'script') { 426 $mode = 0777 & ~(int)octdec($this->config->get('umask')); 427 $this->log(3, "+ chmod +x $dest_file"); 428 } else { 429 $mode = 0666 & ~(int)octdec($this->config->get('umask')); 430 } 431 $this->addFileOperation("chmod", array($mode, $dest_file)); 432 if (!@chmod($dest_file, $mode)) { 433 if (!isset($options['soft'])) { 434 $this->log(0, "failed to change mode of $dest_file"); 435 } 436 } 437 } 438 // }}} 439 $this->addFileOperation("rename", array($dest_file, $final_dest_file, 440 $atts['role'] == 'ext')); 441 } 442 // Store the full path where the file was installed for easy unistall 443 $this->addFileOperation("installed_as", array($file, $installed_as, 444 $save_destdir, dirname(substr($installedas_dest_file, strlen($save_destdir))))); 445 446 //$this->log(2, "installed: $dest_file"); 447 return PEAR_INSTALLER_OK; 448 } 449 450 // }}} 451 // {{{ _installFile2() 452 453 /** 454 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 455 * @param string filename 456 * @param array attributes from <file> tag in package.xml 457 * @param string path to install the file in 458 * @param array options from command-line 459 * @access private 460 */ 461 function _installFile2(&$pkg, $file, $atts, $tmp_path, $options) 462 { 463 if (!isset($this->_registry)) { 464 $this->_registry = &$this->config->getRegistry(); 465 } 466 467 $channel = $pkg->getChannel(); 468 // {{{ assemble the destination paths 469 if (!in_array($atts['attribs']['role'], 470 PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) { 471 return $this->raiseError('Invalid role `' . $atts['attribs']['role'] . 472 "' for file $file"); 473 } 474 $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config); 475 $err = $role->setup($this, $pkg, $atts['attribs'], $file); 476 if (PEAR::isError($err)) { 477 return $err; 478 } 479 if (!$role->isInstallable()) { 480 return; 481 } 482 $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path); 483 if (PEAR::isError($info)) { 484 return $info; 485 } else { 486 list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info; 487 } 488 $final_dest_file = $installed_as = $dest_file; 489 if (isset($this->_options['packagingroot'])) { 490 $final_dest_file = $this->_prependPath($final_dest_file, 491 $this->_options['packagingroot']); 492 } 493 $dest_dir = dirname($final_dest_file); 494 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); 495 // }}} 496 497 if (empty($this->_options['register-only'])) { 498 if (!@is_dir($dest_dir)) { 499 if (!$this->mkDirHier($dest_dir)) { 500 return $this->raiseError("failed to mkdir $dest_dir", 501 PEAR_INSTALLER_FAILED); 502 } 503 $this->log(3, "+ mkdir $dest_dir"); 504 } 505 } 506 $attribs = $atts['attribs']; 507 unset($atts['attribs']); 508 // pretty much nothing happens if we are only registering the install 509 if (empty($this->_options['register-only'])) { 510 if (!count($atts)) { // no tasks 511 if (!file_exists($orig_file)) { 512 return $this->raiseError("file $orig_file does not exist", 513 PEAR_INSTALLER_FAILED); 514 } 515 if (!@copy($orig_file, $dest_file)) { 516 return $this->raiseError("failed to write $dest_file", 517 PEAR_INSTALLER_FAILED); 518 } 519 $this->log(3, "+ cp $orig_file $dest_file"); 520 if (isset($attribs['md5sum'])) { 521 $md5sum = md5_file($dest_file); 522 } 523 } else { // file with tasks 524 if (!file_exists($orig_file)) { 525 return $this->raiseError("file $orig_file does not exist", 526 PEAR_INSTALLER_FAILED); 527 } 528 if (function_exists('file_get_contents')) { 529 $contents = file_get_contents($orig_file); 530 } else { 531 $fp = fopen($orig_file, "r"); 532 $contents = @fread($fp, filesize($orig_file)); // filesize can be 0 533 fclose($fp); 534 } 535 if ($contents === false) { 536 $contents = ''; 537 } 538 if (isset($attribs['md5sum'])) { 539 $md5sum = md5($contents); 540 } 541 foreach ($atts as $tag => $raw) { 542 $tag = str_replace($pkg->getTasksNs() . ':', '', $tag); 543 $task = "PEAR_Task_$tag"; 544 $task = &new $task($this->config, $this, PEAR_TASK_INSTALL); 545 if (!$task->isScript()) { // scripts are only handled after installation 546 $task->init($raw, $attribs, $pkg->getLastInstalledVersion()); 547 $res = $task->startSession($pkg, $contents, $final_dest_file); 548 if ($res === false) { 549 continue; // skip this file 550 } 551 if (PEAR::isError($res)) { 552 return $res; 553 } 554 $contents = $res; // save changes 555 } 556 $wp = @fopen($dest_file, "wb"); 557 if (!is_resource($wp)) { 558 return $this->raiseError("failed to create $dest_file: $php_errormsg", 559 PEAR_INSTALLER_FAILED); 560 } 561 if (fwrite($wp, $contents) === false) { 562 return $this->raiseError("failed writing to $dest_file: $php_errormsg", 563 PEAR_INSTALLER_FAILED); 564 } 565 fclose($wp); 566 } 567 } 568 // {{{ check the md5 569 if (isset($md5sum)) { 570 if (strtolower($md5sum) == strtolower($attribs['md5sum'])) { 571 $this->log(2, "md5sum ok: $final_dest_file"); 572 } else { 573 if (empty($options['force'])) { 574 // delete the file 575 @unlink($dest_file); 576 if (!isset($options['ignore-errors'])) { 577 return $this->raiseError("bad md5sum for file $final_dest_file", 578 PEAR_INSTALLER_FAILED); 579 } else { 580 if (!isset($options['soft'])) { 581 $this->log(0, "warning : bad md5sum for file $final_dest_file"); 582 } 583 } 584 } else { 585 if (!isset($options['soft'])) { 586 $this->log(0, "warning : bad md5sum for file $final_dest_file"); 587 } 588 } 589 } 590 } 591 // }}} 592 // {{{ set file permissions 593 if (!OS_WINDOWS) { 594 if ($role->isExecutable()) { 595 $mode = 0777 & ~(int)octdec($this->config->get('umask')); 596 $this->log(3, "+ chmod +x $dest_file"); 597 } else { 598 $mode = 0666 & ~(int)octdec($this->config->get('umask')); 599 } 600 $this->addFileOperation("chmod", array($mode, $dest_file)); 601 if (!@chmod($dest_file, $mode)) { 602 if (!isset($options['soft'])) { 603 $this->log(0, "failed to change mode of $dest_file"); 604 } 605 } 606 } 607 // }}} 608 $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension())); 609 } 610 // Store the full path where the file was installed for easy uninstall 611 $this->addFileOperation("installed_as", array($file, $installed_as, 612 $save_destdir, dirname(substr($dest_file, strlen($save_destdir))))); 613 614 //$this->log(2, "installed: $dest_file"); 615 return PEAR_INSTALLER_OK; 616 } 617 618 // }}} 619 // {{{ addFileOperation() 620 621 /** 622 * Add a file operation to the current file transaction. 623 * 624 * @see startFileTransaction() 625 * @param string $type This can be one of: 626 * - rename: rename a file ($data has 3 values) 627 * - backup: backup an existing file ($data has 1 value) 628 * - removebackup: clean up backups created during install ($data has 1 value) 629 * - chmod: change permissions on a file ($data has 2 values) 630 * - delete: delete a file ($data has 1 value) 631 * - rmdir: delete a directory if empty ($data has 1 value) 632 * - installed_as: mark a file as installed ($data has 4 values). 633 * @param array $data For all file operations, this array must contain the 634 * full path to the file or directory that is being operated on. For 635 * the rename command, the first parameter must be the file to rename, 636 * the second its new name, the third whether this is a PHP extension. 637 * 638 * The installed_as operation contains 4 elements in this order: 639 * 1. Filename as listed in the filelist element from package.xml 640 * 2. Full path to the installed file 641 * 3. Full path from the php_dir configuration variable used in this 642 * installation 643 * 4. Relative path from the php_dir that this file is installed in 644 */ 645 function addFileOperation($type, $data) 646 { 647 if (!is_array($data)) { 648 return $this->raiseError('Internal Error: $data in addFileOperation' 649 . ' must be an array, was ' . gettype($data)); 650 } 651 if ($type == 'chmod') { 652 $octmode = decoct($data[0]); 653 $this->log(3, "adding to transaction: $type $octmode $data[1]"); 654 } else { 655 $this->log(3, "adding to transaction: $type " . implode(" ", $data)); 656 } 657 $this->file_operations[] = array($type, $data); 658 } 659 660 // }}} 661 // {{{ startFileTransaction() 662 663 function startFileTransaction($rollback_in_case = false) 664 { 665 if (count($this->file_operations) && $rollback_in_case) { 666 $this->rollbackFileTransaction(); 667 } 668 $this->file_operations = array(); 669 } 670 671 // }}} 672 // {{{ commitFileTransaction() 673 674 function commitFileTransaction() 675 { 676 $n = count($this->file_operations); 677 $this->log(2, "about to commit $n file operations"); 678 // {{{ first, check permissions and such manually 679 $errors = array(); 680 foreach ($this->file_operations as $tr) { 681 list($type, $data) = $tr; 682 switch ($type) { 683 case 'rename': 684 if (!file_exists($data[0])) { 685 $errors[] = "cannot rename file $data[0], doesn't exist"; 686 } 687 // check that dest dir. is writable 688 if (!is_writable(dirname($data[1]))) { 689 $errors[] = "permission denied ($type): $data[1]"; 690 } 691 break; 692 case 'chmod': 693 // check that file is writable 694 if (!is_writable($data[1])) { 695 $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]); 696 } 697 break; 698 case 'delete': 699 if (!file_exists($data[0])) { 700 $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted"); 701 } 702 // check that directory is writable 703 if (file_exists($data[0])) { 704 if (!is_writable(dirname($data[0]))) { 705 $errors[] = "permission denied ($type): $data[0]"; 706 } else { 707 // make sure the file to be deleted can be opened for writing 708 $fp = false; 709 if (!is_dir($data[0]) && !($fp = @fopen($data[0], 'a'))) { 710 $errors[] = "permission denied ($type): $data[0]"; 711 } elseif ($fp) { 712 fclose($fp); 713 } 714 } 715 } 716 break; 717 } 718 719 } 720 // }}} 721 $m = sizeof($errors); 722 if ($m > 0) { 723 foreach ($errors as $error) { 724 if (!isset($this->_options['soft'])) { 725 $this->log(1, $error); 726 } 727 } 728 if (!isset($this->_options['ignore-errors'])) { 729 return false; 730 } 731 } 732 $this->_dirtree = array(); 733 // {{{ really commit the transaction 734 foreach ($this->file_operations as $tr) { 735 list($type, $data) = $tr; 736 switch ($type) { 737 case 'backup': 738 @copy($data[0], $data[0] . '.bak'); 739 $this->log(3, "+ backup $data[0] to $data[0].bak"); 740 break; 741 case 'removebackup': 742 @unlink($data[0] . '.bak'); 743 $this->log(3, "+ rm backup of $data[0] ($data[0].bak)"); 744 break; 745 case 'rename': 746 $test = @unlink($data[1]); 747 if (!$test && file_exists($data[1])) { 748 if ($data[2]) { 749 $extra = ', this extension must be installed manually. Rename to "' . 750 basename($data[1]) . '"'; 751 } else { 752 $extra = ''; 753 } 754 if (!isset($this->_options['soft'])) { 755 $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' . 756 $data[0] . $extra); 757 } 758 if (!isset($this->_options['ignore-errors'])) { 759 return false; 760 } 761 } 762 @rename($data[0], $data[1]); 763 $this->log(3, "+ mv $data[0] $data[1]"); 764 break; 765 case 'chmod': 766 @chmod($data[1], $data[0]); 767 $octmode = decoct($data[0]); 768 $this->log(3, "+ chmod $octmode $data[1]"); 769 break; 770 case 'delete': 771 @unlink($data[0]); 772 $this->log(3, "+ rm $data[0]"); 773 break; 774 case 'rmdir': 775 @rmdir($data[0]); 776 $this->log(3, "+ rmdir $data[0]"); 777 break; 778 case 'installed_as': 779 $this->pkginfo->setInstalledAs($data[0], $data[1]); 780 if (!isset($this->_dirtree[dirname($data[1])])) { 781 $this->_dirtree[dirname($data[1])] = true; 782 $this->pkginfo->setDirtree(dirname($data[1])); 783 784 while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\' 785 && $data[3] != '.') { 786 $this->pkginfo->setDirtree($pp = 787 $this->_prependPath($data[3], $data[2])); 788 $this->_dirtree[$pp] = true; 789 $data[3] = dirname($data[3]); 790 } 791 } 792 break; 793 } 794 } 795 // }}} 796 $this->log(2, "successfully committed $n file operations"); 797 $this->file_operations = array(); 798 return true; 799 } 800 801 // }}} 802 // {{{ rollbackFileTransaction() 803 804 function rollbackFileTransaction() 805 { 806 $n = count($this->file_operations); 807 $this->log(2, "rolling back $n file operations"); 808 foreach ($this->file_operations as $tr) { 809 list($type, $data) = $tr; 810 switch ($type) { 811 case 'backup': 812 if (file_exists($data[0] . '.bak')) { 813 @unlink($data[0]); 814 @copy($data[0] . '.bak', $data[0]); 815 $this->log(3, "+ restore $data[0] from $data[0].bak"); 816 } 817 break; 818 case 'rename': 819 @unlink($data[0]); 820 $this->log(3, "+ rm $data[0]"); 821 break; 822 case 'mkdir': 823 @rmdir($data[0]); 824 $this->log(3, "+ rmdir $data[0]"); 825 break; 826 case 'chmod': 827 break; 828 case 'delete': 829 break; 830 case 'installed_as': 831 $this->pkginfo->setInstalledAs($data[0], false); 832 break; 833 } 834 } 835 $this->pkginfo->resetDirtree(); 836 $this->file_operations = array(); 837 } 838 839 // }}} 840 // {{{ mkDirHier($dir) 841 842 function mkDirHier($dir) 843 { 844 $this->addFileOperation('mkdir', array($dir)); 845 return parent::mkDirHier($dir); 846 } 847 848 // }}} 849 // {{{ download() 850 851 /** 852 * Download any files and their dependencies, if necessary 853 * 854 * @param array a mixed list of package names, local files, or package.xml 855 * @param PEAR_Config 856 * @param array options from the command line 857 * @param array this is the array that will be populated with packages to 858 * install. Format of each entry: 859 * 860 * <code> 861 * array('pkg' => 'package_name', 'file' => '/path/to/local/file', 862 * 'info' => array() // parsed package.xml 863 * ); 864 * </code> 865 * @param array this will be populated with any error messages 866 * @param false private recursion variable 867 * @param false private recursion variable 868 * @param false private recursion variable 869 * @deprecated in favor of PEAR_Downloader 870 */ 871 function download($packages, $options, &$config, &$installpackages, 872 &$errors, $installed = false, $willinstall = false, $state = false) 873 { 874 // trickiness: initialize here 875 parent::PEAR_Downloader($this->ui, $options, $config); 876 $ret = parent::download($packages); 877 $errors = $this->getErrorMsgs(); 878 $installpackages = $this->getDownloadedPackages(); 879 trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " . 880 "in favor of PEAR_Downloader class", E_USER_WARNING); 881 return $ret; 882 } 883 884 // }}} 885 // {{{ _parsePackageXml() 886 887 function _parsePackageXml(&$descfile, &$tmpdir) 888 { 889 if (substr($descfile, -4) == '.xml') { 890 $tmpdir = false; 891 } else { 892 // {{{ Decompress pack in tmp dir ------------------------------------- 893 894 // To allow relative package file names 895 $descfile = realpath($descfile); 896 897 if (PEAR::isError($tmpdir = System::mktemp('-d'))) { 898 return $tmpdir; 899 } 900 $this->log(3, '+ tmp dir created at ' . $tmpdir); 901 // }}} 902 } 903 // Parse xml file ----------------------------------------------- 904 $pkg = new PEAR_PackageFile($this->config, $this->debug, $tmpdir); 905 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 906 $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING); 907 PEAR::staticPopErrorHandling(); 908 if (PEAR::isError($p)) { 909 if (is_array($p->getUserInfo())) { 910 foreach ($p->getUserInfo() as $err) { 911 $loglevel = $err['level'] == 'error' ? 0 : 1; 912 if (!isset($this->_options['soft'])) { 913 $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']); 914 } 915 } 916 } 917 return $this->raiseError('Installation failed: invalid package file'); 918 } else { 919 $descfile = $p->getPackageFile(); 920 } 921 return $p; 922 } 923 924 // }}} 925 /** 926 * Set the list of PEAR_Downloader_Package objects to allow more sane 927 * dependency validation 928 * @param array 929 */ 930 function setDownloadedPackages(&$pkgs) 931 { 932 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 933 $err = $this->analyzeDependencies($pkgs); 934 PEAR::popErrorHandling(); 935 if (PEAR::isError($err)) { 936 return $err; 937 } 938 $this->_downloadedPackages = &$pkgs; 939 } 940 941 /** 942 * Set the list of PEAR_Downloader_Package objects to allow more sane 943 * dependency validation 944 * @param array 945 */ 946 function setUninstallPackages(&$pkgs) 947 { 948 $this->_downloadedPackages = &$pkgs; 949 } 950 951 function getInstallPackages() 952 { 953 return $this->_downloadedPackages; 954 } 955 956 // {{{ install() 957 958 /** 959 * Installs the files within the package file specified. 960 * 961 * @param string|PEAR_Downloader_Package $pkgfile path to the package file, 962 * or a pre-initialized packagefile object 963 * @param array $options 964 * recognized options: 965 * - installroot : optional prefix directory for installation 966 * - force : force installation 967 * - register-only : update registry but don't install files 968 * - upgrade : upgrade existing install 969 * - soft : fail silently 970 * - nodeps : ignore dependency conflicts/missing dependencies 971 * - alldeps : install all dependencies 972 * - onlyreqdeps : install only required dependencies 973 * 974 * @return array|PEAR_Error package info if successful 975 */ 976 977 function install($pkgfile, $options = array()) 978 { 979 $this->_options = $options; 980 $this->_registry = &$this->config->getRegistry(); 981 if (is_object($pkgfile)) { 982 $dlpkg = &$pkgfile; 983 $pkg = $pkgfile->getPackageFile(); 984 $pkgfile = $pkg->getArchiveFile(); 985 $descfile = $pkg->getPackageFile(); 986 $tmpdir = dirname($descfile); 987 } else { 988 $descfile = $pkgfile; 989 $tmpdir = ''; 990 if (PEAR::isError($pkg = &$this->_parsePackageXml($descfile, $tmpdir))) { 991 return $pkg; 992 } 993 } 994 995 if (realpath($descfile) != realpath($pkgfile)) { 996 $tar = new Archive_Tar($pkgfile); 997 if (!@$tar->extract($tmpdir)) { 998 return $this->raiseError("unable to unpack $pkgfile"); 999 } 1000 } 1001 1002 $pkgname = $pkg->getName(); 1003 $channel = $pkg->getChannel(); 1004 if (isset($this->_options['packagingroot'])) { 1005 $packrootphp_dir = $this->_prependPath( 1006 $this->config->get('php_dir', null, $channel), 1007 $this->_options['packagingroot']); 1008 } 1009 1010 if (isset($options['installroot'])) { 1011 $this->config->setInstallRoot($options['installroot']); 1012 $this->_registry = &$this->config->getRegistry(); 1013 $installregistry = &$this->_registry; 1014 $this->installroot = ''; // all done automagically now 1015 $php_dir = $this->config->get('php_dir', null, $channel); 1016 } else { 1017 $this->config->setInstallRoot(false); 1018 $this->_registry = &$this->config->getRegistry(); 1019 if (isset($this->_options['packagingroot'])) { 1020 $installregistry = &new PEAR_Registry($packrootphp_dir); 1021 $php_dir = $packrootphp_dir; 1022 } else { 1023 $installregistry = &$this->_registry; 1024 $php_dir = $this->config->get('php_dir', null, $channel); 1025 } 1026 $this->installroot = ''; 1027 } 1028 1029 // {{{ checks to do when not in "force" mode 1030 if (empty($options['force']) && @is_dir($this->config->get('php_dir'))) { 1031 $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname); 1032 $instfilelist = $pkg->getInstallationFileList(true); 1033 if (PEAR::isError($instfilelist)) { 1034 return $instfilelist; 1035 } 1036 $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1'); 1037 if (PEAR::isError($test)) { 1038 return $test; 1039 } 1040 if (sizeof($test)) { 1041 $pkgs = $this->getInstallPackages(); 1042 $found = false; 1043 foreach ($pkgs as $param) { 1044 if ($pkg->isSubpackageOf($param)) { 1045 $found = true; 1046 break; 1047 } 1048 } 1049 if ($found) { 1050 // subpackages can conflict with earlier versions of parent packages 1051 $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel()); 1052 $tmp = $test; 1053 foreach ($tmp as $file => $info) { 1054 if (is_array($info)) { 1055 if (strtolower($info[1]) == strtolower($param->getPackage()) && 1056 strtolower($info[0]) == strtolower($param->getChannel())) { 1057 unset($test[$file]); 1058 unset($parentreg['filelist'][$file]); 1059 } 1060 } else { 1061 if (strtolower($param->getChannel()) != 'pear.php.net') { 1062 continue; 1063 } 1064 if (strtolower($info) == strtolower($param->getPackage())) { 1065 unset($test[$file]); 1066 unset($parentreg['filelist'][$file]); 1067 } 1068 } 1069 } 1070 $pfk = &new PEAR_PackageFile($this->config); 1071 $parentpkg = &$pfk->fromArray($parentreg); 1072 $installregistry->updatePackage2($parentpkg); 1073 } 1074 if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) { 1075 $tmp = $test; 1076 foreach ($tmp as $file => $info) { 1077 if (is_string($info)) { 1078 // pear.php.net packages are always stored as strings 1079 if (strtolower($info) == strtolower($param->getPackage())) { 1080 // upgrading existing package 1081 unset($test[$file]); 1082 } 1083 } 1084 } 1085 } 1086 if (sizeof($test)) { 1087 $msg = "$channel/$pkgname: conflicting files found:\n"; 1088 $longest = max(array_map("strlen", array_keys($test))); 1089 $fmt = "%$longest}s (%s)\n"; 1090 foreach ($test as $file => $info) { 1091 if (!is_array($info)) { 1092 $info = array('pear.php.net', $info); 1093 } 1094 $info = $info[0] . '/' . $info[1]; 1095 $msg .= sprintf($fmt, $file, $info); 1096 } 1097 if (!isset($options['ignore-errors'])) { 1098 return $this->raiseError($msg); 1099 } else { 1100 if (!isset($options['soft'])) { 1101 $this->log(0, "WARNING: $msg"); 1102 } 1103 } 1104 } 1105 } 1106 } 1107 // }}} 1108 1109 $this->startFileTransaction(); 1110 1111 if (empty($options['upgrade']) && empty($options['soft'])) { 1112 // checks to do only when installing new packages 1113 if ($channel == 'pecl.php.net') { 1114 $test = $installregistry->packageExists($pkgname, $channel); 1115 if (!$test) { 1116 $test = $installregistry->packageExists($pkgname, 'pear.php.net'); 1117 } 1118 } else { 1119 $test = $installregistry->packageExists($pkgname, $channel); 1120 } 1121 if (empty($options['force']) && $test) { 1122 return $this->raiseError("$channel/$pkgname is already installed"); 1123 } 1124 } else { 1125 $usechannel = $channel; 1126 if ($channel == 'pecl.php.net') { 1127 $test = $installregistry->packageExists($pkgname, $channel); 1128 if (!$test) { 1129 $test = $installregistry->packageExists($pkgname, 'pear.php.net'); 1130 $usechannel = 'pear.php.net'; 1131 } 1132 } else { 1133 $test = $installregistry->packageExists($pkgname, $channel); 1134 } 1135 if ($test) { 1136 $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel); 1137 $v2 = $pkg->getVersion(); 1138 $cmp = version_compare("$v1", "$v2", 'gt'); 1139 if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) { 1140 return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)"); 1141 } 1142 if (empty($options['register-only'])) { 1143 // when upgrading, remove old release's files first: 1144 if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel, 1145 true))) { 1146 if (!isset($options['ignore-errors'])) { 1147 return $this->raiseError($err); 1148 } else { 1149 if (!isset($options['soft'])) { 1150 $this->log(0, 'WARNING: ' . $err->getMessage()); 1151 } 1152 } 1153 } else { 1154 $backedup = $err; 1155 } 1156 } 1157 } 1158 } 1159 1160 // {{{ Copy files to dest dir --------------------------------------- 1161 1162 // info from the package it self we want to access from _installFile 1163 $this->pkginfo = &$pkg; 1164 // used to determine whether we should build any C code 1165 $this->source_files = 0; 1166 1167 $savechannel = $this->config->get('default_channel'); 1168 if (empty($options['register-only']) && !is_dir($php_dir)) { 1169 if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) { 1170 return $this->raiseError("no installation destination directory '$php_dir'\n"); 1171 } 1172 } 1173 1174 $tmp_path = dirname($descfile); 1175 if (substr($pkgfile, -4) != '.xml') { 1176 $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion(); 1177 } 1178 1179 $this->configSet('default_channel', $channel); 1180 // {{{ install files 1181 1182 if ($pkg->getPackagexmlVersion() == '2.0') { 1183 $filelist = $pkg->getInstallationFilelist(); 1184 } else { 1185 $filelist = $pkg->getFileList(); 1186 } 1187 if (PEAR::isError($filelist)) { 1188 return $filelist; 1189 } 1190 $pkg->resetFilelist(); 1191 $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(), 1192 'version', $pkg->getChannel())); 1193 foreach ($filelist as $file => $atts) { 1194 if ($pkg->getPackagexmlVersion() == '1.0') { 1195 $this->expectError(PEAR_INSTALLER_FAILED); 1196 $res = $this->_installFile($file, $atts, $tmp_path, $options); 1197 $this->popExpect(); 1198 } else { 1199 $this->expectError(PEAR_INSTALLER_FAILED); 1200 $res = $this->_installFile2($pkg, $file, $atts, $tmp_path, $options); 1201 $this->popExpect(); 1202 } 1203 if (PEAR::isError($res)) { 1204 if (empty($options['ignore-errors'])) { 1205 $this->rollbackFileTransaction(); 1206 if ($res->getMessage() == "file does not exist") { 1207 $this->raiseError("file $file in package.xml does not exist"); 1208 } 1209 return $this->raiseError($res); 1210 } else { 1211 if (!isset($options['soft'])) { 1212 $this->log(0, "Warning: " . $res->getMessage()); 1213 } 1214 } 1215 } 1216 if ($res == PEAR_INSTALLER_OK) { 1217 // Register files that were installed 1218 $pkg->installedFile($file, $atts); 1219 } 1220 } 1221 // }}} 1222 1223 // {{{ compile and install source files 1224 if ($this->source_files > 0 && empty($options['nobuild'])) { 1225 if (PEAR::isError($err = 1226 $this->_compileSourceFiles($savechannel, $pkg))) { 1227 return $err; 1228 } 1229 } 1230 // }}} 1231 1232 if (isset($backedup)) { 1233 $this->_removeBackups($backedup); 1234 } 1235 if (!$this->commitFileTransaction()) { 1236 $this->rollbackFileTransaction(); 1237 $this->configSet('default_channel', $savechannel); 1238 return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED); 1239 } 1240 // }}} 1241 1242 $ret = false; 1243 $installphase = 'install'; 1244 $oldversion = false; 1245 // {{{ Register that the package is installed ----------------------- 1246 if (empty($options['upgrade'])) { 1247 // if 'force' is used, replace the info in registry 1248 $usechannel = $channel; 1249 if ($channel == 'pecl.php.net') { 1250 $test = $installregistry->packageExists($pkgname, $channel); 1251 if (!$test) { 1252 $test = $installregistry->packageExists($pkgname, 'pear.php.net'); 1253 $usechannel = 'pear.php.net'; 1254 } 1255 } else { 1256 $test = $installregistry->packageExists($pkgname, $channel); 1257 } 1258 if (!empty($options['force']) && $test) { 1259 $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel); 1260 $installregistry->deletePackage($pkgname, $usechannel); 1261 } 1262 $ret = $installregistry->addPackage2($pkg); 1263 } else { 1264 $usechannel = $channel; 1265 if ($channel == 'pecl.php.net') { 1266 $test = $installregistry->packageExists($pkgname, $channel); 1267 if (!$test) { 1268 $test = $installregistry->packageExists($pkgname, 'pear.php.net'); 1269 $usechannel = 'pear.php.net'; 1270 } 1271 } else { 1272 $test = $installregistry->packageExists($pkgname, $channel); 1273 } 1274 // new: upgrade installs a package if it isn't installed 1275 if (!$test) { 1276 $ret = $installregistry->addPackage2($pkg); 1277 } else { 1278 if ($usechannel != $channel) { 1279 $installregistry->deletePackage($pkgname, $usechannel); 1280 $ret = $installregistry->addPackage2($pkg); 1281 } else { 1282 $ret = $installregistry->updatePackage2($pkg); 1283 } 1284 $installphase = 'upgrade'; 1285 } 1286 } 1287 if (!$ret) { 1288 $this->configSet('default_channel', $savechannel); 1289 return $this->raiseError("Adding package $channel/$pkgname to registry failed"); 1290 } 1291 // }}} 1292 $this->configSet('default_channel', $savechannel); 1293 if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist 1294 if (PEAR_Task_Common::hasPostinstallTasks()) { 1295 PEAR_Task_Common::runPostinstallTasks($installphase); 1296 } 1297 } 1298 return $pkg->toArray(true); 1299 } 1300 1301 // }}} 1302 1303 // {{{ _compileSourceFiles() 1304 /** 1305 * @param string 1306 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 1307 */ 1308 function _compileSourceFiles($savechannel, &$filelist) 1309 { 1310 require_once 'PEAR/Builder.php'; 1311 $this->log(1, "$this->source_files source files, building"); 1312 $bob = &new PEAR_Builder($this->ui); 1313 $bob->debug = $this->debug; 1314 $built = $bob->build($filelist, array(&$this, '_buildCallback')); 1315 if (PEAR::isError($built)) { 1316 $this->rollbackFileTransaction(); 1317 $this->configSet('default_channel', $savechannel); 1318 return $built; 1319 } 1320 $this->log(1, "\nBuild process completed successfully"); 1321 foreach ($built as $ext) { 1322 $bn = basename($ext['file']); 1323 list($_ext_name, $_ext_suff) = explode('.', $bn); 1324 if ($_ext_suff == '.so' || $_ext_suff == '.dll') { 1325 if (extension_loaded($_ext_name)) { 1326 $this->raiseError("Extension '$_ext_name' already loaded. " . 1327 'Please unload it in your php.ini file ' . 1328 'prior to install or upgrade'); 1329 } 1330 $role = 'ext'; 1331 } else { 1332 $role = 'src'; 1333 } 1334 $dest = $ext['dest']; 1335 $this->log(1, "Installing '$ext[file]'"); 1336 $packagingroot = ''; 1337 if (isset($this->_options['packagingroot'])) { 1338 $packagingroot = $this->_options['packagingroot']; 1339 } 1340 $copyto = $this->_prependPath($dest, $packagingroot); 1341 $copydir = dirname($copyto); 1342 // pretty much nothing happens if we are only registering the install 1343 if (empty($this->_options['register-only'])) { 1344 if (!@is_dir($copydir)) { 1345 if (!$this->mkDirHier($copydir)) { 1346 return $this->raiseError("failed to mkdir $copydir", 1347 PEAR_INSTALLER_FAILED); 1348 } 1349 $this->log(3, "+ mkdir $copydir"); 1350 } 1351 if (!@copy($ext['file'], $copyto)) { 1352 return $this->raiseError("failed to write $copyto", PEAR_INSTALLER_FAILED); 1353 } 1354 $this->log(3, "+ cp $ext[file] $copyto"); 1355 if (!OS_WINDOWS) { 1356 $mode = 0666 & ~(int)octdec($this->config->get('umask')); 1357 $this->addFileOperation('chmod', array($mode, $copyto)); 1358 if (!@chmod($copyto, $mode)) { 1359 $this->log(0, "failed to change mode of $copyto"); 1360 } 1361 } 1362 $this->addFileOperation('rename', array($ext['file'], $copyto)); 1363 } 1364 1365 if ($filelist->getPackageXmlVersion() == '1.0') { 1366 $filelist->installedFile($bn, array( 1367 'role' => $role, 1368 'name' => $bn, 1369 'installed_as' => $dest, 1370 'php_api' => $ext['php_api'], 1371 'zend_mod_api' => $ext['zend_mod_api'], 1372 'zend_ext_api' => $ext['zend_ext_api'], 1373 )); 1374 } else { 1375 $filelist->installedFile($bn, array('attribs' => array( 1376 'role' => $role, 1377 'name' => $bn, 1378 'installed_as' => $dest, 1379 'php_api' => $ext['php_api'], 1380 'zend_mod_api' => $ext['zend_mod_api'], 1381 'zend_ext_api' => $ext['zend_ext_api'], 1382 ))); 1383 } 1384 } 1385 } 1386 1387 // }}} 1388 function &getUninstallPackages() 1389 { 1390 return $this->_downloadedPackages; 1391 } 1392 // {{{ uninstall() 1393 1394 /** 1395 * Uninstall a package 1396 * 1397 * This method removes all files installed by the application, and then 1398 * removes any empty directories. 1399 * @param string package name 1400 * @param array Command-line options. Possibilities include: 1401 * 1402 * - installroot: base installation dir, if not the default 1403 * - nodeps: do not process dependencies of other packages to ensure 1404 * uninstallation does not break things 1405 */ 1406 function uninstall($package, $options = array()) 1407 { 1408 if (isset($options['installroot'])) { 1409 $this->config->setInstallRoot($options['installroot']); 1410 $this->installroot = ''; 1411 } else { 1412 $this->config->setInstallRoot(''); 1413 $this->installroot = ''; 1414 } 1415 $this->_registry = &$this->config->getRegistry(); 1416 if (is_object($package)) { 1417 $channel = $package->getChannel(); 1418 $pkg = $package; 1419 $package = $pkg->getPackage(); 1420 } else { 1421 $pkg = false; 1422 $info = $this->_registry->parsePackageName($package, 1423 $this->config->get('default_channel')); 1424 $channel = $info['channel']; 1425 $package = $info['package']; 1426 } 1427 $savechannel = $this->config->get('default_channel'); 1428 $this->configSet('default_channel', $channel); 1429 if (!is_object($pkg)) { 1430 $pkg = $this->_registry->getPackage($package, $channel); 1431 } 1432 if (!$pkg) { 1433 $this->configSet('default_channel', $savechannel); 1434 return $this->raiseError($this->_registry->parsedPackageNameToString( 1435 array( 1436 'channel' => $channel, 1437 'package' => $package 1438 ), true) . ' not installed'); 1439 } 1440 if ($pkg->getInstalledBinary()) { 1441 // this is just an alias for a binary package 1442 return $this->_registry->deletePackage($package, $channel); 1443 } 1444 $filelist = $pkg->getFilelist(); 1445 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 1446 if (!class_exists('PEAR_Dependency2')) { 1447 require_once 'PEAR/Dependency2.php'; 1448 } 1449 $depchecker = &new PEAR_Dependency2($this->config, $options, 1450 array('channel' => $channel, 'package' => $package), 1451 PEAR_VALIDATE_UNINSTALLING); 1452 $e = $depchecker->validatePackageUninstall($this); 1453 PEAR::staticPopErrorHandling(); 1454 if (PEAR::isError($e)) { 1455 if (!isset($options['ignore-errors'])) { 1456 return $this->raiseError($e); 1457 } else { 1458 if (!isset($options['soft'])) { 1459 $this->log(0, 'WARNING: ' . $e->getMessage()); 1460 } 1461 } 1462 } elseif (is_array($e)) { 1463 if (!isset($options['soft'])) { 1464 $this->log(0, $e[0]); 1465 } 1466 } 1467 // {{{ Delete the files 1468 $this->startFileTransaction(); 1469 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1470 if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) { 1471 PEAR::popErrorHandling(); 1472 $this->rollbackFileTransaction(); 1473 $this->configSet('default_channel', $savechannel); 1474 if (!isset($options['ignore-errors'])) { 1475 return $this->raiseError($err); 1476 } else { 1477 if (!isset($options['soft'])) { 1478 $this->log(0, 'WARNING: ' . $err->getMessage()); 1479 } 1480 } 1481 } else { 1482 PEAR::popErrorHandling(); 1483 } 1484 if (!$this->commitFileTransaction()) { 1485 $this->rollbackFileTransaction(); 1486 if (!isset($options['ignore-errors'])) { 1487 return $this->raiseError("uninstall failed"); 1488 } elseif (!isset($options['soft'])) { 1489 $this->log(0, 'WARNING: uninstall failed'); 1490 } 1491 } else { 1492 $this->startFileTransaction(); 1493 if ($dirtree = $pkg->getDirTree()) { 1494 // attempt to delete empty directories 1495 uksort($dirtree, array($this, '_sortDirs')); 1496 foreach($dirtree as $dir => $notused) { 1497 $this->addFileOperation('rmdir', array($dir)); 1498 } 1499 } else { 1500 $this->configSet('default_channel', $savechannel); 1501 return $this->_registry->deletePackage($package, $channel); 1502 } 1503 if (!$this->commitFileTransaction()) { 1504 $this->rollbackFileTransaction(); 1505 } 1506 } 1507 // }}} 1508 1509 $this->configSet('default_channel', $savechannel); 1510 // Register that the package is no longer installed 1511 return $this->_registry->deletePackage($package, $channel); 1512 } 1513 1514 /** 1515 * Sort a list of arrays of array(downloaded packagefilename) by dependency. 1516 * 1517 * It also removes duplicate dependencies 1518 * @param array an array of PEAR_PackageFile_v[1/2] objects 1519 * @return array|PEAR_Error array of array(packagefilename, package.xml contents) 1520 */ 1521 function sortPackagesForUninstall(&$packages) 1522 { 1523 $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config); 1524 if (PEAR::isError($this->_dependencyDB)) { 1525 return $this->_dependencyDB; 1526 } 1527 usort($packages, array(&$this, '_sortUninstall')); 1528 } 1529 1530 function _sortUninstall($a, $b) 1531 { 1532 if (!$a->getDeps() && !$b->getDeps()) { 1533 return 0; // neither package has dependencies, order is insignificant 1534 } 1535 if ($a->getDeps() && !$b->getDeps()) { 1536 return -1; // $a must be installed after $b because $a has dependencies 1537 } 1538 if (!$a->getDeps() && $b->getDeps()) { 1539 return 1; // $b must be installed after $a because $b has dependencies 1540 } 1541 // both packages have dependencies 1542 if ($this->_dependencyDB->dependsOn($a, $b)) { 1543 return -1; 1544 } 1545 if ($this->_dependencyDB->dependsOn($b, $a)) { 1546 return 1; 1547 } 1548 return 0; 1549 } 1550 1551 // }}} 1552 // {{{ _sortDirs() 1553 function _sortDirs($a, $b) 1554 { 1555 if (strnatcmp($a, $b) == -1) return 1; 1556 if (strnatcmp($a, $b) == 1) return -1; 1557 return 0; 1558 } 1559 1560 // }}} 1561 1562 // {{{ _buildCallback() 1563 1564 function _buildCallback($what, $data) 1565 { 1566 if (($what == 'cmdoutput' && $this->debug > 1) || 1567 ($what == 'output' && $this->debug > 0)) { 1568 $this->ui->outputData(rtrim($data), 'build'); 1569 } 1570 } 1571 1572 // }}} 1573 } 1574 1575 // {{{ md5_file() utility function 1576 if (!function_exists("md5_file")) { 1577 function md5_file($filename) { 1578 $fp = fopen($filename, "r"); 1579 if (!$fp) return null; 1580 if (function_exists('file_get_contents')) { 1581 fclose($fp); 1582 $contents = file_get_contents($filename); 1583 } else { 1584 $contents = fread($fp, filesize($filename)); 1585 fclose($fp); 1586 } 1587 return md5($contents); 1588 } 1589 } 1590 // }}} 1591 1592 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Wed Nov 21 12:27:40 2007 | par Balluche grâce à PHPXref 0.7 |
|