| Viewing file:  DependencyDB.php (23.62 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
<?php/**
 * PEAR_DependencyDB, advanced installed packages dependency database
 *
 * PHP versions 4 and 5
 *
 * @category   pear
 * @package    PEAR
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @version    CVS: $Id$
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
 
 /**
 * Needed for error handling
 */
 require_once 'PEAR.php';
 require_once 'PEAR/Config.php';
 
 $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array();
 /**
 * Track dependency relationships between installed packages
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Tomas V.V.Cox <cox@idec.net.com>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @version    Release: 1.9.5
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
 class PEAR_DependencyDB
 {
 // {{{ properties
 
 /**
 * This is initialized by {@link setConfig()}
 * @var PEAR_Config
 * @access private
 */
 var $_config;
 /**
 * This is initialized by {@link setConfig()}
 * @var PEAR_Registry
 * @access private
 */
 var $_registry;
 /**
 * Filename of the dependency DB (usually .depdb)
 * @var string
 * @access private
 */
 var $_depdb = false;
 /**
 * File name of the lockfile (usually .depdblock)
 * @var string
 * @access private
 */
 var $_lockfile = false;
 /**
 * Open file resource for locking the lockfile
 * @var resource|false
 * @access private
 */
 var $_lockFp = false;
 /**
 * API version of this class, used to validate a file on-disk
 * @var string
 * @access private
 */
 var $_version = '1.0';
 /**
 * Cached dependency database file
 * @var array|null
 * @access private
 */
 var $_cache;
 
 // }}}
 // {{{ & singleton()
 
 /**
 * Get a raw dependency database.  Calls setConfig() and assertDepsDB()
 * @param PEAR_Config
 * @param string|false full path to the dependency database, or false to use default
 * @return PEAR_DependencyDB|PEAR_Error
 * @static
 */
 function &singleton(&$config, $depdb = false)
 {
 $phpdir = $config->get('php_dir', null, 'pear.php.net');
 if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) {
 $a = new PEAR_DependencyDB;
 $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a;
 $a->setConfig($config, $depdb);
 $e = $a->assertDepsDB();
 if (PEAR::isError($e)) {
 return $e;
 }
 }
 
 return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir];
 }
 
 /**
 * Set up the registry/location of dependency DB
 * @param PEAR_Config|false
 * @param string|false full path to the dependency database, or false to use default
 */
 function setConfig(&$config, $depdb = false)
 {
 if (!$config) {
 $this->_config = &PEAR_Config::singleton();
 } else {
 $this->_config = &$config;
 }
 
 $this->_registry = &$this->_config->getRegistry();
 if (!$depdb) {
 $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') .
 DIRECTORY_SEPARATOR . '.depdb';
 } else {
 $this->_depdb = $depdb;
 }
 
 $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock';
 }
 // }}}
 
 function hasWriteAccess()
 {
 if (!file_exists($this->_depdb)) {
 $dir = $this->_depdb;
 while ($dir && $dir != '.') {
 $dir = dirname($dir); // cd ..
 if ($dir != '.' && file_exists($dir)) {
 if (is_writeable($dir)) {
 return true;
 }
 
 return false;
 }
 }
 
 return false;
 }
 
 return is_writeable($this->_depdb);
 }
 
 // {{{ assertDepsDB()
 
 /**
 * Create the dependency database, if it doesn't exist.  Error if the database is
 * newer than the code reading it.
 * @return void|PEAR_Error
 */
 function assertDepsDB()
 {
 if (!is_file($this->_depdb)) {
 $this->rebuildDB();
 return;
 }
 
 $depdb = $this->_getDepDB();
 // Datatype format has been changed, rebuild the Deps DB
 if ($depdb['_version'] < $this->_version) {
 $this->rebuildDB();
 }
 
 if ($depdb['_version']{0} > $this->_version{0}) {
 return PEAR::raiseError('Dependency database is version ' .
 $depdb['_version'] . ', and we are version ' .
 $this->_version . ', cannot continue');
 }
 }
 
 /**
 * Get a list of installed packages that depend on this package
 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
 * @return array|false
 */
 function getDependentPackages(&$pkg)
 {
 $data = $this->_getDepDB();
 if (is_object($pkg)) {
 $channel = strtolower($pkg->getChannel());
 $package = strtolower($pkg->getPackage());
 } else {
 $channel = strtolower($pkg['channel']);
 $package = strtolower($pkg['package']);
 }
 
 if (isset($data['packages'][$channel][$package])) {
 return $data['packages'][$channel][$package];
 }
 
 return false;
 }
 
 /**
 * Get a list of the actual dependencies of installed packages that depend on
 * a package.
 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
 * @return array|false
 */
 function getDependentPackageDependencies(&$pkg)
 {
 $data = $this->_getDepDB();
 if (is_object($pkg)) {
 $channel = strtolower($pkg->getChannel());
 $package = strtolower($pkg->getPackage());
 } else {
 $channel = strtolower($pkg['channel']);
 $package = strtolower($pkg['package']);
 }
 
 $depend = $this->getDependentPackages($pkg);
 if (!$depend) {
 return false;
 }
 
 $dependencies = array();
 foreach ($depend as $info) {
 $temp = $this->getDependencies($info);
 foreach ($temp as $dep) {
 if (
 isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) &&
 strtolower($dep['dep']['channel']) == $channel &&
 strtolower($dep['dep']['name']) == $package
 ) {
 if (!isset($dependencies[$info['channel']])) {
 $dependencies[$info['channel']] = array();
 }
 
 if (!isset($dependencies[$info['channel']][$info['package']])) {
 $dependencies[$info['channel']][$info['package']] = array();
 }
 $dependencies[$info['channel']][$info['package']][] = $dep;
 }
 }
 }
 
 return $dependencies;
 }
 
 /**
 * Get a list of dependencies of this installed package
 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
 * @return array|false
 */
 function getDependencies(&$pkg)
 {
 if (is_object($pkg)) {
 $channel = strtolower($pkg->getChannel());
 $package = strtolower($pkg->getPackage());
 } else {
 $channel = strtolower($pkg['channel']);
 $package = strtolower($pkg['package']);
 }
 
 $data = $this->_getDepDB();
 if (isset($data['dependencies'][$channel][$package])) {
 return $data['dependencies'][$channel][$package];
 }
 
 return false;
 }
 
 /**
 * Determine whether $parent depends on $child, near or deep
 * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
 * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
 */
 function dependsOn($parent, $child)
 {
 $c = array();
 $this->_getDepDB();
 return $this->_dependsOn($parent, $child, $c);
 }
 
 function _dependsOn($parent, $child, &$checked)
 {
 if (is_object($parent)) {
 $channel = strtolower($parent->getChannel());
 $package = strtolower($parent->getPackage());
 } else {
 $channel = strtolower($parent['channel']);
 $package = strtolower($parent['package']);
 }
 
 if (is_object($child)) {
 $depchannel = strtolower($child->getChannel());
 $deppackage = strtolower($child->getPackage());
 } else {
 $depchannel = strtolower($child['channel']);
 $deppackage = strtolower($child['package']);
 }
 
 if (isset($checked[$channel][$package][$depchannel][$deppackage])) {
 return false; // avoid endless recursion
 }
 
 $checked[$channel][$package][$depchannel][$deppackage] = true;
 if (!isset($this->_cache['dependencies'][$channel][$package])) {
 return false;
 }
 
 foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
 if (isset($info['dep']['uri'])) {
 if (is_object($child)) {
 if ($info['dep']['uri'] == $child->getURI()) {
 return true;
 }
 } elseif (isset($child['uri'])) {
 if ($info['dep']['uri'] == $child['uri']) {
 return true;
 }
 }
 return false;
 }
 
 if (strtolower($info['dep']['channel']) == $depchannel &&
 strtolower($info['dep']['name']) == $deppackage) {
 return true;
 }
 }
 
 foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
 if (isset($info['dep']['uri'])) {
 if ($this->_dependsOn(array(
 'uri' => $info['dep']['uri'],
 'package' => $info['dep']['name']), $child, $checked)) {
 return true;
 }
 } else {
 if ($this->_dependsOn(array(
 'channel' => $info['dep']['channel'],
 'package' => $info['dep']['name']), $child, $checked)) {
 return true;
 }
 }
 }
 
 return false;
 }
 
 /**
 * Register dependencies of a package that is being installed or upgraded
 * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2
 */
 function installPackage(&$package)
 {
 $data = $this->_getDepDB();
 unset($this->_cache);
 $this->_setPackageDeps($data, $package);
 $this->_writeDepDB($data);
 }
 
 /**
 * Remove dependencies of a package that is being uninstalled, or upgraded.
 *
 * Upgraded packages first uninstall, then install
 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have
 *        indices 'channel' and 'package'
 */
 function uninstallPackage(&$pkg)
 {
 $data = $this->_getDepDB();
 unset($this->_cache);
 if (is_object($pkg)) {
 $channel = strtolower($pkg->getChannel());
 $package = strtolower($pkg->getPackage());
 } else {
 $channel = strtolower($pkg['channel']);
 $package = strtolower($pkg['package']);
 }
 
 if (!isset($data['dependencies'][$channel][$package])) {
 return true;
 }
 
 foreach ($data['dependencies'][$channel][$package] as $dep) {
 $found      = false;
 $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']);
 $depname    = strtolower($dep['dep']['name']);
 if (isset($data['packages'][$depchannel][$depname])) {
 foreach ($data['packages'][$depchannel][$depname] as $i => $info) {
 if ($info['channel'] == $channel && $info['package'] == $package) {
 $found = true;
 break;
 }
 }
 }
 
 if ($found) {
 unset($data['packages'][$depchannel][$depname][$i]);
 if (!count($data['packages'][$depchannel][$depname])) {
 unset($data['packages'][$depchannel][$depname]);
 if (!count($data['packages'][$depchannel])) {
 unset($data['packages'][$depchannel]);
 }
 } else {
 $data['packages'][$depchannel][$depname] =
 array_values($data['packages'][$depchannel][$depname]);
 }
 }
 }
 
 unset($data['dependencies'][$channel][$package]);
 if (!count($data['dependencies'][$channel])) {
 unset($data['dependencies'][$channel]);
 }
 
 if (!count($data['dependencies'])) {
 unset($data['dependencies']);
 }
 
 if (!count($data['packages'])) {
 unset($data['packages']);
 }
 
 $this->_writeDepDB($data);
 }
 
 /**
 * Rebuild the dependency DB by reading registry entries.
 * @return true|PEAR_Error
 */
 function rebuildDB()
 {
 $depdb = array('_version' => $this->_version);
 if (!$this->hasWriteAccess()) {
 // allow startup for read-only with older Registry
 return $depdb;
 }
 
 $packages = $this->_registry->listAllPackages();
 if (PEAR::isError($packages)) {
 return $packages;
 }
 
 foreach ($packages as $channel => $ps) {
 foreach ($ps as $package) {
 $package = $this->_registry->getPackage($package, $channel);
 if (PEAR::isError($package)) {
 return $package;
 }
 $this->_setPackageDeps($depdb, $package);
 }
 }
 
 $error = $this->_writeDepDB($depdb);
 if (PEAR::isError($error)) {
 return $error;
 }
 
 $this->_cache = $depdb;
 return true;
 }
 
 /**
 * Register usage of the dependency DB to prevent race conditions
 * @param int one of the LOCK_* constants
 * @return true|PEAR_Error
 * @access private
 */
 function _lock($mode = LOCK_EX)
 {
 if (stristr(php_uname(), 'Windows 9')) {
 return true;
 }
 
 if ($mode != LOCK_UN && is_resource($this->_lockFp)) {
 // XXX does not check type of lock (LOCK_SH/LOCK_EX)
 return true;
 }
 
 $open_mode = 'w';
 // XXX People reported problems with LOCK_SH and 'w'
 if ($mode === LOCK_SH) {
 if (!file_exists($this->_lockfile)) {
 touch($this->_lockfile);
 } elseif (!is_file($this->_lockfile)) {
 return PEAR::raiseError('could not create Dependency lock file, ' .
 'it exists and is not a regular file');
 }
 $open_mode = 'r';
 }
 
 if (!is_resource($this->_lockFp)) {
 $this->_lockFp = @fopen($this->_lockfile, $open_mode);
 }
 
 if (!is_resource($this->_lockFp)) {
 return PEAR::raiseError("could not create Dependency lock file" .
 (isset($php_errormsg) ? ": " . $php_errormsg : ""));
 }
 
 if (!(int)flock($this->_lockFp, $mode)) {
 switch ($mode) {
 case LOCK_SH: $str = 'shared';    break;
 case LOCK_EX: $str = 'exclusive'; break;
 case LOCK_UN: $str = 'unlock';    break;
 default:      $str = 'unknown';   break;
 }
 
 return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)");
 }
 
 return true;
 }
 
 /**
 * Release usage of dependency DB
 * @return true|PEAR_Error
 * @access private
 */
 function _unlock()
 {
 $ret = $this->_lock(LOCK_UN);
 if (is_resource($this->_lockFp)) {
 fclose($this->_lockFp);
 }
 $this->_lockFp = null;
 return $ret;
 }
 
 /**
 * Load the dependency database from disk, or return the cache
 * @return array|PEAR_Error
 */
 function _getDepDB()
 {
 if (!$this->hasWriteAccess()) {
 return array('_version' => $this->_version);
 }
 
 if (isset($this->_cache)) {
 return $this->_cache;
 }
 
 if (!$fp = fopen($this->_depdb, 'r')) {
 $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'");
 return $err;
 }
 
 $rt = get_magic_quotes_runtime();
 set_magic_quotes_runtime(0);
 clearstatcache();
 fclose($fp);
 $data = unserialize(file_get_contents($this->_depdb));
 set_magic_quotes_runtime($rt);
 $this->_cache = $data;
 return $data;
 }
 
 /**
 * Write out the dependency database to disk
 * @param array the database
 * @return true|PEAR_Error
 * @access private
 */
 function _writeDepDB(&$deps)
 {
 if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
 return $e;
 }
 
 if (!$fp = fopen($this->_depdb, 'wb')) {
 $this->_unlock();
 return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing");
 }
 
 $rt = get_magic_quotes_runtime();
 set_magic_quotes_runtime(0);
 fwrite($fp, serialize($deps));
 set_magic_quotes_runtime($rt);
 fclose($fp);
 $this->_unlock();
 $this->_cache = $deps;
 return true;
 }
 
 /**
 * Register all dependencies from a package in the dependencies database, in essence
 * "installing" the package's dependency information
 * @param array the database
 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
 * @access private
 */
 function _setPackageDeps(&$data, &$pkg)
 {
 $pkg->setConfig($this->_config);
 if ($pkg->getPackagexmlVersion() == '1.0') {
 $gen = &$pkg->getDefaultGenerator();
 $deps = $gen->dependenciesToV2();
 } else {
 $deps = $pkg->getDeps(true);
 }
 
 if (!$deps) {
 return;
 }
 
 if (!is_array($data)) {
 $data = array();
 }
 
 if (!isset($data['dependencies'])) {
 $data['dependencies'] = array();
 }
 
 $channel = strtolower($pkg->getChannel());
 $package = strtolower($pkg->getPackage());
 
 if (!isset($data['dependencies'][$channel])) {
 $data['dependencies'][$channel] = array();
 }
 
 $data['dependencies'][$channel][$package] = array();
 if (isset($deps['required']['package'])) {
 if (!isset($deps['required']['package'][0])) {
 $deps['required']['package'] = array($deps['required']['package']);
 }
 
 foreach ($deps['required']['package'] as $dep) {
 $this->_registerDep($data, $pkg, $dep, 'required');
 }
 }
 
 if (isset($deps['optional']['package'])) {
 if (!isset($deps['optional']['package'][0])) {
 $deps['optional']['package'] = array($deps['optional']['package']);
 }
 
 foreach ($deps['optional']['package'] as $dep) {
 $this->_registerDep($data, $pkg, $dep, 'optional');
 }
 }
 
 if (isset($deps['required']['subpackage'])) {
 if (!isset($deps['required']['subpackage'][0])) {
 $deps['required']['subpackage'] = array($deps['required']['subpackage']);
 }
 
 foreach ($deps['required']['subpackage'] as $dep) {
 $this->_registerDep($data, $pkg, $dep, 'required');
 }
 }
 
 if (isset($deps['optional']['subpackage'])) {
 if (!isset($deps['optional']['subpackage'][0])) {
 $deps['optional']['subpackage'] = array($deps['optional']['subpackage']);
 }
 
 foreach ($deps['optional']['subpackage'] as $dep) {
 $this->_registerDep($data, $pkg, $dep, 'optional');
 }
 }
 
 if (isset($deps['group'])) {
 if (!isset($deps['group'][0])) {
 $deps['group'] = array($deps['group']);
 }
 
 foreach ($deps['group'] as $group) {
 if (isset($group['package'])) {
 if (!isset($group['package'][0])) {
 $group['package'] = array($group['package']);
 }
 
 foreach ($group['package'] as $dep) {
 $this->_registerDep($data, $pkg, $dep, 'optional',
 $group['attribs']['name']);
 }
 }
 
 if (isset($group['subpackage'])) {
 if (!isset($group['subpackage'][0])) {
 $group['subpackage'] = array($group['subpackage']);
 }
 
 foreach ($group['subpackage'] as $dep) {
 $this->_registerDep($data, $pkg, $dep, 'optional',
 $group['attribs']['name']);
 }
 }
 }
 }
 
 if ($data['dependencies'][$channel][$package] == array()) {
 unset($data['dependencies'][$channel][$package]);
 if (!count($data['dependencies'][$channel])) {
 unset($data['dependencies'][$channel]);
 }
 }
 }
 
 /**
 * @param array the database
 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
 * @param array the specific dependency
 * @param required|optional whether this is a required or an optional dep
 * @param string|false dependency group this dependency is from, or false for ordinary dep
 */
 function _registerDep(&$data, &$pkg, $dep, $type, $group = false)
 {
 $info = array(
 'dep'   => $dep,
 'type'  => $type,
 'group' => $group
 );
 
 $dep  = array_map('strtolower', $dep);
 $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
 if (!isset($data['dependencies'])) {
 $data['dependencies'] = array();
 }
 
 $channel = strtolower($pkg->getChannel());
 $package = strtolower($pkg->getPackage());
 
 if (!isset($data['dependencies'][$channel])) {
 $data['dependencies'][$channel] = array();
 }
 
 if (!isset($data['dependencies'][$channel][$package])) {
 $data['dependencies'][$channel][$package] = array();
 }
 
 $data['dependencies'][$channel][$package][] = $info;
 if (isset($data['packages'][$depchannel][$dep['name']])) {
 $found = false;
 foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) {
 if ($p['channel'] == $channel && $p['package'] == $package) {
 $found = true;
 break;
 }
 }
 } else {
 if (!isset($data['packages'])) {
 $data['packages'] = array();
 }
 
 if (!isset($data['packages'][$depchannel])) {
 $data['packages'][$depchannel] = array();
 }
 
 if (!isset($data['packages'][$depchannel][$dep['name']])) {
 $data['packages'][$depchannel][$dep['name']] = array();
 }
 
 $found = false;
 }
 
 if (!$found) {
 $data['packages'][$depchannel][$dep['name']][] = array(
 'channel' => $channel,
 'package' => $package
 );
 }
 }
 }
 |