diff --git a/lib/SimpleSAML/Module.php b/lib/SimpleSAML/Module.php index 5e333f66becde88b64db22ca1dd64719a90e6ada..a40ef48a7ad6bcc6e3c54d355f75df805a316069 100644 --- a/lib/SimpleSAML/Module.php +++ b/lib/SimpleSAML/Module.php @@ -13,17 +13,25 @@ class Module { /** - * A cache containing the modules currently installed. Each key in the array is the module name, and the value is - * a boolean telling if the module is enabled or not. + * A list containing the modules currently installed. * * @var array */ - private static $modules = array(); + public static $modules = array(); + + /** + * A cache containing specific information for modules, like whether they are enabled or not, or their hooks. + * + * @var array + */ + public static $module_info = array(); + /** * Autoload function for SimpleSAMLphp modules following PSR-0. * * @param string $className Name of the class. + * * @deprecated This method will be removed in SSP 2.0. * * TODO: this autoloader should be removed once everything has been migrated to namespaces. @@ -37,8 +45,8 @@ class Module } $modNameEnd = strpos($className, '_', $modulePrefixLength); - $module = substr($className, $modulePrefixLength, $modNameEnd - $modulePrefixLength); - $path = explode('_', substr($className, $modNameEnd + 1)); + $module = substr($className, $modulePrefixLength, $modNameEnd - $modulePrefixLength); + $path = explode('_', substr($className, $modNameEnd + 1)); if (!self::isModuleEnabled($module)) { return; @@ -54,7 +62,8 @@ class Module // the file exists, but the class is not defined. Is it using namespaces? $nspath = join('\\', $path); if (class_exists('SimpleSAML\Module\\'.$module.'\\'.$nspath) || - interface_exists('SimpleSAML\Module\\'.$module.'\\'.$nspath)) { + interface_exists('SimpleSAML\Module\\'.$module.'\\'.$nspath) + ) { // the class has been migrated, create an alias and warn about it \SimpleSAML\Logger::warning( "The class or interface '$className' is now using namespaces, please use 'SimpleSAML\\Module\\". @@ -132,22 +141,32 @@ class Module */ public static function isModuleEnabled($module) { - if (isset(self::$modules[$module])) { - return self::$modules[$module]; + $config = \SimpleSAML_Configuration::getOptionalConfig(); + return self::isModuleEnabledWithConf($module, $config->getArray('module.enable', array())); + } + + + private static function isModuleEnabledWithConf($module, $mod_config) + { + if (isset(self::$module_info[$module]['enabled'])) { + return self::$module_info[$module]['enabled']; + } + + if (!empty(self::$modules) && !in_array($module, self::$modules)) { + return false; } $moduleDir = self::getModuleDir($module); if (!is_dir($moduleDir)) { + self::$module_info[$module]['enabled'] = false; return false; } - $globalConfig = \SimpleSAML_Configuration::getOptionalConfig(); - $moduleEnable = $globalConfig->getArray('module.enable', array()); - - if (isset($moduleEnable[$module])) { - if ($moduleEnable[$module] === true) { - return $moduleEnable[$module]; + if (isset($mod_config[$module])) { + if (is_bool($mod_config[$module])) { + self::$module_info[$module]['enabled'] = $mod_config[$module]; + return $mod_config[$module]; } throw new \Exception("Invalid module.enable value for the '$module' module."); @@ -161,13 +180,16 @@ class Module } if (file_exists($moduleDir.'/enable')) { + self::$module_info[$module]['enabled'] = true; return true; } if (!file_exists($moduleDir.'/disable') && file_exists($moduleDir.'/default-enable')) { + self::$module_info[$module]['enabled'] = true; return true; } + self::$module_info[$module]['enabled'] = false; return false; } @@ -182,17 +204,17 @@ class Module public static function getModules() { if (!empty(self::$modules)) { - return array_keys(self::$modules); + return self::$modules; } $path = self::getModuleDir('.'); - $dh = opendir($path); + $dh = scandir($path); if ($dh === false) { throw new \Exception('Unable to open module directory "'.$path.'".'); } - while (($f = readdir($dh)) !== false) { + foreach ($dh as $f) { if ($f[0] === '.') { continue; } @@ -201,12 +223,10 @@ class Module continue; } - self::$modules[$f] = self::isModuleEnabled($f); + self::$modules[] = $f; } - closedir($dh); - - return array_keys(self::$modules); + return self::$modules; } @@ -293,6 +313,44 @@ class Module } + /** + * Get the available hooks for a given module. + * + * @param string $module The module where we should look for hooks. + * + * @return array An array with the hooks available for this module. Each element is an array with two keys: 'file' + * points to the file that contains the hook, and 'func' contains the name of the function implementing that hook. + * When there are no hooks defined, an empty array is returned. + */ + public static function getModuleHooks($module) + { + if (isset(self::$modules[$module]['hooks'])) { + return self::$modules[$module]['hooks']; + } + + $hook_dir = self::getModuleDir($module).'/hooks'; + if (!is_dir($hook_dir)) { + return array(); + } + + $hooks = array(); + $files = scandir($hook_dir); + foreach ($files as $file) { + if ($file[0] === '.') { + continue; + } + + if (!preg_match('/hook_(\w+)\.php/', $file, $matches)) { + continue; + } + $hook_name = $matches[1]; + $hook_func = $module.'_hook_'.$hook_name; + $hooks[$hook_name] = array('file' => $hook_dir.'/'.$file, 'func' => $hook_func); + } + return $hooks; + } + + /** * Call a hook in all enabled modules. * @@ -300,29 +358,37 @@ class Module * * @param string $hook The name of the hook. * @param mixed &$data The data which should be passed to each hook. Will be passed as a reference. + * + * @throws \SimpleSAML_Error_Exception If an invalid hook is found in a module. */ public static function callHooks($hook, &$data = null) { assert('is_string($hook)'); $modules = self::getModules(); + $config = \SimpleSAML_Configuration::getOptionalConfig()->getArray('module.enable', array()); sort($modules); foreach ($modules as $module) { - if (!self::isModuleEnabled($module)) { + if (!self::isModuleEnabledWithConf($module, $config)) { continue; } - $hookfile = self::getModuleDir($module).'/hooks/hook_'.$hook.'.php'; - if (!file_exists($hookfile)) { + if (!isset(self::$module_info[$module]['hooks'])) { + self::$module_info[$module]['hooks'] = self::getModuleHooks($module); + } + + if (!isset(self::$module_info[$module]['hooks'][$hook])) { continue; } - require_once($hookfile); + require_once(self::$module_info[$module]['hooks'][$hook]['file']); - $hookfunc = $module.'_hook_'.$hook; - assert('is_callable($hookfunc)'); + if (!is_callable(self::$module_info[$module]['hooks'][$hook]['func'])) { + throw new \SimpleSAML_Error_Exception('Invalid hook \''.$hook.'\' for module \''.$module.'\'.'); + } - $hookfunc($data); + $fn = self::$module_info[$module]['hooks'][$hook]['func']; + $fn($data); } } }