PageRenderTime 48ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/includes/pear/PEAR/DependencyDB.php

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