[ Index ] |
|
Code source de PHP PEAR 1.4.5 |
1 <?php 2 /** 3 * PEAR_DependencyDB, advanced installed packages dependency database 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 Tomas V. V. Cox <cox@idecnet.com> 16 * @author Greg Beaver <cellog@php.net> 17 * @copyright 1997-2006 The PHP Group 18 * @license http://www.php.net/license/3_0.txt PHP License 3.0 19 * @version CVS: $Id: DependencyDB.php,v 1.35 2007/01/06 04:03:32 cellog Exp $ 20 * @link http://pear.php.net/package/PEAR 21 * @since File available since Release 1.4.0a1 22 */ 23 24 /** 25 * Needed for error handling 26 */ 27 require_once 'PEAR.php'; 28 require_once 'PEAR/Config.php'; 29 30 $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array(); 31 /** 32 * Track dependency relationships between installed packages 33 * @category pear 34 * @package PEAR 35 * @author Greg Beaver <cellog@php.net> 36 * @author Tomas V.V.Cox <cox@idec.net.com> 37 * @copyright 1997-2006 The PHP Group 38 * @license http://www.php.net/license/3_0.txt PHP License 3.0 39 * @version Release: 1.5.0 40 * @link http://pear.php.net/package/PEAR 41 * @since Class available since Release 1.4.0a1 42 */ 43 class PEAR_DependencyDB 44 { 45 // {{{ properties 46 47 /** 48 * This is initialized by {@link setConfig()} 49 * @var PEAR_Config 50 * @access private 51 */ 52 var $_config; 53 /** 54 * This is initialized by {@link setConfig()} 55 * @var PEAR_Registry 56 * @access private 57 */ 58 var $_registry; 59 /** 60 * Filename of the dependency DB (usually .depdb) 61 * @var string 62 * @access private 63 */ 64 var $_depdb = false; 65 /** 66 * File name of the lockfile (usually .depdblock) 67 * @var string 68 * @access private 69 */ 70 var $_lockfile = false; 71 /** 72 * Open file resource for locking the lockfile 73 * @var resource|false 74 * @access private 75 */ 76 var $_lockFp = false; 77 /** 78 * API version of this class, used to validate a file on-disk 79 * @var string 80 * @access private 81 */ 82 var $_version = '1.0'; 83 /** 84 * Cached dependency database file 85 * @var array|null 86 * @access private 87 */ 88 var $_cache; 89 90 // }}} 91 // {{{ & singleton() 92 93 /** 94 * Get a raw dependency database. Calls setConfig() and assertDepsDB() 95 * @param PEAR_Config 96 * @param string|false full path to the dependency database, or false to use default 97 * @return PEAR_DependencyDB|PEAR_Error 98 * @static 99 */ 100 function &singleton(&$config, $depdb = false) 101 { 102 if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] 103 [$config->get('php_dir', null, 'pear.php.net')])) { 104 $a = new PEAR_DependencyDB; 105 $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] 106 [$config->get('php_dir', null, 'pear.php.net')] = &$a; 107 $a->setConfig($config, $depdb); 108 if (PEAR::isError($e = $a->assertDepsDB())) { 109 return $e; 110 } 111 } 112 return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] 113 [$config->get('php_dir', null, 'pear.php.net')]; 114 } 115 116 /** 117 * Set up the registry/location of dependency DB 118 * @param PEAR_Config|false 119 * @param string|false full path to the dependency database, or false to use default 120 */ 121 function setConfig(&$config, $depdb = false) 122 { 123 if (!$config) { 124 $this->_config = &PEAR_Config::singleton(); 125 } else { 126 $this->_config = &$config; 127 } 128 $this->_registry = &$this->_config->getRegistry(); 129 if (!$depdb) { 130 $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') . 131 DIRECTORY_SEPARATOR . '.depdb'; 132 } else { 133 $this->_depdb = $depdb; 134 } 135 $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock'; 136 } 137 // }}} 138 139 function hasWriteAccess() 140 { 141 if (!file_exists($this->_depdb)) { 142 $dir = $this->_depdb; 143 while ($dir && $dir != '.') { 144 $dir = dirname($dir); // cd .. 145 if ($dir != '.' && file_exists($dir)) { 146 if (is_writeable($dir)) { 147 return true; 148 } else { 149 return false; 150 } 151 } 152 } 153 return false; 154 } 155 return is_writeable($this->_depdb); 156 } 157 158 // {{{ assertDepsDB() 159 160 /** 161 * Create the dependency database, if it doesn't exist. Error if the database is 162 * newer than the code reading it. 163 * @return void|PEAR_Error 164 */ 165 function assertDepsDB() 166 { 167 if (!is_file($this->_depdb)) { 168 $this->rebuildDB(); 169 } else { 170 $depdb = $this->_getDepDB(); 171 // Datatype format has been changed, rebuild the Deps DB 172 if ($depdb['_version'] < $this->_version) { 173 $this->rebuildDB(); 174 } 175 if ($depdb['_version']{0} > $this->_version{0}) { 176 return PEAR::raiseError('Dependency database is version ' . 177 $depdb['_version'] . ', and we are version ' . 178 $this->_version . ', cannot continue'); 179 } 180 } 181 } 182 183 /** 184 * Get a list of installed packages that depend on this package 185 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array 186 * @return array|false 187 */ 188 function getDependentPackages(&$pkg) 189 { 190 $data = $this->_getDepDB(); 191 if (is_object($pkg)) { 192 $channel = strtolower($pkg->getChannel()); 193 $package = strtolower($pkg->getPackage()); 194 } else { 195 $channel = strtolower($pkg['channel']); 196 $package = strtolower($pkg['package']); 197 } 198 if (isset($data['packages'][$channel][$package])) { 199 return $data['packages'][$channel][$package]; 200 } 201 return false; 202 } 203 204 /** 205 * Get a list of the actual dependencies of installed packages that depend on 206 * a package. 207 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array 208 * @return array|false 209 */ 210 function getDependentPackageDependencies(&$pkg) 211 { 212 $data = $this->_getDepDB(); 213 if (is_object($pkg)) { 214 $channel = strtolower($pkg->getChannel()); 215 $package = strtolower($pkg->getPackage()); 216 } else { 217 $channel = strtolower($pkg['channel']); 218 $package = strtolower($pkg['package']); 219 } 220 $depend = $this->getDependentPackages($pkg); 221 if (!$depend) { 222 return false; 223 } 224 $dependencies = array(); 225 foreach ($depend as $info) { 226 $temp = $this->getDependencies($info); 227 foreach ($temp as $dep) { 228 if (strtolower($dep['dep']['channel']) == strtolower($channel) && 229 strtolower($dep['dep']['name']) == strtolower($package)) { 230 if (!isset($dependencies[$info['channel']])) { 231 $dependencies[$info['channel']] = array(); 232 } 233 if (!isset($dependencies[$info['channel']][$info['package']])) { 234 $dependencies[$info['channel']][$info['package']] = array(); 235 } 236 $dependencies[$info['channel']][$info['package']][] = $dep; 237 } 238 } 239 } 240 return $dependencies; 241 } 242 243 /** 244 * Get a list of dependencies of this installed package 245 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array 246 * @return array|false 247 */ 248 function getDependencies(&$pkg) 249 { 250 if (is_object($pkg)) { 251 $channel = strtolower($pkg->getChannel()); 252 $package = strtolower($pkg->getPackage()); 253 } else { 254 $channel = strtolower($pkg['channel']); 255 $package = strtolower($pkg['package']); 256 } 257 $data = $this->_getDepDB(); 258 if (isset($data['dependencies'][$channel][$package])) { 259 return $data['dependencies'][$channel][$package]; 260 } 261 return false; 262 } 263 264 /** 265 * Determine whether $parent depends on $child, near or deep 266 * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2 267 * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2 268 */ 269 function dependsOn($parent, $child) 270 { 271 $c = array(); 272 $this->_getDepDB(); 273 return $this->_dependsOn($parent, $child, $c); 274 } 275 276 function _dependsOn($parent, $child, &$checked) 277 { 278 if (is_object($parent)) { 279 $channel = strtolower($parent->getChannel()); 280 $package = strtolower($parent->getPackage()); 281 } else { 282 $channel = strtolower($parent['channel']); 283 $package = strtolower($parent['package']); 284 } 285 if (is_object($child)) { 286 $depchannel = strtolower($child->getChannel()); 287 $deppackage = strtolower($child->getPackage()); 288 } else { 289 $depchannel = strtolower($child['channel']); 290 $deppackage = strtolower($child['package']); 291 } 292 if (isset($checked[$channel][$package][$depchannel][$deppackage])) { 293 return false; // avoid endless recursion 294 } 295 $checked[$channel][$package][$depchannel][$deppackage] = true; 296 if (!isset($this->_cache['dependencies'][$channel][$package])) { 297 return false; 298 } 299 foreach ($this->_cache['dependencies'][$channel][$package] as $info) { 300 if (isset($info['dep']['uri'])) { 301 if (is_object($child)) { 302 if ($info['dep']['uri'] == $child->getURI()) { 303 return true; 304 } 305 } elseif (isset($child['uri'])) { 306 if ($info['dep']['uri'] == $child['uri']) { 307 return true; 308 } 309 } 310 return false; 311 } 312 if (strtolower($info['dep']['channel']) == strtolower($depchannel) && 313 strtolower($info['dep']['name']) == strtolower($deppackage)) { 314 return true; 315 } 316 } 317 foreach ($this->_cache['dependencies'][$channel][$package] as $info) { 318 if (isset($info['dep']['uri'])) { 319 if ($this->_dependsOn(array( 320 'uri' => $info['dep']['uri'], 321 'package' => $info['dep']['name']), $child, $checked)) { 322 return true; 323 } 324 } else { 325 if ($this->_dependsOn(array( 326 'channel' => $info['dep']['channel'], 327 'package' => $info['dep']['name']), $child, $checked)) { 328 return true; 329 } 330 } 331 } 332 return false; 333 } 334 335 /** 336 * Register dependencies of a package that is being installed or upgraded 337 * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2 338 */ 339 function installPackage(&$package) 340 { 341 $data = $this->_getDepDB(); 342 unset($this->_cache); 343 $this->_setPackageDeps($data, $package); 344 $this->_writeDepDB($data); 345 } 346 347 /** 348 * Remove dependencies of a package that is being uninstalled, or upgraded. 349 * 350 * Upgraded packages first uninstall, then install 351 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have 352 * indices 'channel' and 'package' 353 */ 354 function uninstallPackage(&$pkg) 355 { 356 $data = $this->_getDepDB(); 357 unset($this->_cache); 358 if (is_object($pkg)) { 359 $channel = strtolower($pkg->getChannel()); 360 $package = strtolower($pkg->getPackage()); 361 } else { 362 $channel = strtolower($pkg['channel']); 363 $package = strtolower($pkg['package']); 364 } 365 if (!isset($data['dependencies'][$channel][$package])) { 366 return true; 367 } 368 foreach ($data['dependencies'][$channel][$package] as $dep) { 369 $found = false; 370 if (isset($dep['dep']['uri'])) { 371 $depchannel = '__uri'; 372 } else { 373 $depchannel = strtolower($dep['dep']['channel']); 374 } 375 if (isset($data['packages'][$depchannel][strtolower($dep['dep']['name'])])) { 376 foreach ($data['packages'][$depchannel][strtolower($dep['dep']['name'])] as 377 $i => $info) { 378 if ($info['channel'] == $channel && 379 $info['package'] == $package) { 380 $found = true; 381 break; 382 } 383 } 384 } 385 if ($found) { 386 unset($data['packages'][$depchannel][strtolower($dep['dep']['name'])][$i]); 387 if (!count($data['packages'][$depchannel][strtolower($dep['dep']['name'])])) { 388 unset($data['packages'][$depchannel][strtolower($dep['dep']['name'])]); 389 if (!count($data['packages'][$depchannel])) { 390 unset($data['packages'][$depchannel]); 391 } 392 } else { 393 $data['packages'][$depchannel][strtolower($dep['dep']['name'])] = 394 array_values( 395 $data['packages'][$depchannel][strtolower($dep['dep']['name'])]); 396 } 397 } 398 } 399 unset($data['dependencies'][$channel][$package]); 400 if (!count($data['dependencies'][$channel])) { 401 unset($data['dependencies'][$channel]); 402 } 403 if (!count($data['dependencies'])) { 404 unset($data['dependencies']); 405 } 406 if (!count($data['packages'])) { 407 unset($data['packages']); 408 } 409 $this->_writeDepDB($data); 410 } 411 412 /** 413 * Rebuild the dependency DB by reading registry entries. 414 * @return true|PEAR_Error 415 */ 416 function rebuildDB() 417 { 418 $depdb = array('_version' => $this->_version); 419 if (!$this->hasWriteAccess()) { 420 // allow startup for read-only with older Registry 421 return $depdb; 422 } 423 $packages = $this->_registry->listAllPackages(); 424 foreach ($packages as $channel => $ps) { 425 foreach ($ps as $package) { 426 $package = $this->_registry->getPackage($package, $channel); 427 $this->_setPackageDeps($depdb, $package); 428 } 429 } 430 $error = $this->_writeDepDB($depdb); 431 if (PEAR::isError($error)) { 432 return $error; 433 } 434 $this->_cache = $depdb; 435 return true; 436 } 437 438 /** 439 * Register usage of the dependency DB to prevent race conditions 440 * @param int one of the LOCK_* constants 441 * @return true|PEAR_Error 442 * @access private 443 */ 444 function _lock($mode = LOCK_EX) 445 { 446 if (!eregi('Windows 9', php_uname())) { 447 if ($mode != LOCK_UN && is_resource($this->_lockFp)) { 448 // XXX does not check type of lock (LOCK_SH/LOCK_EX) 449 return true; 450 } 451 $open_mode = 'w'; 452 // XXX People reported problems with LOCK_SH and 'w' 453 if ($mode === LOCK_SH) { 454 if (!file_exists($this->_lockfile)) { 455 touch($this->_lockfile); 456 } elseif (!is_file($this->_lockfile)) { 457 return PEAR::raiseError('could not create Dependency lock file, ' . 458 'it exists and is not a regular file'); 459 } 460 $open_mode = 'r'; 461 } 462 463 if (!is_resource($this->_lockFp)) { 464 $this->_lockFp = @fopen($this->_lockfile, $open_mode); 465 } 466 if (!is_resource($this->_lockFp)) { 467 return PEAR::raiseError("could not create Dependency lock file" . 468 (isset($php_errormsg) ? ": " . $php_errormsg : "")); 469 } 470 if (!(int)flock($this->_lockFp, $mode)) { 471 switch ($mode) { 472 case LOCK_SH: $str = 'shared'; break; 473 case LOCK_EX: $str = 'exclusive'; break; 474 case LOCK_UN: $str = 'unlock'; break; 475 default: $str = 'unknown'; break; 476 } 477 return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)"); 478 } 479 } 480 return true; 481 } 482 483 /** 484 * Release usage of dependency DB 485 * @return true|PEAR_Error 486 * @access private 487 */ 488 function _unlock() 489 { 490 $ret = $this->_lock(LOCK_UN); 491 if (is_resource($this->_lockFp)) { 492 fclose($this->_lockFp); 493 } 494 $this->_lockFp = null; 495 return $ret; 496 } 497 498 /** 499 * Load the dependency database from disk, or return the cache 500 * @return array|PEAR_Error 501 */ 502 function _getDepDB() 503 { 504 if (!$this->hasWriteAccess()) { 505 return array('_version' => $this->_version); 506 } 507 if (isset($this->_cache)) { 508 return $this->_cache; 509 } 510 if (!$fp = fopen($this->_depdb, 'r')) { 511 $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'"); 512 return $err; 513 } 514 $rt = get_magic_quotes_runtime(); 515 set_magic_quotes_runtime(0); 516 clearstatcache(); 517 fclose($fp); 518 $data = unserialize(file_get_contents($this->_depdb)); 519 set_magic_quotes_runtime($rt); 520 $this->_cache = $data; 521 return $data; 522 } 523 524 /** 525 * Write out the dependency database to disk 526 * @param array the database 527 * @return true|PEAR_Error 528 * @access private 529 */ 530 function _writeDepDB(&$deps) 531 { 532 if (PEAR::isError($e = $this->_lock(LOCK_EX))) { 533 return $e; 534 } 535 if (!$fp = fopen($this->_depdb, 'wb')) { 536 $this->_unlock(); 537 return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing"); 538 } 539 $rt = get_magic_quotes_runtime(); 540 set_magic_quotes_runtime(0); 541 fwrite($fp, serialize($deps)); 542 set_magic_quotes_runtime($rt); 543 fclose($fp); 544 $this->_unlock(); 545 $this->_cache = $deps; 546 return true; 547 } 548 549 /** 550 * Register all dependencies from a package in the dependencies database, in essence 551 * "installing" the package's dependency information 552 * @param array the database 553 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 554 * @access private 555 */ 556 function _setPackageDeps(&$data, &$pkg) 557 { 558 $pkg->setConfig($this->_config); 559 if ($pkg->getPackagexmlVersion() == '1.0') { 560 $gen = &$pkg->getDefaultGenerator(); 561 $deps = $gen->dependenciesToV2(); 562 } else { 563 $deps = $pkg->getDeps(true); 564 } 565 if (!$deps) { 566 return; 567 } 568 if (!is_array($data)) { 569 $data = array(); 570 } 571 if (!isset($data['dependencies'])) { 572 $data['dependencies'] = array(); 573 } 574 if (!isset($data['dependencies'][strtolower($pkg->getChannel())])) { 575 $data['dependencies'][strtolower($pkg->getChannel())] = array(); 576 } 577 $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())] 578 = array(); 579 if (isset($deps['required']['package'])) { 580 if (!isset($deps['required']['package'][0])) { 581 $deps['required']['package'] = array($deps['required']['package']); 582 } 583 foreach ($deps['required']['package'] as $dep) { 584 $this->_registerDep($data, $pkg, $dep, 'required'); 585 } 586 } 587 if (isset($deps['optional']['package'])) { 588 if (!isset($deps['optional']['package'][0])) { 589 $deps['optional']['package'] = array($deps['optional']['package']); 590 } 591 foreach ($deps['optional']['package'] as $dep) { 592 $this->_registerDep($data, $pkg, $dep, 'optional'); 593 } 594 } 595 if (isset($deps['required']['subpackage'])) { 596 if (!isset($deps['required']['subpackage'][0])) { 597 $deps['required']['subpackage'] = array($deps['required']['subpackage']); 598 } 599 foreach ($deps['required']['subpackage'] as $dep) { 600 $this->_registerDep($data, $pkg, $dep, 'required'); 601 } 602 } 603 if (isset($deps['optional']['subpackage'])) { 604 if (!isset($deps['optional']['subpackage'][0])) { 605 $deps['optional']['subpackage'] = array($deps['optional']['subpackage']); 606 } 607 foreach ($deps['optional']['subpackage'] as $dep) { 608 $this->_registerDep($data, $pkg, $dep, 'optional'); 609 } 610 } 611 if (isset($deps['group'])) { 612 if (!isset($deps['group'][0])) { 613 $deps['group'] = array($deps['group']); 614 } 615 foreach ($deps['group'] as $group) { 616 if (isset($group['package'])) { 617 if (!isset($group['package'][0])) { 618 $group['package'] = array($group['package']); 619 } 620 foreach ($group['package'] as $dep) { 621 $this->_registerDep($data, $pkg, $dep, 'optional', 622 $group['attribs']['name']); 623 } 624 } 625 if (isset($group['subpackage'])) { 626 if (!isset($group['subpackage'][0])) { 627 $group['subpackage'] = array($group['subpackage']); 628 } 629 foreach ($group['subpackage'] as $dep) { 630 $this->_registerDep($data, $pkg, $dep, 'optional', 631 $group['attribs']['name']); 632 } 633 } 634 } 635 } 636 if ($data['dependencies'][strtolower($pkg->getChannel())] 637 [strtolower($pkg->getPackage())] == array()) { 638 unset($data['dependencies'][strtolower($pkg->getChannel())] 639 [strtolower($pkg->getPackage())]); 640 if (!count($data['dependencies'][strtolower($pkg->getChannel())])) { 641 unset($data['dependencies'][strtolower($pkg->getChannel())]); 642 } 643 } 644 } 645 646 /** 647 * @param array the database 648 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 649 * @param array the specific dependency 650 * @param required|optional whether this is a required or an optional dep 651 * @param string|false dependency group this dependency is from, or false for ordinary dep 652 */ 653 function _registerDep(&$data, &$pkg, $dep, $type, $group = false) 654 { 655 $info = array( 656 'dep' => $dep, 657 'type' => $type, 658 'group' => $group); 659 660 if (isset($dep['channel'])) { 661 $depchannel = $dep['channel']; 662 } else { 663 $depchannel = '__uri'; 664 } 665 if (!isset($data['dependencies'])) { 666 $data['dependencies'] = array(); 667 } 668 if (!isset($data['dependencies'][strtolower($pkg->getChannel())])) { 669 $data['dependencies'][strtolower($pkg->getChannel())] = array(); 670 } 671 if (!isset($data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())])) { 672 $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())] = array(); 673 } 674 $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())][] 675 = $info; 676 if (isset($data['packages'][strtolower($depchannel)][strtolower($dep['name'])])) { 677 $found = false; 678 foreach ($data['packages'][strtolower($depchannel)][strtolower($dep['name'])] 679 as $i => $p) { 680 if ($p['channel'] == strtolower($pkg->getChannel()) && 681 $p['package'] == strtolower($pkg->getPackage())) { 682 $found = true; 683 break; 684 } 685 } 686 if (!$found) { 687 $data['packages'][strtolower($depchannel)][strtolower($dep['name'])][] 688 = array('channel' => strtolower($pkg->getChannel()), 689 'package' => strtolower($pkg->getPackage())); 690 } 691 } else { 692 if (!isset($data['packages'])) { 693 $data['packages'] = array(); 694 } 695 if (!isset($data['packages'][strtolower($depchannel)])) { 696 $data['packages'][strtolower($depchannel)] = array(); 697 } 698 if (!isset($data['packages'][strtolower($depchannel)][strtolower($dep['name'])])) { 699 $data['packages'][strtolower($depchannel)][strtolower($dep['name'])] = array(); 700 } 701 $data['packages'][strtolower($depchannel)][strtolower($dep['name'])][] 702 = array('channel' => strtolower($pkg->getChannel()), 703 'package' => strtolower($pkg->getPackage())); 704 } 705 } 706 } 707 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 14:08:00 2007 | par Balluche grâce à PHPXref 0.7 |