1<?php 2/** 3 * PEAR_PackageFile_v2, package.xml version 2.0 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 * @copyright 1997-2006 The PHP Group 17 * @license http://www.php.net/license/3_0.txt PHP License 3.0 18 * @version CVS: $Id: v2.php,v 1.129.2.1 2006/03/23 04:07:51 cellog Exp $ 19 * @link http://pear.php.net/package/PEAR 20 * @since File available since Release 1.4.0a1 21 */ 22/** 23 * For error handling 24 */ 25require_once 'PEAR/ErrorStack.php'; 26/** 27 * @category pear 28 * @package PEAR 29 * @author Greg Beaver <cellog@php.net> 30 * @copyright 1997-2006 The PHP Group 31 * @license http://www.php.net/license/3_0.txt PHP License 3.0 32 * @version Release: 1.4.11 33 * @link http://pear.php.net/package/PEAR 34 * @since Class available since Release 1.4.0a1 35 */ 36class PEAR_PackageFile_v2 37{ 38 39 /** 40 * Parsed package information 41 * @var array 42 * @access private 43 */ 44 var $_packageInfo = array(); 45 46 /** 47 * path to package .tgz or false if this is a local/extracted package.xml 48 * @var string|false 49 * @access private 50 */ 51 var $_archiveFile; 52 53 /** 54 * path to package .xml or false if this is an abstract parsed-from-string xml 55 * @var string|false 56 * @access private 57 */ 58 var $_packageFile; 59 60 /** 61 * This is used by file analysis routines to log progress information 62 * @var PEAR_Common 63 * @access protected 64 */ 65 var $_logger; 66 67 /** 68 * This is set to the highest validation level that has been validated 69 * 70 * If the package.xml is invalid or unknown, this is set to 0. If 71 * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL. If 72 * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING 73 * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING. This allows validation 74 * "caching" to occur, which is particularly important for package validation, so 75 * that PHP files are not validated twice 76 * @var int 77 * @access private 78 */ 79 var $_isValid = 0; 80 81 /** 82 * True if the filelist has been validated 83 * @param bool 84 */ 85 var $_filesValid = false; 86 87 /** 88 * @var PEAR_Registry 89 * @access protected 90 */ 91 var $_registry; 92 93 /** 94 * @var PEAR_Config 95 * @access protected 96 */ 97 var $_config; 98 99 /** 100 * Optional Dependency group requested for installation 101 * @var string 102 * @access private 103 */ 104 var $_requestedGroup = false; 105 106 /** 107 * @var PEAR_ErrorStack 108 * @access protected 109 */ 110 var $_stack; 111 112 /** 113 * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible 114 */ 115 var $_tasksNs; 116 117 /** 118 * Determines whether this packagefile was initialized only with partial package info 119 * 120 * If this package file was constructed via parsing REST, it will only contain 121 * 122 * - package name 123 * - channel name 124 * - dependencies 125 * @var boolean 126 * @access private 127 */ 128 var $_incomplete = true; 129 130 /** 131 * @var PEAR_PackageFile_v2_Validator 132 */ 133 var $_v2Validator; 134 135 /** 136 * The constructor merely sets up the private error stack 137 */ 138 function PEAR_PackageFile_v2() 139 { 140 $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null); 141 $this->_isValid = false; 142 } 143 144 /** 145 * To make unit-testing easier 146 * @param PEAR_Frontend_* 147 * @param array options 148 * @param PEAR_Config 149 * @return PEAR_Downloader 150 * @access protected 151 */ 152 function &getPEARDownloader(&$i, $o, &$c) 153 { 154 $z = &new PEAR_Downloader($i, $o, $c); 155 return $z; 156 } 157 158 /** 159 * To make unit-testing easier 160 * @param PEAR_Config 161 * @param array options 162 * @param array package name as returned from {@link PEAR_Registry::parsePackageName()} 163 * @param int PEAR_VALIDATE_* constant 164 * @return PEAR_Dependency2 165 * @access protected 166 */ 167 function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING) 168 { 169 if (!class_exists('PEAR_Dependency2')) { 170 require_once 'PEAR/Dependency2.php'; 171 } 172 $z = &new PEAR_Dependency2($c, $o, $p, $s); 173 return $z; 174 } 175 176 function getInstalledBinary() 177 { 178 return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] : 179 false; 180 } 181 182 /** 183 * Installation of source package has failed, attempt to download and install the 184 * binary version of this package. 185 * @param PEAR_Installer 186 * @return array|false 187 */ 188 function installBinary(&$installer) 189 { 190 if (!OS_WINDOWS) { 191 $a = false; 192 return $a; 193 } 194 if ($this->getPackageType() == 'extsrc') { 195 if (!is_array($installer->getInstallPackages())) { 196 $a = false; 197 return $a; 198 } 199 foreach ($installer->getInstallPackages() as $p) { 200 if ($p->isExtension($this->_packageInfo['providesextension'])) { 201 if ($p->getPackageType() != 'extsrc') { 202 $a = false; 203 return $a; // the user probably downloaded it separately 204 } 205 } 206 } 207 if (isset($this->_packageInfo['extsrcrelease']['binarypackage'])) { 208 $installer->log(0, 'Attempting to download binary version of extension "' . 209 $this->_packageInfo['providesextension'] . '"'); 210 $params = $this->_packageInfo['extsrcrelease']['binarypackage']; 211 if (!is_array($params) || !isset($params[0])) { 212 $params = array($params); 213 } 214 if (isset($this->_packageInfo['channel'])) { 215 foreach ($params as $i => $param) { 216 $params[$i] = array('channel' => $this->_packageInfo['channel'], 217 'package' => $param, 'version' => $this->getVersion()); 218 } 219 } 220 $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(), 221 $installer->config); 222 $verbose = $dl->config->get('verbose'); 223 $dl->config->set('verbose', -1); 224 foreach ($params as $param) { 225 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 226 $ret = $dl->download(array($param)); 227 PEAR::popErrorHandling(); 228 if (is_array($ret) && count($ret)) { 229 break; 230 } 231 } 232 $dl->config->set('verbose', $verbose); 233 if (is_array($ret)) { 234 if (count($ret) == 1) { 235 $pf = $ret[0]->getPackageFile(); 236 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 237 $err = $installer->install($ret[0]); 238 PEAR::popErrorHandling(); 239 if (is_array($err)) { 240 $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage(); 241 // "install" self, so all dependencies will work transparently 242 $this->_registry->addPackage2($this); 243 $installer->log(0, 'Download and install of binary extension "' . 244 $this->_registry->parsedPackageNameToString( 245 array('channel' => $pf->getChannel(), 246 'package' => $pf->getPackage()), true) . '" successful'); 247 $a = array($ret[0], $err); 248 return $a; 249 } 250 $installer->log(0, 'Download and install of binary extension "' . 251 $this->_registry->parsedPackageNameToString( 252 array('channel' => $pf->getChannel(), 253 'package' => $pf->getPackage()), true) . '" failed'); 254 } 255 } 256 } 257 } 258 $a = false; 259 return $a; 260 } 261 262 /** 263 * @return string|false Extension name 264 */ 265 function getProvidesExtension() 266 { 267 if (in_array($this->getPackageType(), array('extsrc', 'extbin'))) { 268 if (isset($this->_packageInfo['providesextension'])) { 269 return $this->_packageInfo['providesextension']; 270 } 271 } 272 return false; 273 } 274 275 /** 276 * @param string Extension name 277 * @return bool 278 */ 279 function isExtension($extension) 280 { 281 if (in_array($this->getPackageType(), array('extsrc', 'extbin'))) { 282 return $this->_packageInfo['providesextension'] == $extension; 283 } 284 return false; 285 } 286 287 /** 288 * Tests whether every part of the package.xml 1.0 is represented in 289 * this package.xml 2.0 290 * @param PEAR_PackageFile_v1 291 * @return bool 292 */ 293 function isEquivalent($pf1) 294 { 295 if (!$pf1) { 296 return true; 297 } 298 if ($this->getPackageType() == 'bundle') { 299 return false; 300 } 301 $this->_stack->getErrors(true); 302 if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) { 303 return false; 304 } 305 $pass = true; 306 if ($pf1->getPackage() != $this->getPackage()) { 307 $this->_differentPackage($pf1->getPackage()); 308 $pass = false; 309 } 310 if ($pf1->getVersion() != $this->getVersion()) { 311 $this->_differentVersion($pf1->getVersion()); 312 $pass = false; 313 } 314 if (trim($pf1->getSummary()) != $this->getSummary()) { 315 $this->_differentSummary($pf1->getSummary()); 316 $pass = false; 317 } 318 if (preg_replace('/\s+/', '', $pf1->getDescription()) != 319 preg_replace('/\s+/', '', $this->getDescription())) { 320 $this->_differentDescription($pf1->getDescription()); 321 $pass = false; 322 } 323 if ($pf1->getState() != $this->getState()) { 324 $this->_differentState($pf1->getState()); 325 $pass = false; 326 } 327 if (!strstr(preg_replace('/\s+/', '', $this->getNotes()), 328 preg_replace('/\s+/', '', $pf1->getNotes()))) { 329 $this->_differentNotes($pf1->getNotes()); 330 $pass = false; 331 } 332 $mymaintainers = $this->getMaintainers(); 333 $yourmaintainers = $pf1->getMaintainers(); 334 for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) { 335 $reset = false; 336 for ($i2 = 0; $i2 < count($mymaintainers); $i2++) { 337 if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) { 338 if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) { 339 $this->_differentRole($mymaintainers[$i2]['handle'], 340 $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']); 341 $pass = false; 342 } 343 if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) { 344 $this->_differentEmail($mymaintainers[$i2]['handle'], 345 $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']); 346 $pass = false; 347 } 348 if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) { 349 $this->_differentName($mymaintainers[$i2]['handle'], 350 $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']); 351 $pass = false; 352 } 353 unset($mymaintainers[$i2]); 354 $mymaintainers = array_values($mymaintainers); 355 unset($yourmaintainers[$i1]); 356 $yourmaintainers = array_values($yourmaintainers); 357 $reset = true; 358 break; 359 } 360 } 361 if ($reset) { 362 $i1 = -1; 363 } 364 } 365 $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers); 366 $filelist = $this->getFilelist(); 367 foreach ($pf1->getFilelist() as $file => $atts) { 368 if (!isset($filelist[$file])) { 369 $this->_missingFile($file); 370 $pass = false; 371 } 372 } 373 return $pass; 374 } 375 376 function _differentPackage($package) 377 { 378 $this->_stack->push(__FUNCTION__, 'error', array('package' => $package, 379 'self' => $this->getPackage()), 380 'package.xml 1.0 package "%package%" does not match "%self%"'); 381 } 382 383 function _differentVersion($version) 384 { 385 $this->_stack->push(__FUNCTION__, 'error', array('version' => $version, 386 'self' => $this->getVersion()), 387 'package.xml 1.0 version "%version%" does not match "%self%"'); 388 } 389 390 function _differentState($state) 391 { 392 $this->_stack->push(__FUNCTION__, 'error', array('state' => $state, 393 'self' => $this->getState()), 394 'package.xml 1.0 state "%state%" does not match "%self%"'); 395 } 396 397 function _differentRole($handle, $role, $selfrole) 398 { 399 $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, 400 'role' => $role, 'self' => $selfrole), 401 'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"'); 402 } 403 404 function _differentEmail($handle, $email, $selfemail) 405 { 406 $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, 407 'email' => $email, 'self' => $selfemail), 408 'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"'); 409 } 410 411 function _differentName($handle, $name, $selfname) 412 { 413 $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, 414 'name' => $name, 'self' => $selfname), 415 'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"'); 416 } 417 418 function _unmatchedMaintainers($my, $yours) 419 { 420 if ($my) { 421 array_walk($my, create_function('&$i, $k', '$i = $i["handle"];')); 422 $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my), 423 'package.xml 2.0 has unmatched extra maintainers "%handles%"'); 424 } 425 if ($yours) { 426 array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];')); 427 $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours), 428 'package.xml 1.0 has unmatched extra maintainers "%handles%"'); 429 } 430 } 431 432 function _differentNotes($notes) 433 { 434 $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...'; 435 $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() : 436 substr($this->getNotes(), 0, 24) . '...'; 437 $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes, 438 'self' => $truncmynotes), 439 'package.xml 1.0 release notes "%notes%" do not match "%self%"'); 440 } 441 442 function _differentSummary($summary) 443 { 444 $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...'; 445 $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() : 446 substr($this->getsummary(), 0, 24) . '...'; 447 $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary, 448 'self' => $truncmysummary), 449 'package.xml 1.0 summary "%summary%" does not match "%self%"'); 450 } 451 452 function _differentDescription($description) 453 { 454 $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...'); 455 $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() : 456 substr($this->getdescription(), 0, 24) . '...'); 457 $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription, 458 'self' => $truncmydescription), 459 'package.xml 1.0 description "%description%" does not match "%self%"'); 460 } 461 462 function _missingFile($file) 463 { 464 $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), 465 'package.xml 1.0 file "%file%" is not present in <contents>'); 466 } 467 468 /** 469 * WARNING - do not use this function unless you know what you're doing 470 */ 471 function setRawState($state) 472 { 473 $this->_packageInfo['stability']['release'] = $state; 474 } 475 476 /** 477 * WARNING - do not use this function unless you know what you're doing 478 */ 479 function setRawCompatible($compatible) 480 { 481 $this->_packageInfo['compatible'] = $compatible; 482 } 483 484 /** 485 * WARNING - do not use this function unless you know what you're doing 486 */ 487 function setRawPackage($package) 488 { 489 $this->_packageInfo['name'] = $package; 490 } 491 492 /** 493 * WARNING - do not use this function unless you know what you're doing 494 */ 495 function setRawChannel($channel) 496 { 497 $this->_packageInfo['channel'] = $channel; 498 } 499 500 function setRequestedGroup($group) 501 { 502 $this->_requestedGroup = $group; 503 } 504 505 function getRequestedGroup() 506 { 507 if (isset($this->_requestedGroup)) { 508 return $this->_requestedGroup; 509 } 510 return false; 511 } 512 513 /** 514 * For saving in the registry. 515 * 516 * Set the last version that was installed 517 * @param string 518 */ 519 function setLastInstalledVersion($version) 520 { 521 $this->_packageInfo['_lastversion'] = $version; 522 } 523 524 /** 525 * @return string|false 526 */ 527 function getLastInstalledVersion() 528 { 529 if (isset($this->_packageInfo['_lastversion'])) { 530 return $this->_packageInfo['_lastversion']; 531 } 532 return false; 533 } 534 535 /** 536 * Determines whether this package.xml has post-install scripts or not 537 * @return array|false 538 */ 539 function listPostinstallScripts() 540 { 541 $filelist = $this->getFilelist(); 542 $contents = $this->getContents(); 543 $contents = $contents['dir']['file']; 544 if (!is_array($contents) || !isset($contents[0])) { 545 $contents = array($contents); 546 } 547 $taskfiles = array(); 548 foreach ($contents as $file) { 549 $atts = $file['attribs']; 550 unset($file['attribs']); 551 if (count($file)) { 552 $taskfiles[$atts['name']] = $file; 553 } 554 } 555 $common = new PEAR_Common; 556 $common->debug = $this->_config->get('verbose'); 557 $this->_scripts = array(); 558 $ret = array(); 559 foreach ($taskfiles as $name => $tasks) { 560 if (!isset($filelist[$name])) { 561 // ignored files will not be in the filelist 562 continue; 563 } 564 $atts = $filelist[$name]; 565 foreach ($tasks as $tag => $raw) { 566 $task = $this->getTask($tag); 567 $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL); 568 if ($task->isScript()) { 569 $ret[] = $filelist[$name]['installed_as']; 570 } 571 } 572 } 573 if (count($ret)) { 574 return $ret; 575 } 576 return false; 577 } 578 579 /** 580 * Initialize post-install scripts for running 581 * 582 * This method can be used to detect post-install scripts, as the return value 583 * indicates whether any exist 584 * @return bool 585 */ 586 function initPostinstallScripts() 587 { 588 $filelist = $this->getFilelist(); 589 $contents = $this->getContents(); 590 $contents = $contents['dir']['file']; 591 if (!is_array($contents) || !isset($contents[0])) { 592 $contents = array($contents); 593 } 594 $taskfiles = array(); 595 foreach ($contents as $file) { 596 $atts = $file['attribs']; 597 unset($file['attribs']); 598 if (count($file)) { 599 $taskfiles[$atts['name']] = $file; 600 } 601 } 602 $common = new PEAR_Common; 603 $common->debug = $this->_config->get('verbose'); 604 $this->_scripts = array(); 605 foreach ($taskfiles as $name => $tasks) { 606 $atts = $filelist[$name]; 607 foreach ($tasks as $tag => $raw) { 608 $taskname = $this->getTask($tag); 609 $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL); 610 if (!$task->isScript()) { 611 continue; // scripts are only handled after installation 612 } 613 $lastversion = isset($this->_packageInfo['_lastversion']) ? 614 $this->_packageInfo['_lastversion'] : null; 615 $task->init($raw, $atts, $lastversion); 616 $res = $task->startSession($this, $atts['installed_as']); 617 if (!$res) { 618 continue; // skip this file 619 } 620 if (PEAR::isError($res)) { 621 return $res; 622 } 623 $assign = &$task; 624 $this->_scripts[] = &$assign; 625 } 626 } 627 if (count($this->_scripts)) { 628 return true; 629 } 630 return false; 631 } 632 633 function runPostinstallScripts() 634 { 635 if ($this->initPostinstallScripts()) { 636 $ui = &PEAR_Frontend::singleton(); 637 if ($ui) { 638 $ui->runPostinstallScripts($this->_scripts, $this); 639 } 640 } 641 } 642 643 644 /** 645 * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with 646 * <file> tags. 647 */ 648 function flattenFilelist() 649 { 650 if (isset($this->_packageInfo['bundle'])) { 651 return; 652 } 653 $filelist = array(); 654 if (isset($this->_packageInfo['contents']['dir']['dir'])) { 655 $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']); 656 if (!isset($filelist[1])) { 657 $filelist = $filelist[0]; 658 } 659 $this->_packageInfo['contents']['dir']['file'] = $filelist; 660 unset($this->_packageInfo['contents']['dir']['dir']); 661 } else { 662 // else already flattened but check for baseinstalldir propagation 663 if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) { 664 if (isset($this->_packageInfo['contents']['dir']['file'][0])) { 665 foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) { 666 if (isset($file['attribs']['baseinstalldir'])) { 667 continue; 668 } 669 $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir'] 670 = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir']; 671 } 672 } else { 673 if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) { 674 $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'] 675 = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir']; 676 } 677 } 678 } 679 } 680 } 681 682 /** 683 * @param array the final flattened file list 684 * @param array the current directory being processed 685 * @param string|false any recursively inherited baeinstalldir attribute 686 * @param string private recursion variable 687 * @return array 688 * @access protected 689 */ 690 function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '') 691 { 692 if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) { 693 $baseinstall = $dir['attribs']['baseinstalldir']; 694 } 695 if (isset($dir['dir'])) { 696 if (!isset($dir['dir'][0])) { 697 $dir['dir'] = array($dir['dir']); 698 } 699 foreach ($dir['dir'] as $subdir) { 700 if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) { 701 $name = '*unknown*'; 702 } else { 703 $name = $subdir['attribs']['name']; 704 } 705 $newpath = empty($path) ? $name : 706 $path . '/' . $name; 707 $this->_getFlattenedFilelist($files, $subdir, 708 $baseinstall, $newpath); 709 } 710 } 711 if (isset($dir['file'])) { 712 if (!isset($dir['file'][0])) { 713 $dir['file'] = array($dir['file']); 714 } 715 foreach ($dir['file'] as $file) { 716 $attrs = $file['attribs']; 717 $name = $attrs['name']; 718 if ($baseinstall && !isset($attrs['baseinstalldir'])) { 719 $attrs['baseinstalldir'] = $baseinstall; 720 } 721 $attrs['name'] = empty($path) ? $name : $path . '/' . $name; 722 $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), 723 $attrs['name']); 724 $file['attribs'] = $attrs; 725 $files[] = $file; 726 } 727 } 728 } 729 730 function setConfig(&$config) 731 { 732 $this->_config = &$config; 733 $this->_registry = &$config->getRegistry(); 734 } 735 736 function setLogger(&$logger) 737 { 738 if (!is_object($logger) || !method_exists($logger, 'log')) { 739 return PEAR::raiseError('Logger must be compatible with PEAR_Common::log'); 740 } 741 $this->_logger = &$logger; 742 } 743 744 /** 745 * WARNING - do not use this function directly unless you know what you're doing 746 */ 747 function setDeps($deps) 748 { 749 $this->_packageInfo['dependencies'] = $deps; 750 } 751 752 function setPackagefile($file, $archive = false) 753 { 754 $this->_packageFile = $file; 755 $this->_archiveFile = $archive ? $archive : $file; 756 } 757 758 /** 759 * Wrapper to {@link PEAR_ErrorStack::getErrors()} 760 * @param boolean determines whether to purge the error stack after retrieving 761 * @return array 762 */ 763 function getValidationWarnings($purge = true) 764 { 765 return $this->_stack->getErrors($purge); 766 } 767 768 function getPackageFile() 769 { 770 return $this->_packageFile; 771 } 772 773 function getArchiveFile() 774 { 775 return $this->_archiveFile; 776 } 777 778 779 /** 780 * Directly set the array that defines this packagefile 781 * 782 * WARNING: no validation. This should only be performed by internal methods 783 * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2 784 * @param array 785 */ 786 function fromArray($pinfo) 787 { 788 unset($pinfo['old']); 789 unset($pinfo['xsdversion']); 790 $this->_incomplete = false; 791 $this->_packageInfo = $pinfo; 792 } 793 794 function isIncomplete() 795 { 796 return $this->_incomplete; 797 } 798 799 /** 800 * @return array 801 */ 802 function toArray($forreg = false) 803 { 804 if (!$this->validate(PEAR_VALIDATE_NORMAL)) { 805 return false; 806 } 807 return $this->getArray($forreg); 808 } 809 810 function getArray($forReg = false) 811 { 812 if ($forReg) { 813 $arr = $this->_packageInfo; 814 $arr['old'] = array(); 815 $arr['old']['version'] = $this->getVersion(); 816 $arr['old']['release_date'] = $this->getDate(); 817 $arr['old']['release_state'] = $this->getState(); 818 $arr['old']['release_license'] = $this->getLicense(); 819 $arr['old']['release_notes'] = $this->getNotes(); 820 $arr['old']['release_deps'] = $this->getDeps(); 821 $arr['old']['maintainers'] = $this->getMaintainers(); 822 $arr['xsdversion'] = '2.0'; 823 return $arr; 824 } else { 825 $info = $this->_packageInfo; 826 unset($info['dirtree']); 827 if (isset($info['_lastversion'])) { 828 unset($info['_lastversion']); 829 } 830 if (isset($info['#binarypackage'])) { 831 unset($info['#binarypackage']); 832 } 833 return $info; 834 } 835 } 836 837 function packageInfo($field) 838 { 839 $arr = $this->getArray(true); 840 if ($field == 'state') { 841 return $arr['stability']['release']; 842 } 843 if ($field == 'api-version') { 844 return $arr['version']['api']; 845 } 846 if ($field == 'api-state') { 847 return $arr['stability']['api']; 848 } 849 if (isset($arr['old'][$field])) { 850 if (!is_string($arr['old'][$field])) { 851 return null; 852 } 853 return $arr['old'][$field]; 854 } 855 if (isset($arr[$field])) { 856 if (!is_string($arr[$field])) { 857 return null; 858 } 859 return $arr[$field]; 860 } 861 return null; 862 } 863 864 function getName() 865 { 866 return $this->getPackage(); 867 } 868 869 function getPackage() 870 { 871 if (isset($this->_packageInfo['name'])) { 872 return $this->_packageInfo['name']; 873 } 874 return false; 875 } 876 877 function getChannel() 878 { 879 if (isset($this->_packageInfo['uri'])) { 880 return '__uri'; 881 } 882 if (isset($this->_packageInfo['channel'])) { 883 return strtolower($this->_packageInfo['channel']); 884 } 885 return false; 886 } 887 888 function getUri() 889 { 890 if (isset($this->_packageInfo['uri'])) { 891 return $this->_packageInfo['uri']; 892 } 893 return false; 894 } 895 896 function getExtends() 897 { 898 if (isset($this->_packageInfo['extends'])) { 899 return $this->_packageInfo['extends']; 900 } 901 return false; 902 } 903 904 function getSummary() 905 { 906 if (isset($this->_packageInfo['summary'])) { 907 return $this->_packageInfo['summary']; 908 } 909 return false; 910 } 911 912 function getDescription() 913 { 914 if (isset($this->_packageInfo['description'])) { 915 return $this->_packageInfo['description']; 916 } 917 return false; 918 } 919 920 function getMaintainers($raw = false) 921 { 922 if (!isset($this->_packageInfo['lead'])) { 923 return false; 924 } 925 if ($raw) { 926 $ret = array('lead' => $this->_packageInfo['lead']); 927 (isset($this->_packageInfo['developer'])) ? 928 $ret['developer'] = $this->_packageInfo['developer'] :null; 929 (isset($this->_packageInfo['contributor'])) ? 930 $ret['contributor'] = $this->_packageInfo['contributor'] :null; 931 (isset($this->_packageInfo['helper'])) ? 932 $ret['helper'] = $this->_packageInfo['helper'] :null; 933 return $ret; 934 } else { 935 $ret = array(); 936 $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] : 937 array($this->_packageInfo['lead']); 938 foreach ($leads as $lead) { 939 $s = $lead; 940 $s['handle'] = $s['user']; 941 unset($s['user']); 942 $s['role'] = 'lead'; 943 $ret[] = $s; 944 } 945 if (isset($this->_packageInfo['developer'])) { 946 $leads = isset($this->_packageInfo['developer'][0]) ? 947 $this->_packageInfo['developer'] : 948 array($this->_packageInfo['developer']); 949 foreach ($leads as $maintainer) { 950 $s = $maintainer; 951 $s['handle'] = $s['user']; 952 unset($s['user']); 953 $s['role'] = 'developer'; 954 $ret[] = $s; 955 } 956 } 957 if (isset($this->_packageInfo['contributor'])) { 958 $leads = isset($this->_packageInfo['contributor'][0]) ? 959 $this->_packageInfo['contributor'] : 960 array($this->_packageInfo['contributor']); 961 foreach ($leads as $maintainer) { 962 $s = $maintainer; 963 $s['handle'] = $s['user']; 964 unset($s['user']); 965 $s['role'] = 'contributor'; 966 $ret[] = $s; 967 } 968 } 969 if (isset($this->_packageInfo['helper'])) { 970 $leads = isset($this->_packageInfo['helper'][0]) ? 971 $this->_packageInfo['helper'] : 972 array($this->_packageInfo['helper']); 973 foreach ($leads as $maintainer) { 974 $s = $maintainer; 975 $s['handle'] = $s['user']; 976 unset($s['user']); 977 $s['role'] = 'helper'; 978 $ret[] = $s; 979 } 980 } 981 return $ret; 982 } 983 return false; 984 } 985 986 function getLeads() 987 { 988 if (isset($this->_packageInfo['lead'])) { 989 return $this->_packageInfo['lead']; 990 } 991 return false; 992 } 993 994 function getDevelopers() 995 { 996 if (isset($this->_packageInfo['developer'])) { 997 return $this->_packageInfo['developer']; 998 } 999 return false; 1000 } 1001 1002 function getContributors() 1003 { 1004 if (isset($this->_packageInfo['contributor'])) { 1005 return $this->_packageInfo['contributor']; 1006 } 1007 return false; 1008 } 1009 1010 function getHelpers() 1011 { 1012 if (isset($this->_packageInfo['helper'])) { 1013 return $this->_packageInfo['helper']; 1014 } 1015 return false; 1016 } 1017 1018 function setDate($date) 1019 { 1020 if (!isset($this->_packageInfo['date'])) { 1021 // ensure that the extends tag is set up in the right location 1022 $this->_packageInfo = $this->_insertBefore($this->_packageInfo, 1023 array('time', 'version', 1024 'stability', 'license', 'notes', 'contents', 'compatible', 1025 'dependencies', 'providesextension', 'srcpackage', 'srcuri', 1026 'phprelease', 'extsrcrelease', 1027 'extbinrelease', 'bundle', 'changelog'), array(), 'date'); 1028 } 1029 $this->_packageInfo['date'] = $date; 1030 $this->_isValid = 0; 1031 } 1032 1033 function setTime($time) 1034 { 1035 $this->_isValid = 0; 1036 if (!isset($this->_packageInfo['time'])) { 1037 // ensure that the time tag is set up in the right location 1038 $this->_packageInfo = $this->_insertBefore($this->_packageInfo, 1039 array('version', 1040 'stability', 'license', 'notes', 'contents', 'compatible', 1041 'dependencies', 'providesextension', 'srcpackage', 'srcuri', 1042 'phprelease', 'extsrcrelease', 1043 'extbinrelease', 'bundle', 'changelog'), $time, 'time'); 1044 } 1045 $this->_packageInfo['time'] = $time; 1046 } 1047 1048 function getDate() 1049 { 1050 if (isset($this->_packageInfo['date'])) { 1051 return $this->_packageInfo['date']; 1052 } 1053 return false; 1054 } 1055 1056 function getTime() 1057 { 1058 if (isset($this->_packageInfo['time'])) { 1059 return $this->_packageInfo['time']; 1060 } 1061 return false; 1062 } 1063 1064 /** 1065 * @param package|api version category to return 1066 */ 1067 function getVersion($key = 'release') 1068 { 1069 if (isset($this->_packageInfo['version'][$key])) { 1070 return $this->_packageInfo['version'][$key]; 1071 } 1072 return false; 1073 } 1074 1075 function getStability() 1076 { 1077 if (isset($this->_packageInfo['stability'])) { 1078 return $this->_packageInfo['stability']; 1079 } 1080 return false; 1081 } 1082 1083 function getState($key = 'release') 1084 { 1085 if (isset($this->_packageInfo['stability'][$key])) { 1086 return $this->_packageInfo['stability'][$key]; 1087 } 1088 return false; 1089 } 1090 1091 function getLicense($raw = false) 1092 { 1093 if (isset($this->_packageInfo['license'])) { 1094 if ($raw) { 1095 return $this->_packageInfo['license']; 1096 } 1097 if (is_array($this->_packageInfo['license'])) { 1098 return $this->_packageInfo['license']['_content']; 1099 } else { 1100 return $this->_packageInfo['license']; 1101 } 1102 } 1103 return false; 1104 } 1105 1106 function getLicenseLocation() 1107 { 1108 if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) { 1109 return false; 1110 } 1111 return $this->_packageInfo['license']['attribs']; 1112 } 1113 1114 function getNotes() 1115 { 1116 if (isset($this->_packageInfo['notes'])) { 1117 return $this->_packageInfo['notes']; 1118 } 1119 return false; 1120 } 1121 1122 /** 1123 * Return the <usesrole> tag contents, if any 1124 * @return array|false 1125 */ 1126 function getUsesrole() 1127 { 1128 if (isset($this->_packageInfo['usesrole'])) { 1129 return $this->_packageInfo['usesrole']; 1130 } 1131 return false; 1132 } 1133 1134 /** 1135 * Return the <usestask> tag contents, if any 1136 * @return array|false 1137 */ 1138 function getUsestask() 1139 { 1140 if (isset($this->_packageInfo['usestask'])) { 1141 return $this->_packageInfo['usestask']; 1142 } 1143 return false; 1144 } 1145 1146 /** 1147 * This should only be used to retrieve filenames and install attributes 1148 */ 1149 function getFilelist($preserve = false) 1150 { 1151 if (isset($this->_packageInfo['filelist']) && !$preserve) { 1152 return $this->_packageInfo['filelist']; 1153 } 1154 $this->flattenFilelist(); 1155 if ($contents = $this->getContents()) { 1156 $ret = array(); 1157 if (!isset($contents['dir']['file'][0])) { 1158 $contents['dir']['file'] = array($contents['dir']['file']); 1159 } 1160 foreach ($contents['dir']['file'] as $file) { 1161 $name = $file['attribs']['name']; 1162 if (!$preserve) { 1163 $file = $file['attribs']; 1164 } 1165 $ret[$name] = $file; 1166 } 1167 if (!$preserve) { 1168 $this->_packageInfo['filelist'] = $ret; 1169 } 1170 return $ret; 1171 } 1172 return false; 1173 } 1174 1175 /** 1176 * Return configure options array, if any 1177 * 1178 * @return array|false 1179 */ 1180 function getConfigureOptions() 1181 { 1182 if ($this->getPackageType() != 'extsrc') { 1183 return false; 1184 } 1185 $releases = $this->getReleases(); 1186 if (isset($releases[0])) { 1187 $releases = $release[0]; 1188 } 1189 if (isset($releases['configureoption'])) { 1190 if (!isset($releases['configureoption'][0])) { 1191 $releases['configureoption'] = array($releases['configureoption']); 1192 } 1193 for ($i = 0; $i < count($releases['configureoption']); $i++) { 1194 $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs']; 1195 } 1196 return $releases['configureoption']; 1197 } 1198 return false; 1199 } 1200 1201 /** 1202 * This is only used at install-time, after all serialization 1203 * is over. 1204 */ 1205 function resetFilelist() 1206 { 1207 $this->_packageInfo['filelist'] = array(); 1208 } 1209 1210 /** 1211 * Retrieve a list of files that should be installed on this computer 1212 * @return array 1213 */ 1214 function getInstallationFilelist($forfilecheck = false) 1215 { 1216 $contents = $this->getFilelist(true); 1217 if (isset($contents['dir']['attribs']['baseinstalldir'])) { 1218 $base = $contents['dir']['attribs']['baseinstalldir']; 1219 } 1220 if (isset($this->_packageInfo['bundle'])) { 1221 return PEAR::raiseError( 1222 'Exception: bundles should be handled in download code only'); 1223 } 1224 $release = $this->getReleases(); 1225 if ($release) { 1226 if (!isset($release[0])) { 1227 if (!isset($release['installconditions']) && !isset($release['filelist'])) { 1228 if ($forfilecheck) { 1229 return $this->getFilelist(); 1230 } 1231 return $contents; 1232 } 1233 $release = array($release); 1234 } 1235 $depchecker = &$this->getPEARDependency2($this->_config, array(), 1236 array('channel' => $this->getChannel(), 'package' => $this->getPackage()), 1237 PEAR_VALIDATE_INSTALLING); 1238 foreach ($release as $instance) { 1239 if (isset($instance['installconditions'])) { 1240 $installconditions = $instance['installconditions']; 1241 if (is_array($installconditions)) { 1242 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1243 foreach ($installconditions as $type => $conditions) { 1244 if (!isset($conditions[0])) { 1245 $conditions = array($conditions); 1246 } 1247 foreach ($conditions as $condition) { 1248 $ret = $depchecker->{"validate{$type}Dependency"}($condition); 1249 if (PEAR::isError($ret)) { 1250 PEAR::popErrorHandling(); 1251 continue 3; // skip this release 1252 } 1253 } 1254 } 1255 PEAR::popErrorHandling(); 1256 } 1257 } 1258 // this is the release to use 1259 if (isset($instance['filelist'])) { 1260 // ignore files 1261 if (isset($instance['filelist']['ignore'])) { 1262 $ignore = isset($instance['filelist']['ignore'][0]) ? 1263 $instance['filelist']['ignore'] : 1264 array($instance['filelist']['ignore']); 1265 foreach ($ignore as $ig) { 1266 unset ($contents[$ig['attribs']['name']]); 1267 } 1268 } 1269 // install files as this name 1270 if (isset($instance['filelist']['install'])) { 1271 $installas = isset($instance['filelist']['install'][0]) ? 1272 $instance['filelist']['install'] : 1273 array($instance['filelist']['install']); 1274 foreach ($installas as $as) { 1275 $contents[$as['attribs']['name']]['attribs']['install-as'] = 1276 $as['attribs']['as']; 1277 } 1278 } 1279 } 1280 if ($forfilecheck) { 1281 foreach ($contents as $file => $attrs) { 1282 $contents[$file] = $attrs['attribs']; 1283 } 1284 } 1285 return $contents; 1286 } 1287 } else { // simple release - no installconditions or install-as 1288 if ($forfilecheck) { 1289 return $this->getFilelist(); 1290 } 1291 return $contents; 1292 } 1293 // no releases matched 1294 return PEAR::raiseError('No releases in package.xml matched the existing operating ' . 1295 'system, extensions installed, or architecture, cannot install'); 1296 } 1297 1298 /** 1299 * This is only used at install-time, after all serialization 1300 * is over. 1301 * @param string file name 1302 * @param string installed path 1303 */ 1304 function setInstalledAs($file, $path) 1305 { 1306 if ($path) { 1307 return $this->_packageInfo['filelist'][$file]['installed_as'] = $path; 1308 } 1309 unset($this->_packageInfo['filelist'][$file]['installed_as']); 1310 } 1311 1312 function getInstalledLocation($file) 1313 { 1314 if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) { 1315 return $this->_packageInfo['filelist'][$file]['installed_as']; 1316 } 1317 return false; 1318 } 1319 1320 /** 1321 * This is only used at install-time, after all serialization 1322 * is over. 1323 */ 1324 function installedFile($file, $atts) 1325 { 1326 if (isset($this->_packageInfo['filelist'][$file])) { 1327 $this->_packageInfo['filelist'][$file] = 1328 array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']); 1329 } else { 1330 $this->_packageInfo['filelist'][$file] = $atts['attribs']; 1331 } 1332 } 1333 1334 /** 1335 * Retrieve the contents tag 1336 */ 1337 function getContents() 1338 { 1339 if (isset($this->_packageInfo['contents'])) { 1340 return $this->_packageInfo['contents']; 1341 } 1342 return false; 1343 } 1344 1345 /** 1346 * @param string full path to file 1347 * @param string attribute name 1348 * @param string attribute value 1349 * @param int risky but fast - use this to choose a file based on its position in the list 1350 * of files. Index is zero-based like PHP arrays. 1351 * @return bool success of operation 1352 */ 1353 function setFileAttribute($filename, $attr, $value, $index = false) 1354 { 1355 $this->_isValid = 0; 1356 if (in_array($attr, array('role', 'name', 'baseinstalldir'))) { 1357 $this->_filesValid = false; 1358 } 1359 if ($index !== false && 1360 isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) { 1361 $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value; 1362 return true; 1363 } 1364 if (!isset($this->_packageInfo['contents']['dir']['file'])) { 1365 return false; 1366 } 1367 $files = $this->_packageInfo['contents']['dir']['file']; 1368 if (!isset($files[0])) { 1369 $files = array($files); 1370 $ind = false; 1371 } else { 1372 $ind = true; 1373 } 1374 foreach ($files as $i => $file) { 1375 if (isset($file['attribs'])) { 1376 if ($file['attribs']['name'] == $filename) { 1377 if ($ind) { 1378 $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value; 1379 } else { 1380 $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value; 1381 } 1382 return true; 1383 } 1384 } 1385 } 1386 return false; 1387 } 1388 1389 function setDirtree($path) 1390 { 1391 $this->_packageInfo['dirtree'][$path] = true; 1392 } 1393 1394 function getDirtree() 1395 { 1396 if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) { 1397 return $this->_packageInfo['dirtree']; 1398 } 1399 return false; 1400 } 1401 1402 function resetDirtree() 1403 { 1404 unset($this->_packageInfo['dirtree']); 1405 } 1406 1407 /** 1408 * Determines whether this package claims it is compatible with the version of 1409 * the package that has a recommended version dependency 1410 * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package 1411 * @return boolean 1412 */ 1413 function isCompatible($pf) 1414 { 1415 if (!isset($this->_packageInfo['compatible'])) { 1416 return false; 1417 } 1418 if (!isset($this->_packageInfo['channel'])) { 1419 return false; 1420 } 1421 $me = $pf->getVersion(); 1422 $compatible = $this->_packageInfo['compatible']; 1423 if (!isset($compatible[0])) { 1424 $compatible = array($compatible); 1425 } 1426 $found = false; 1427 foreach ($compatible as $info) { 1428 if (strtolower($info['name']) == strtolower($pf->getPackage())) { 1429 if (strtolower($info['channel']) == strtolower($pf->getChannel())) { 1430 $found = true; 1431 break; 1432 } 1433 } 1434 } 1435 if (!$found) { 1436 return false; 1437 } 1438 if (isset($info['exclude'])) { 1439 if (!isset($info['exclude'][0])) { 1440 $info['exclude'] = array($info['exclude']); 1441 } 1442 foreach ($info['exclude'] as $exclude) { 1443 if (version_compare($me, $exclude, '==')) { 1444 return false; 1445 } 1446 } 1447 } 1448 if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) { 1449 return true; 1450 } 1451 return false; 1452 } 1453 1454 /** 1455 * @return array|false 1456 */ 1457 function getCompatible() 1458 { 1459 if (isset($this->_packageInfo['compatible'])) { 1460 return $this->_packageInfo['compatible']; 1461 } 1462 return false; 1463 } 1464 1465 function getDependencies() 1466 { 1467 if (isset($this->_packageInfo['dependencies'])) { 1468 return $this->_packageInfo['dependencies']; 1469 } 1470 return false; 1471 } 1472 1473 function isSubpackageOf($p) 1474 { 1475 return $p->isSubpackage($this); 1476 } 1477 1478 /** 1479 * Determines whether the passed in package is a subpackage of this package. 1480 * 1481 * No version checking is done, only name verification. 1482 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 1483 * @return bool 1484 */ 1485 function isSubpackage($p) 1486 { 1487 $sub = array(); 1488 if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) { 1489 $sub = $this->_packageInfo['dependencies']['required']['subpackage']; 1490 if (!isset($sub[0])) { 1491 $sub = array($sub); 1492 } 1493 } 1494 if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) { 1495 $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage']; 1496 if (!isset($sub1[0])) { 1497 $sub1 = array($sub1); 1498 } 1499 $sub = array_merge($sub, $sub1); 1500 } 1501 if (isset($this->_packageInfo['dependencies']['group'])) { 1502 $group = $this->_packageInfo['dependencies']['group']; 1503 if (!isset($group[0])) { 1504 $group = array($group); 1505 } 1506 foreach ($group as $deps) { 1507 if (isset($deps['subpackage'])) { 1508 $sub2 = $deps['subpackage']; 1509 if (!isset($sub2[0])) { 1510 $sub2 = array($sub2); 1511 } 1512 $sub = array_merge($sub, $sub2); 1513 } 1514 } 1515 } 1516 foreach ($sub as $dep) { 1517 if (strtolower($dep['name']) == strtolower($p->getPackage())) { 1518 if (isset($dep['channel'])) { 1519 if (strtolower($dep['channel']) == strtolower($p->getChannel())) { 1520 return true; 1521 } 1522 } else { 1523 if ($dep['uri'] == $p->getURI()) { 1524 return true; 1525 } 1526 } 1527 } 1528 } 1529 return false; 1530 } 1531 1532 function dependsOn($package, $channel) 1533 { 1534 if (!($deps = $this->getDependencies())) { 1535 return false; 1536 } 1537 foreach (array('package', 'subpackage') as $type) { 1538 foreach (array('required', 'optional') as $needed) { 1539 if (isset($deps[$needed][$type])) { 1540 if (!isset($deps[$needed][$type][0])) { 1541 $deps[$needed][$type] = array($deps[$needed][$type]); 1542 } 1543 foreach ($deps[$needed][$type] as $dep) { 1544 $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; 1545 if (strtolower($dep['name']) == strtolower($package) && 1546 $depchannel == $channel) { 1547 return true; 1548 } 1549 } 1550 } 1551 } 1552 if (isset($deps['group'])) { 1553 if (!isset($deps['group'][0])) { 1554 $dep['group'] = array($deps['group']); 1555 } 1556 foreach ($deps['group'] as $group) { 1557 if (isset($group[$type])) { 1558 if (!is_array($group[$type])) { 1559 $group[$type] = array($group[$type]); 1560 } 1561 foreach ($group[$type] as $dep) { 1562 $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; 1563 if (strtolower($dep['name']) == strtolower($package) && 1564 $depchannel == $channel) { 1565 return true; 1566 } 1567 } 1568 } 1569 } 1570 } 1571 } 1572 return false; 1573 } 1574 1575 /** 1576 * Get the contents of a dependency group 1577 * @param string 1578 * @return array|false 1579 */ 1580 function getDependencyGroup($name) 1581 { 1582 $name = strtolower($name); 1583 if (!isset($this->_packageInfo['dependencies']['group'])) { 1584 return false; 1585 } 1586 $groups = $this->_packageInfo['dependencies']['group']; 1587 if (!isset($groups[0])) { 1588 $groups = array($groups); 1589 } 1590 foreach ($groups as $group) { 1591 if (strtolower($group['attribs']['name']) == $name) { 1592 return $group; 1593 } 1594 } 1595 return false; 1596 } 1597 1598 /** 1599 * Retrieve a partial package.xml 1.0 representation of dependencies 1600 * 1601 * a very limited representation of dependencies is returned by this method. 1602 * The <exclude> tag for excluding certain versions of a dependency is 1603 * completely ignored. In addition, dependency groups are ignored, with the 1604 * assumption that all dependencies in dependency groups are also listed in 1605 * the optional group that work with all dependency groups 1606 * @param boolean return package.xml 2.0 <dependencies> tag 1607 * @return array|false 1608 */ 1609 function getDeps($raw = false, $nopearinstaller = false) 1610 { 1611 if (isset($this->_packageInfo['dependencies'])) { 1612 if ($raw) { 1613 return $this->_packageInfo['dependencies']; 1614 } 1615 $ret = array(); 1616 $map = array( 1617 'php' => 'php', 1618 'package' => 'pkg', 1619 'subpackage' => 'pkg', 1620 'extension' => 'ext', 1621 'os' => 'os', 1622 'pearinstaller' => 'pkg', 1623 ); 1624 foreach (array('required', 'optional') as $type) { 1625 $optional = ($type == 'optional') ? 'yes' : 'no'; 1626 if (!isset($this->_packageInfo['dependencies'][$type])) { 1627 continue; 1628 } 1629 foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) { 1630 if ($dtype == 'pearinstaller' && $nopearinstaller) { 1631 continue; 1632 } 1633 if (!isset($deps[0])) { 1634 $deps = array($deps); 1635 } 1636 foreach ($deps as $dep) { 1637 if (!isset($map[$dtype])) { 1638 // no support for arch type 1639 continue; 1640 } 1641 if ($dtype == 'pearinstaller') { 1642 $dep['name'] = 'PEAR'; 1643 $dep['channel'] = 'pear.php.net'; 1644 } 1645 $s = array('type' => $map[$dtype]); 1646 if (isset($dep['channel'])) { 1647 $s['channel'] = $dep['channel']; 1648 } 1649 if (isset($dep['uri'])) { 1650 $s['uri'] = $dep['uri']; 1651 } 1652 if (isset($dep['name'])) { 1653 $s['name'] = $dep['name']; 1654 } 1655 if (isset($dep['conflicts'])) { 1656 $s['rel'] = 'not'; 1657 } else { 1658 if (!isset($dep['min']) && 1659 !isset($dep['max'])) { 1660 $s['rel'] = 'has'; 1661 $s['optional'] = $optional; 1662 } elseif (isset($dep['min']) && 1663 isset($dep['max'])) { 1664 $s['rel'] = 'ge'; 1665 $s1 = $s; 1666 $s1['rel'] = 'le'; 1667 $s['version'] = $dep['min']; 1668 $s1['version'] = $dep['max']; 1669 if (isset($dep['channel'])) { 1670 $s1['channel'] = $dep['channel']; 1671 } 1672 if ($dtype != 'php') { 1673 $s['name'] = $dep['name']; 1674 $s1['name'] = $dep['name']; 1675 } 1676 $s['optional'] = $optional; 1677 $s1['optional'] = $optional; 1678 $ret[] = $s1; 1679 } elseif (isset($dep['min'])) { 1680 if (isset($dep['exclude']) && 1681 $dep['exclude'] == $dep['min']) { 1682 $s['rel'] = 'gt'; 1683 } else { 1684 $s['rel'] = 'ge'; 1685 } 1686 $s['version'] = $dep['min']; 1687 $s['optional'] = $optional; 1688 if ($dtype != 'php') { 1689 $s['name'] = $dep['name']; 1690 } 1691 } elseif (isset($dep['max'])) { 1692 if (isset($dep['exclude']) && 1693 $dep['exclude'] == $dep['max']) { 1694 $s['rel'] = 'lt'; 1695 } else { 1696 $s['rel'] = 'le'; 1697 } 1698 $s['version'] = $dep['max']; 1699 $s['optional'] = $optional; 1700 if ($dtype != 'php') { 1701 $s['name'] = $dep['name']; 1702 } 1703 } 1704 } 1705 $ret[] = $s; 1706 } 1707 } 1708 } 1709 if (count($ret)) { 1710 return $ret; 1711 } 1712 } 1713 return false; 1714 } 1715 1716 /** 1717 * @return php|extsrc|extbin|bundle|false 1718 */ 1719 function getPackageType() 1720 { 1721 if (isset($this->_packageInfo['phprelease'])) { 1722 return 'php'; 1723 } 1724 if (isset($this->_packageInfo['extsrcrelease'])) { 1725 return 'extsrc'; 1726 } 1727 if (isset($this->_packageInfo['extbinrelease'])) { 1728 return 'extbin'; 1729 } 1730 if (isset($this->_packageInfo['bundle'])) { 1731 return 'bundle'; 1732 } 1733 return false; 1734 } 1735 1736 /** 1737 * @return array|false 1738 */ 1739 function getReleases() 1740 { 1741 $type = $this->getPackageType(); 1742 if ($type != 'bundle') { 1743 $type .= 'release'; 1744 } 1745 if ($this->getPackageType() && isset($this->_packageInfo[$type])) { 1746 return $this->_packageInfo[$type]; 1747 } 1748 return false; 1749 } 1750 1751 /** 1752 * @return array 1753 */ 1754 function getChangelog() 1755 { 1756 if (isset($this->_packageInfo['changelog'])) { 1757 return $this->_packageInfo['changelog']; 1758 } 1759 return false; 1760 } 1761 1762 function hasDeps() 1763 { 1764 return isset($this->_packageInfo['dependencies']); 1765 } 1766 1767 function getPackagexmlVersion() 1768 { 1769 return '2.0'; 1770 } 1771 1772 /** 1773 * @return array|false 1774 */ 1775 function getSourcePackage() 1776 { 1777 if (isset($this->_packageInfo['extbinrelease'])) { 1778 return array('channel' => $this->_packageInfo['srcchannel'], 1779 'package' => $this->_packageInfo['srcpackage']); 1780 } 1781 return false; 1782 } 1783 1784 function getBundledPackages() 1785 { 1786 if (isset($this->_packageInfo['bundle'])) { 1787 return $this->_packageInfo['contents']['bundledpackage']; 1788 } 1789 return false; 1790 } 1791 1792 function getLastModified() 1793 { 1794 if (isset($this->_packageInfo['_lastmodified'])) { 1795 return $this->_packageInfo['_lastmodified']; 1796 } 1797 return false; 1798 } 1799 1800 /** 1801 * Get the contents of a file listed within the package.xml 1802 * @param string 1803 * @return string 1804 */ 1805 function getFileContents($file) 1806 { 1807 if ($this->_archiveFile == $this->_packageFile) { // unpacked 1808 $dir = dirname($this->_packageFile); 1809 $file = $dir . DIRECTORY_SEPARATOR . $file; 1810 $file = str_replace(array('/', '\\'), 1811 array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file); 1812 if (file_exists($file) && is_readable($file)) { 1813 return implode('', file($file)); 1814 } 1815 } else { // tgz 1816 $tar = &new Archive_Tar($this->_archiveFile); 1817 $tar->pushErrorHandling(PEAR_ERROR_RETURN); 1818 if ($file != 'package.xml' && $file != 'package2.xml') { 1819 $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file; 1820 } 1821 $file = $tar->extractInString($file); 1822 $tar->popErrorHandling(); 1823 if (PEAR::isError($file)) { 1824 return PEAR::raiseError("Cannot locate file '$file' in archive"); 1825 } 1826 return $file; 1827 } 1828 } 1829 1830 function &getRW() 1831 { 1832 if (!class_exists('PEAR_PackageFile_v2_rw')) { 1833 require_once 'PEAR/PackageFile/v2/rw.php'; 1834 } 1835 $a = new PEAR_PackageFile_v2_rw; 1836 foreach (get_object_vars($this) as $name => $unused) { 1837 if (!isset($this->$name)) { 1838 continue; 1839 } 1840 if ($name == '_config' || $name == '_logger'|| $name == '_registry' || 1841 $name == '_stack') { 1842 $a->$name = &$this->$name; 1843 } else { 1844 $a->$name = $this->$name; 1845 } 1846 } 1847 return $a; 1848 } 1849 1850 function &getDefaultGenerator() 1851 { 1852 if (!class_exists('PEAR_PackageFile_Generator_v2')) { 1853 require_once 'PEAR/PackageFile/Generator/v2.php'; 1854 } 1855 $a = &new PEAR_PackageFile_Generator_v2($this); 1856 return $a; 1857 } 1858 1859 function analyzeSourceCode($file, $string = false) 1860 { 1861 if (!isset($this->_v2Validator) || 1862 !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) { 1863 if (!class_exists('PEAR_PackageFile_v2_Validator')) { 1864 require_once 'PEAR/PackageFile/v2/Validator.php'; 1865 } 1866 $this->_v2Validator = new PEAR_PackageFile_v2_Validator; 1867 } 1868 return $this->_v2Validator->analyzeSourceCode($file, $string); 1869 } 1870 1871 function validate($state = PEAR_VALIDATE_NORMAL) 1872 { 1873 if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) { 1874 return false; 1875 } 1876 if (!isset($this->_v2Validator) || 1877 !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) { 1878 if (!class_exists('PEAR_PackageFile_v2_Validator')) { 1879 require_once 'PEAR/PackageFile/v2/Validator.php'; 1880 } 1881 $this->_v2Validator = new PEAR_PackageFile_v2_Validator; 1882 } 1883 if (isset($this->_packageInfo['xsdversion'])) { 1884 unset($this->_packageInfo['xsdversion']); 1885 } 1886 return $this->_v2Validator->validate($this, $state); 1887 } 1888 1889 function getTasksNs() 1890 { 1891 if (!isset($this->_tasksNs)) { 1892 if (isset($this->_packageInfo['attribs'])) { 1893 foreach ($this->_packageInfo['attribs'] as $name => $value) { 1894 if ($value == 'http://pear.php.net/dtd/tasks-1.0') { 1895 $this->_tasksNs = str_replace('xmlns:', '', $name); 1896 break; 1897 } 1898 } 1899 } 1900 } 1901 return $this->_tasksNs; 1902 } 1903 1904 /** 1905 * Determine whether a task name is a valid task. Custom tasks may be defined 1906 * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task> 1907 * 1908 * Note that this method will auto-load the task class file and test for the existence 1909 * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class 1910 * PEAR_Task_mycustom_task 1911 * @param string 1912 * @return boolean 1913 */ 1914 function getTask($task) 1915 { 1916 $this->getTasksNs(); 1917 // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace 1918 $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task); 1919 $task = str_replace(' ', '/', ucwords($task)); 1920 $ps = (strtolower(substr(PHP_OS, 0, 3)) == 'win') ? ';' : ':'; 1921 foreach (explode($ps, ini_get('include_path')) as $path) { 1922 if (file_exists($path . "/PEAR/Task/$task.php")) { 1923 include_once "PEAR/Task/$task.php"; 1924 $task = str_replace('/', '_', $task); 1925 if (class_exists("PEAR_Task_$task")) { 1926 return "PEAR_Task_$task"; 1927 } 1928 } 1929 } 1930 return false; 1931 } 1932 1933 /** 1934 * Key-friendly array_splice 1935 * @param tagname to splice a value in before 1936 * @param mixed the value to splice in 1937 * @param string the new tag name 1938 */ 1939 function _ksplice($array, $key, $value, $newkey) 1940 { 1941 $offset = array_search($key, array_keys($array)); 1942 $after = array_slice($array, $offset); 1943 $before = array_slice($array, 0, $offset); 1944 $before[$newkey] = $value; 1945 return array_merge($before, $after); 1946 } 1947 1948 /** 1949 * @param array a list of possible keys, in the order they may occur 1950 * @param mixed contents of the new package.xml tag 1951 * @param string tag name 1952 * @access private 1953 */ 1954 function _insertBefore($array, $keys, $contents, $newkey) 1955 { 1956 foreach ($keys as $key) { 1957 if (isset($array[$key])) { 1958 return $array = $this->_ksplice($array, $key, $contents, $newkey); 1959 } 1960 } 1961 $array[$newkey] = $contents; 1962 return $array; 1963 } 1964 1965 /** 1966 * @param subsection of {@link $_packageInfo} 1967 * @param array|string tag contents 1968 * @param array format: 1969 * <pre> 1970 * array( 1971 * tagname => array(list of tag names that follow this one), 1972 * childtagname => array(list of child tag names that follow this one), 1973 * ) 1974 * </pre> 1975 * 1976 * This allows construction of nested tags 1977 * @access private 1978 */ 1979 function _mergeTag($manip, $contents, $order) 1980 { 1981 if (count($order)) { 1982 foreach ($order as $tag => $curorder) { 1983 if (!isset($manip[$tag])) { 1984 // ensure that the tag is set up 1985 $manip = $this->_insertBefore($manip, $curorder, array(), $tag); 1986 } 1987 if (count($order) > 1) { 1988 $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1)); 1989 return $manip; 1990 } 1991 } 1992 } else { 1993 return $manip; 1994 } 1995 if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) { 1996 $manip[$tag][] = $contents; 1997 } else { 1998 if (!count($manip[$tag])) { 1999 $manip[$tag] = $contents; 2000 } else { 2001 $manip[$tag] = array($manip[$tag]); 2002 $manip[$tag][] = $contents; 2003 } 2004 } 2005 return $manip; 2006 } 2007} 2008?> 2009