Location: PHPKode > projects > EPESI > epesi-1.3.0-rev9190/include/module_manager.php
<?php
/**
 * @author Paul Bukowski <hide@address.com>
 * @version 1.0
 * @copyright Copyright &copy; 2007, Telaxus LLC
 * @license SPL
 * @package epesi-base
 */
defined("_VALID_ACCESS") || die('Direct access forbidden');

/**
 * This class provides methods for module manipulations
 * @package epesi-base
 * @subpackage module
 */
class ModuleManager {
	public static $not_loaded_modules = null;
	public static $loaded_modules = array();
	public static $modules = array();
	public static $modules_install = array();
	public static $modules_common = array();
	public static $root = array();
	private static $processing = array();
	private static $processed_modules = array('install'=>array(),'downgrade'=>array(),'upgrade'=>array(),'uninstall'=>array());

	/**
	 * Includes file with module installation class.
	 *
	 * Do not use directly.
	 *
	 * @param string $module_class_name module class name - underscore separated
	 */
	public static final function include_install($module_class_name) {
		if(isset(self::$modules_install[$module_class_name])) return;
		$path = self::get_module_dir_path($module_class_name);
		$file = self::get_module_file_name($module_class_name);
		ob_start();
		require_once ('modules/' . $path . '/' . $file . 'Install.php');
		ob_end_clean();
		$x = $module_class_name.'Install';
		if(!(class_exists($x) && in_array($x, get_declared_classes())) || !array_key_exists('ModuleInstall',class_parents($x)))
			trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);
		self::$modules_install[$module_class_name] = new $x($module_class_name);
	}

	/**
	 * Includes file with module common class.
	 *
	 * Do not use directly.
	 *
	 * @param string module name
	 */
	public static final function include_common($class_name,$version) {
		$path = self::get_module_dir_path($class_name);
		$file = self::get_module_file_name($class_name);
		$file_url = 'modules/' . $path . '/' . $file . 'Common_'.$version.'.php';
		if(file_exists($file_url)) {
			ob_start();
			require_once ($file_url);
			ob_end_clean();
			$x = $class_name.'Common';
			if(class_exists($x)) {
				if(!array_key_exists('ModuleCommon',class_parents($x)))
					trigger_error('Module '.$path.': Common class should extend ModuleCommon class.',E_USER_ERROR);
				call_user_func(array($class_name.'Common','Instance'),$class_name);
			}
			return true;
		}
		return false;
	}

	/**
	 * Includes file with module main class.
	 *
	 * Do not use directly.
	 *
	 * @param string module name
	 */
	public static final function include_main($class_name, $version) {
		if(class_exists($class_name)) return;
		$path = self::get_module_dir_path($class_name);
		$file = self::get_module_file_name($class_name);
		$file_url = 'modules/' . $path . '/' . $file . '_'.$version.'.php';
		if( file_exists($file_url) ) {
			ob_start();
			require_once ($file_url);
			ob_end_clean();
			if(!class_exists($class_name) || !array_key_exists('Module',class_parents($class_name)))
				trigger_error('Module '.$path.': Invalid main file',E_USER_ERROR);
			return true;
		}
		return false;
	}

	/**
	 * Creates array of installed modules indexed by priority of loading, based on dependencies.
	 *
	 * Do not use directly.
	 *
	 * @return array array containing information about modules priorities
	 */
	public static final function create_load_priority_array() {
		$queue = array();
		$virtual_modules = array(); //virtually loaded modules
		$priority = array();
		$installed_modules = ModuleManager::get_load_priority_array(true);

		foreach($installed_modules as $v) {
			$module_to_load = $v['name'];
			$version = $v['version'];
			$deps = self :: check_dependencies($module_to_load, $version, $virtual_modules);

			if(!empty($deps)) {
				$queue[] = array('name'=>$module_to_load,'version'=>$version);
				continue;
			}

			$priority[] = $module_to_load;
			self :: register($module_to_load, $version, $virtual_modules);

			//queue
			$registered = true;
			while($registered) {
				$registered = false;
				foreach($queue as $k=>$m) {
					$deps = self :: check_dependencies($m['name'], $m['version'], $virtual_modules);
					if(empty($deps)) {
						$priority[] = $m['name'];
						unset($queue[$k]);
						self :: register($m['name'], $m['version'], $virtual_modules);
						$registered = true;
					}
				}
			}
		}
		if(!empty($queue)) {
			$x = 'Modules deps not satisfied: ';
			foreach($queue as $k=>$m) {
				$deps = self :: check_dependencies($m['name'], $m['version'], $virtual_modules);
				$yyy = array();
				foreach($deps as $xxx) {
					$yyy[] = $xxx['name'].' ['.$xxx['version'].']';
				}
				$x .= $m['name'].' ['.$m['version'].'] ('.implode(',',$yyy).'), ';
			}
			$x .= '<br>';
			trigger_error($x,E_USER_ERROR);
		}
		foreach($priority as $k=>$v)
			DB::Execute('UPDATE modules SET priority=%d WHERE name=%s',array($k,$v));
	}

	/**
	 * Check dependencies and return array of unsatisfied dependencies.
	 *
	 * This function is called when installing modules.
	 * Should not be used directly.
	 *
	 * @param string module to check if all requirements are satisifed
	 * @param integer module version to check
	 * @param array table with loaded modules
	 * @return array
	 */
	private static final function check_dependencies($module_to_check, $version, & $module_table) {
		self::include_install($module_to_check);

		$func = array (
			self::$modules_install[$module_to_check],
			'requires'
		);
		if(!is_callable($func)) return array();
		$req_mod = call_user_func($func,$version);

		$ret = array();

		foreach ($req_mod as $m) {
			$m['name'] = str_replace('/','_',$m['name']);
			if (!array_key_exists($m['name'], $module_table) || $module_table[$m['name']]['version']<$m['version'])
				$ret[] = $m;
		}

		return $ret;
	}

	private static function satisfy_dependencies($module_to_install,$version,$check=null) {
		self::$processing[$module_to_install] = $version;
		try {
			$deps = self :: check_dependencies($module_to_install, $version, self::$modules);
			while(!empty($deps)) {
				$m = $deps[0];
				if(isset(self::$processing[$m['name']]))
					throw new Exception('Cross dependencies: '.$module_to_install);

				if (!self :: exists($m['name'],$m['version']))
					throw new Exception('Module not found: ' . '<b>' . $m['name'] . '</b>' . ' version='.$m['version']);

				print('Inst/Up required module: ' . '<b>' . $m['name'] . '</b>' . ' version='.$m['version'].' by ' . '<b>' . $module_to_install . '</b>' . '<br>');
				if(self :: is_installed($m['name'])<0){
					if (!self :: install($m['name'], $m['version'],$check))
						return false;
				} else {
					if (!self :: upgrade($m['name'], $m['version'])) return false;
				}
				$deps = self :: check_dependencies($module_to_install, $version, self::$modules);
			}
		} catch (Exception $e) {
			print ($e->getMessage().'<br>');
			return false;
		}
		unset(self::$processing[$module_to_install]);
		return true;
	}

	/**
	 * Returns directory path of the module. Also this is module name.
	 *
	 * @param string $module module name or class name - both / and _ separated
	 * @return string directory path of the module without modules/ prefix
	 */
	public static final function get_module_dir_path($module) {
		return str_replace('_', '/',$module);
	}

	/**
	 * Returns main filename part of the module.
	 *
	 * Module class named Base_Box
	 * get_module_file_name returns 'Box'
	 * @param string $module module name or class name. 
	 * @return string final portion of module name
	 */
	public static final function get_module_file_name($module) {
        $module_class_name = self::get_module_class_name($module);
		$ret = strrchr($module_class_name,'_');
		return ($ret)? substr($ret,1):$module_class_name;
	}

	/**
	 * Creates list of modules currently available to install along with list of available versions.
	 *
	 * @return array array built as follows: array('Box'=>array(0,1)...)
	 */
	public static final function list_modules() {
		$dirs = dir_tree('modules',array('theme','lang','help'));
		$ret = array();
		foreach($dirs as $d) {
			$module = str_replace('/','_',substr($d,8,-1));
			$file = self::get_module_file_name($module);

			if(!file_exists($d . $file . 'Install.php'))
				continue;

			self::include_install($module);
			$version_f = array(self::$modules_install[$module],'version');
			if(is_callable($version_f))
				$version_ret = call_user_func($version_f);
			else
				$version_ret = 0;
			$version_arr = array();
			if(is_array($version_ret)) {
				$version_arr = $version_ret;
				$version = count($version_ret);
			} else {
				$version = intval($version_ret);
				for($i=0; $i<=$version; $i++)
					$version_arr[] = $i;
			}

			$ret[$module] = $version_arr;
		}
		return $ret;
	}

	/**
	 * Check if module install file exists
	 * @param string $module module name or module class name - both / and _ separated.
	 * @return bool true if file was found, false otherwise
	 */
	public static final function exists($module) {
		$path = self::get_module_dir_path($module);
		$file = self::get_module_file_name($module);
		return file_exists('modules/' . $path . '/' . $file . 'Install.php');
	}

	/**
	 * Registers module passed as first parameter in array passed as third parameter.
	 * It is used to mark in an array that module is loaded and provides some external modules.
	 *
	 * Do not use directly.
	 *
	 * @param string $module_class_name module class name - underscore separated.
	 * @param integer $version module version
	 * @param array modules list
	 */
	public static final function register($module_class_name, $version, & $module_table) {
		$module_table[$module_class_name] = $version;
	}

	/**
	 * Unregisters module passed as first parameter from array passed as third parameter.
	 * It is used to mark in an array that module is not loaded and doesn't provide some external modules anymore.
	 *
	 * Do not use directly.
	 *
	 * @param string $module_class_name module class name - underscore separated.
	 * @param integer module version
	 * @param array modules list
	 */
	public static final function unregister($module_class_name, & $module_table) {
		unset($module_table[$module_class_name]);
	}

	/**
	 * Checks if module is installed.
	 *
	 * @param string $module module name or module class name - both / and _ separated.
	 * @return integer version of installed module or -1 when it's not installed
	 */
	public static final function is_installed($module) {
        $module_class_name = self::get_module_class_name($module);
        if (isset(self::$modules) && array_key_exists($module_class_name, self::$modules))
            return self::$modules[$module_class_name];
        return -1;
    }

	/**
	 * This function performs upgrade process when it is requested by Setup module.
	 *
	 * Do not use directly.
	 *
	 * @param string module name
	 * @param int version to which module should be upgraded
	 * @return bool true is module was upgraded sucesfully, false otherwise
	 */
	public static final function upgrade($module,$to_version) {

		$installed_version = self::is_installed($module);
		if($installed_version>=$to_version) {
			print('Upgrading module \''.$module.'\' to version '.$to_version.': There is already installed version '.$installed_version.'<br>');
			return false;
		}
		if($installed_version==-1) {
			print('Upgrading module \''.$module.'\' to version '.$to_version.': module is not installed, please install it first.<br>');
			return false;
		}
		if (!self :: exists($module,$to_version)) {
			print('Upgrading module \''.$module.'\' to version '.$to_version.': specified version of module is missing, please download it first.<br>');
			return false;
		}

		self::include_install($module);

		for($i=$installed_version+1; $i<=$to_version; $i++) {
			$up_func = array(self::$modules_install[$module], 'upgrade_'.$i);
			if(!self::satisfy_dependencies($module,$i) || (is_callable($up_func)
				&& !call_user_func($up_func))) {
				print('Upgrading module \''.$module.'\' to version '.$to_version.': upgrade to version '.$i.' failed, calling downgrade to revert changes<br>');
				call_user_func(array(self::$modules_install[$module], 'downgrade_'.$i));
				print('Upgrading module \''.$module.'\' to version '.$to_version.': downgraded to version '.($i-1).'<br>');
				break;
			}
		}

		$i--;

		if(!DB::Execute('UPDATE modules SET version=%d WHERE name=%s',array($i,$module))) {
			print('Upgrading module \''.$module.'\' to version '.$to_version.': unable to update database<br>');
			return false;
		}

		self::register($module,$to_version,self::$modules);

		self::create_load_priority_array();
		self::create_common_cache();

		self::$processed_modules['upgrade'][$module] = $to_version;
		if($i==$to_version)	{
			if(DEBUG)
				print('Module '.$module.' succesfully upgraded to version '.$to_version.'<br>');
			return true;
		}
		return false;
	}

	/**
	 * This function performs downgrade process when it is requested by Setup module.
	 *
	 * Do not use directly.
	 *
	 * @param string module name
	 * @param int version to which module should be downgraded
	 * @return bool true if module was downgraded sucesfully, false otherwise
	 */
	public static final function downgrade($module,$to_version) {
		$installed_version = self::is_installed($module);
		if($installed_version<$to_version) {
			print('Downgrading module \''.$module.'\' to version '.$to_version.': There is already installed version '.$installed_version.'<br>');
			return false;
		}
		if($installed_version==-1) {
			print('Downgrading module \''.$module.'\' to version '.$to_version.': module not installed.<br>');
			return false;
		}
		if (!self :: exists($module,$to_version)) {
			print('Downgrading module \''.$module.'\' to version '.$to_version.': specified version of module is missing, please download it first.<br>');
			return false;
		}

		self::include_install($module);

		//check if any other module requires this one....
		foreach(self::$modules as $k=>$k_version) {
			if($k==$module) continue;

			$func = array (
				self::$modules_install[$k],
				'requires'
			);
			if(!is_callable($func)) continue;
			$req_mod = call_user_func($func,$k_version);

			foreach($req_mod as $req)
				if($req['name']==$module && $req['version']>$to_version) {
					print('Downgrading module \''.$module.'\' to version '.$to_version.': module '.$k.' requires this module at least in version '.$req['version'].' !<br>');
					return false;
				}
		}

		//go
		for($i=$installed_version; $i>$to_version; $i--) {
			$down_func = array(self::$modules_install[$module], 'downgrade_'.$i);
			if(!self::satisfy_dependencies($module,$i) || (is_callable($down_func)
				&& !call_user_func($down_func))) {
				print('Downgrading module \''.$module.'\' to version '.$to_version.' from '.$i.' failed.<br>');
				break;
			}
		}

		if(!DB::Execute('UPDATE modules SET version=%d WHERE name=%s',array($i,$module))) {
			print('Downgrading module \''.$module.'\' to version '.$to_version.' failed: unable to update database<br>');
			return false;
		}

		self::create_load_priority_array();
		self::create_common_cache();

		print('Module '.$module.' succesfully downgraded to version '.$to_version.'<br>');
		self::$processed_modules['downgrade'][$module] = $to_version;
		return true;
	}
    
    public static final function get_module_class_name($module) {
        return str_replace('/', '_', $module);
    }

	/**
	 * Installs module given as first parameter.
	 * Additionally, this function calls upgrade to version given as second parameter.
	 *
	 * @param string $module module name or module class name - both / and _ separated.
	 * @param integer $version module version
	 * @return bool true if installation success, false otherwise
	 */
	public static final function install($module, $version=null, $check=null, $include_common=true) {
		if($check===null) {
			if(defined('UPDATING_EPESI'))
				$check=false;
			else
				$check=true;
		}
		$debug = '<div class="green" style="text-align: left;">';

        $module_class_name = self::get_module_class_name($module);
        
		//already installed?
		$debug .= '<b>' . $module_class_name . '</b>' .': is installed?<br>';

		self::include_install($module_class_name);

		$func_version = array(self::$modules_install[$module_class_name], 'version');
		if(is_callable($func_version))
			$inst_ver = call_user_func($func_version);
		else
			$inst_ver = 0;
		if(is_array($inst_ver)) $inst_ver = count($inst_ver);
			else $inst_ver = intval($inst_ver);

		if(!isset($version)) {
			$version = $inst_ver-1;
		} else {
			if($inst_ver<$version) {
				print($debug.'Module ' . '<b>' . $module_class_name . '</b>' .' is too old. Please download newer version<br>');
				return false;
			}
		}

		if(self::is_installed($module_class_name)>=$version)
			return true;

		if (!self :: exists($module_class_name,$version))
			return false;

		//check dependecies
		if(!self::satisfy_dependencies($module_class_name,$version,$check)) {
			print($debug.'<b>' . $module_class_name . '</b>' . ': dependencies not satisfied.<br>');
			return false;
		}

		$debug .= '<b>' . $module_class_name . '</b>' . ': calling install method<br>';
        if(preg_match('/^mysql/i',DATABASE_DRIVER))
            DB::Execute('SET FOREIGN_KEY_CHECKS = 0');
		//call install script and fill database
		if(!call_user_func(array (
			self::$modules_install[$module_class_name],
			'install'
		))) {
			$debug .= '<b>' . $module_class_name . '</b>' . ': failed install, calling uninstall<br>';
			call_user_func(array (
				self::$modules_install[$module_class_name],
				'uninstall'
			));
			Acl::del_aco_section($module_class_name);
			self::remove_data_dir($module_class_name);
			print($debug.'<b>' . $module_class_name . '</b>' . ': uninstalled<br>');
            if(preg_match('/^mysql/i',DATABASE_DRIVER))
                DB::Execute('SET FOREIGN_KEY_CHECKS = 1');
			return false;
		}
        if(preg_match('/^mysql/i',DATABASE_DRIVER))
            DB::Execute('SET FOREIGN_KEY_CHECKS = 1');

		$debug .= '<b>' . $module_class_name . '</b>' . ': registering<br>';
		$ret = DB::Execute('insert into modules(name, version) values(%s,0)', $module_class_name);
		if (!$ret) {
			print ($debug.'<b>' . $module_class_name . '</b>' . ' module installation failed: database<br>');
			return false;
		}

		self::register($module_class_name, $version, self::$modules);

        PatchUtil::mark_applied($module_class_name);

		if ($check) {
			$debug .= '<b>' . $module_class_name . '</b>' . ': rewriting priorities<br>';
			self::create_load_priority_array();
		}

		print ('<b>' . $module_class_name . '</b>' . ' module installed!<br>');

		if($version!=0) {
			$debug .= '<b>' . $module_class_name . '</b>' . ': upgrades...<br>';
			$up = self::upgrade($module_class_name, $version);
			if(!$up) {
				print($debug);
				return false;
			}
		}
		self::$not_loaded_modules[] = array('name'=>$module_class_name,'version'=>$version);

		//$debug .= '<b>' . $module_to_install . '</b>' . ': deps ok, including common class<br>';
		if($include_common) {
            self::include_common($module_class_name,$version);
//    		self::create_common_cache();
        }
        if(file_exists(DATA_DIR.'/cache/common.php')) unlink(DATA_DIR.'/cache/common.php');

		//$debug .= '</div>';
		self::$processed_modules['install'][$module_class_name] = $version;
		return true;

	}

	/**
	 * Uninstalls module.
	 *
	 * @param string module name
	 * @return bool true if uninstallation success, false otherwise
	 */
	public static final function uninstall($module_to_uninstall) {
		$installed_version = self::is_installed($module_to_uninstall);
		if ($installed_version<0) {
			print($module_to_uninstall . ' module not installed<br>');
			return false;
		}

		self::include_install($module_to_uninstall);

		foreach (self::$modules as $name => $version) { //for each module
			if ($name == $module_to_uninstall)
				continue;

			self::include_install($name);
			$required = call_user_func(array (
				self::$modules_install[$name],
				'requires'
				),$version);

			foreach ($required as $req_mod) { //for each dependency of that module
				$req_mod['name'] = str_replace('/','_',$req_mod['name']);
				if ($req_mod['name'] == $module_to_uninstall) {
					print ($module_to_uninstall . ' module is required by ' . $name . ' module! You have to uninstall ' . $name . ' first.<br>');
					return false;
				}
			}
		}

		if($installed_version>0 && !self::downgrade($module_to_uninstall, 0))
			return false;

		if(!call_user_func(array (
			self::$modules_install[$module_to_uninstall],
			'uninstall'
		))) return false;

		Acl::del_aco_section(self::$modules_install[$module_to_uninstall]->get_type());

		$ret = DB::Execute('DELETE FROM modules WHERE name=%s', $module_to_uninstall);
		if(!$ret) {
			print ($module_to_uninstall . " module uninstallation failed: database<br>");
			return false;
		}

		self::unregister($module_to_uninstall,self::$modules);

		if (!self::remove_data_dir($module_to_uninstall)){
			print ($module_to_uninstall . " module uninstallation failed: data directory remove<br>");
			return false;
		}

		self::create_load_priority_array();
//		self::create_common_cache();
        if(file_exists(DATA_DIR.'/cache/common.php')) unlink(DATA_DIR.'/cache/common.php');

		print ($module_to_uninstall . " module uninstalled! You can safely remove module directory.<br>");
		self::$processed_modules['uninstall'][$module_to_uninstall] = -1;
		return true;
	}

	public static function get_processed_modules() {
		return self::$processed_modules;
	}

	/**
	 * Returns an array of installed modules indexed by priority of loading, based on dependencies.
	 *
	 * Do not use directly.
	 *
	 * @return array array containing information with modules priorities
	 */
	public static final function get_load_priority_array($force=false) {
		static $load_prior_array=null;
		if($load_prior_array===null || $force) {
			$installed_modules = DB::Execute('SELECT name,version FROM modules ORDER BY priority');
			if ($installed_modules!==false) {
				$load_prior_array = array();
				while (($row = $installed_modules->FetchRow()))
					$load_prior_array[] = $row;
			}
		}
		return $load_prior_array;
	}

	/**
	 * Creates new module instance.
	 *
	 * Do not use directly.
	 * Use pack_module instead.
	 *
	 * @param module name
	 * @return object newly created module object
	 * @throws exception 'module not loaded' if the module is not registered
	 */
	public static final function & new_instance($mod,$parent,$name,$clear_vars=false) {
		if(!array_key_exists($mod, self::$loaded_modules)) {
			$loaded = false;
			foreach(self::$not_loaded_modules as $i=>$v) {
				$version = $v['version'];
				$module = $v['name'];
				ModuleManager :: include_main($module, $version);
				unset(self::$not_loaded_modules[$i]);
				self::$loaded_modules[$module] = true;
				if($module==$mod) {
					$loaded=true;
					break;
				}
			}
			if (!$loaded)
				throw new Exception('module '.$mod.' not loaded');
		}
		if(!class_exists($mod))
			trigger_error('Class not exists: '.$mod,E_USER_ERROR);
		$m = new $mod($mod,$parent,$name,$clear_vars);
		return $m;
	}

	/**
	 * Returns instance of module.
	 *
	 * @param string module name
	 * @return bool null if module instance was not found, requested module object otherwise
	 */
	public static final function & get_instance($path) {
		$xx = explode('/',$path);
		$curr = & self::$root;
		if($curr->get_node_id() != $xx[1]) {
			$x = null;
			return $x;
		}
		$xx_count = count($xx);
		if($xx_count>2) {
			$curr = & $curr->get_child($xx[2]);
			if(!$curr) return $curr;
			$xx_count--;
			for($i=2; $i<$xx_count; $i++) {
				if($curr->get_node_id() == $xx[$i]) {
					$curr = & $curr->get_child($xx[$i+1]);
				} else {
					$x = null;
					return $x;
				}
				if(!$curr) return $curr;
			}
		}
		return $curr;
	}

	/**
	 * Creates default data directory for module.
	 *
	 * Do not use directly.
	 *
	 * @param string module name
	 * @return bool true if directory was created or already exists, false otherwise
	 */
	public static final function create_data_dir($name) {
		$name = str_replace('/','_',$name);
		$dir = DATA_DIR.'/'.$name;
		if (is_dir($dir) && is_writable($dir))
			return true;
		$x = mkdir($dir);
		file_put_contents($dir.'/index.html','');
		return $x;
	}

	/**
	 * Removes default data directory of a module.
	 *
	 * Do not use directly.
	 *
	 * @param string module name
	 * @return bool true if directory was removed or did not exist, false otherwise
	 */
	public static final function remove_data_dir($name) {
		$name = str_replace('/','_',$name);
		$dir = DATA_DIR.'/'.$name.'/';
		if(is_dir($dir))
			recursive_rmdir($dir);
		return true;
	}

	public static final function get_data_dir($name) {
		$name = str_replace('/','_',$name);
		return DATA_DIR.'/'.$name.'/';
	}

	/**
	 * Loads all installed classes definitions.
	 *
	 * Do not use directly.
	 */
	public static final function load_modules() {
		self::$modules = array();
		$installed_modules = ModuleManager::get_load_priority_array(true);
		self::$not_loaded_modules = $installed_modules;
		self::$loaded_modules = array();
		$cache_file = DATA_DIR.'/cache/common.php';
		$cached = false;
		if(CACHE_COMMON_FILES) {
			if(!file_exists($cache_file))
				self::create_common_cache();
			ob_start();
			require_once($cache_file);
			ob_end_clean();
			$cached = true;
		}
		foreach($installed_modules as $row) {
			$module = $row['name'];
			$version = $row['version'];
			if(!$cached)
				ModuleManager :: include_common($module, $version);
			ModuleManager :: register($module, $version, self::$modules);
		}
	}
	
	public static final function create_common_cache() {
		$installed_modules = ModuleManager::get_load_priority_array(true);
		$ret = '';
		foreach($installed_modules as $row) {
			$module = $row['name'];
			$version = $row['version'];
			$path = self::get_module_dir_path($module);
			$file = self::get_module_file_name($module);
			$file_url = 'modules/' . $path . '/' . $file . 'Common_'.$version.'.php';
			if(file_exists($file_url)) {
				$ret .= file_get_contents ($file_url);
				$ret .= '<?php $x = \''.$module.'Common\';'.
					'if(class_exists($x)){ '.
						'if(!array_key_exists(\'ModuleCommon\',class_parents($x)))'.
							'trigger_error(\'Module '.$path.': Common class should extend ModuleCommon class.\',E_USER_ERROR);'.
							'call_user_func(array($x,\'Instance\'),\''.$module.'\');'.
					'} ?>';
			}
		}
		$cache_dir = DATA_DIR.'/cache/';
		if(!file_exists($cache_dir))
			mkdir($cache_dir,0777,true);

		file_put_contents($cache_dir.'common.php',$ret);
	}

	/**
	 * Creates root(first) module instance.
	 *
	 * Do not use directly.
	 */
	public static function & create_root() {
		ob_start();
		try {
			$default_module = Variable::get('default_module');
			self::$root = & ModuleManager :: new_instance($default_module,null,'0');
		} catch (Exception $e) {
			self::$root = & ModuleManager :: new_instance(FIRST_RUN,null,'0');
		}
		$ret = trim(ob_get_contents());
		if(strlen($ret)>0 || self::$root==null) trigger_error($ret,E_USER_ERROR);
		ob_end_clean();
		return self::$root;
	}

	/**
	 * Checks access to a method.
	 * First parameter is a module object and second is a method in this module.
	 *
	 * If you want to restric access to a method just create a method called
	 * 'methodname_access' returning false if you want restrict user from accessing
	 * 'methodname' method.
	 *
	 * check_access is called automatically with each pack_module call.
	 *
	 * @param object module
	 * @param string function name
	 * @return bool true if access is granted, false otherwise
	 */
	public static final function check_access($mod, $m) {
		$comm = $mod.'Common';
		if(class_exists($comm)) {
			$sing = call_user_func(array($comm,'Instance'));
			if (method_exists($sing, $m . '_access') &&
				!call_user_func(array($sing, $m . '_access')))
				return false;
		}
		return true;
	}

	public static final function call_common_methods($method,$cached=true,$args=array()) {
		static $cache;
		$cache_id = $method.md5(serialize($args));
		if(!isset($cache[$cache_id]) || !$cached) {
			$ret = array();
			ob_start();
			foreach(self::$modules as $name=>$version)
				if(class_exists($name.'Common') && method_exists($name.'Common', $method)) {
					$ret[$name] = call_user_func_array(array($name.'Common',$method),$args);
				}
			ob_end_clean();
			$cache[$cache_id]=$ret;
		}
		return $cache[$cache_id];
	}

	public static final function check_common_methods($method,$cached=true) {
		static $cache;
		$cache_id = $method;
		if(!isset($cache[$cache_id]) || !$cached) {
			$ret = array();
			foreach(self::$modules as $name=>$version)
				if(class_exists($name.'Common') && method_exists($name.'Common', $method)) {
					$ret[] = $name;
				}
			$cache[$cache_id]=&$ret;
		}
		return $cache[$cache_id];
	}

    /**
     * Returns array containing required modules.
     *
     * @param bool $verbose If true then function returns associative array containing names of modules that require specific module
     * @return array If module is required then arr['module_name'] is equal to number of modules that require specific module.
     */
    public static function required_modules($verbose = false) {
        $ret = array();
        foreach (self::$modules as $name => $version) {
			self::include_install($name);
			$required = call_user_func(array (
				self::$modules_install[$name],
				'requires'
				),$version);

			foreach ($required as $req_mod) {
                $req_name = str_replace('/','_',$req_mod['name']);
                if($verbose) {
                    $ret[$req_name][] = $name;
                } else {
                    if(!isset($ret[$req_name])) $ret[$req_name] = 1;
                    else $ret[ $req_name ]++;
                }
			}
		}
        return $ret;
    }
}
?>
Return current item: EPESI