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);
}
}
}