diff --git a/Core/System/class.abstract.bus.php b/Core/System/class.abstract.bus.php index bfa7397..3b83e8d 100644 --- a/Core/System/class.abstract.bus.php +++ b/Core/System/class.abstract.bus.php @@ -12,6 +12,7 @@ abstract class Bus { protected $models; protected $layout; protected $events; + protected $modules; protected function __construct(&$core){ $this->core = &$core; @@ -23,6 +24,7 @@ abstract class Bus { $this->layout = &$core->mods->layout; $this->events = &$core->mods->events; $this->router = &$core->mods->router; + $this->modules = &$core->mods->modules; } } diff --git a/Core/System/class.abstract.module.php b/Core/System/class.abstract.module.php index fa1550c..e8716f6 100644 --- a/Core/System/class.abstract.module.php +++ b/Core/System/class.abstract.module.php @@ -24,6 +24,11 @@ class Module extends Bus { */ protected $linkName = 'placeholder'; + /** + * @var moduleInfo object of the module + */ + protected $cfg; + /** * Constructor * @@ -52,6 +57,15 @@ class Module extends Bus { return $this->modulePath; } + /** + * Returns the config of the module (moduleInfo.php) + * @access public + * @return stdClass module config + */ + public function getModuleConfig() { + return $this->cfg; + } + /** * Changes the path to the location of the module * @@ -84,4 +98,45 @@ class Module extends Bus { public function setModuleName($modName) { $this->moduleName = $modName; } + + /** + * Add the moduleInfo.php to the module for direct interaction + * @access public + * @param stdClass module config + */ + public function setModuleConfig($config) { + $this->cfg = $config; + } + + /** + * Set a value in the modules moduleInfo.php + * @access protected + * @param Mixed config Key + * @param Mixed config value + */ + public function setConfigValue($key, $value) { + $file = $this->getModulePath() . "moduleInfo.php"; + $this->cfg->$key = $value; + + // Check if the module path is set yet + if ($this->getModulePath() == null) { + $this->logger->logWarning("Could not write module config. ModulePath is not set", get_class($this)); + return false; + } + + if (file_exists($file) && is_writable($file)) { + $config = var_export($this->cfg, true); + file_put_contents($file, "cfg->$key; + } } \ No newline at end of file diff --git a/Core/System/class.core.php b/Core/System/class.core.php index 6ce1126..0fa4082 100644 --- a/Core/System/class.core.php +++ b/Core/System/class.core.php @@ -10,16 +10,16 @@ use \stdClass; */ class Core { - public $mods; - public $register; - - /** - * An array which modules are loaded, and should not be loaded again - * @access private - * @var Array of module names - */ - private $loaded_modules = array(); - private $loaded = false; + private $loaded = false; + + /** + * Modules Class holder + * @access public + * @var FuzeWorks\Modules + */ + public $modules; + + public $mods; ## START/STOP public function init() { @@ -31,10 +31,9 @@ class Core { register_shutdown_function(array($this, "shutdown")); // Load core functionality - $this->mods = new stdClass(); $this->loadStartupFiles(); - $this->buildRegister(); + $this->mods->modules->buildRegister(); $this->mods->events->buildEventRegister(); // And initialize the router paths @@ -66,158 +65,37 @@ class Core { require_once("Core/System/class.layout.php"); require_once("Core/System/class.events.php"); require_once("Core/System/class.router.php"); + require_once("Core/System/class.modules.php"); - // Load them + // Create the module holder + + + // Load the modules $this->mods->events = new Events ($this); - $this->mods->config = new Config ($this); + $this->mods->config = new Config ($this); $this->mods->logger = new Logger ($this); $this->mods->models = new Models ($this); $this->mods->layout = new Layout ($this); $this->mods->router = new Router ($this); + $this->mods->modules = new Modules ($this); $this->loaded = true; } public function shutdown() { $this->mods->events->fireEvent('coreShutdownEvent'); + $this->mods->logger->shutdown(); } public function loadMod($name) { - // Where the modules are - $path = "Modules/"; - - // Check if the requested module is registered - if (isset($this->register[$name])) { - if (!empty($this->register[$name])) { - // Load the moduleInfo - $cfg = (object) $this->register[$name]; - - // Check if the module is already loaded. If so, only return a reference, if not, load the module - if (in_array($name, $this->loaded_modules)) { - // return the link - $msg = "Module '".ucfirst((isset($cfg->name) ? $cfg->name : $cfg->module_name)) . "' is already loaded"; - $this->mods->logger->log($msg); - $c = &$this->mods->{strtolower($cfg->module_name)}; - return $c; - } else { - // Load the module - $file = $cfg->directory . $cfg->module_file; - - // Load the dependencies before the module loads - $deps = (isset($cfg->dependencies) ? $cfg->dependencies : array()); - for ($i=0; $i < count($deps); $i++) { - $this->loadMod($deps[$i]); - } - - // Check if the file exists - if (file_exists($file)) { - // And load it - require_once($file); - $class_name = $cfg->module_class; - $msg = "Loading Module '".ucfirst((isset($cfg->name) ? $cfg->name : $cfg->module_name)) . "'"; - $msg .= (isset($cfg->version) ? "; version: ".$cfg->version : ""); - $msg .= (isset($cfg->author) ? "; made by ".$cfg->author : ""); - $msg .= (isset($cfg->website) ? "; from ".$cfg->website: ""); - $this->mods->logger->log($msg); - } else { - // Throw Exception if the file does not exist - throw new Exception("Requested mod '".$name."' could not be loaded. Class file not found", 1); - return false; - } - - // If it is an abstract module, load an StdClass for the module address - if (isset($cfg->abstract)) { - if ($cfg->abstract) { - $CLASS = new stdClass(); - return $this->mods->{strtolower($cfg->module_name)} = &$CLASS; - } - } - - // Load the module class - $class_name = $cfg->module_class; - $CLASS = new $class_name($this); - - // Apply default methods - if (method_exists($CLASS, 'setModulePath')) { - $CLASS->setModulePath($cfg->directory); - } - if (method_exists($CLASS, 'setModuleLinkName')) { - $CLASS->setModuleLinkName(strtolower($cfg->module_name)); - } - if (method_exists($CLASS, 'setModuleName')) { - $CLASS->setModuleName($name); - } - - if (!method_exists($CLASS, 'onLoad')) { - throw new Exception("Module '".$name."' does not have an onLoad() method! Invalid module", 1); - } - $CLASS->onLoad(); - - // Add to the loaded modules - $this->loaded_modules[] = $name; - - // Return a reference - return $this->mods->{strtolower($cfg->module_name)} = &$CLASS; - } - } - } + return $this->mods->modules->loadMod($name); } - public function buildRegister() { - $this->mods->logger->newLevel("Loading Module Headers", 'Core'); - - // Get all the module directories - $dir = "Modules/"; - $mod_dirs = array(); - $mod_dirs = array_values(array_diff(scandir($dir), array('..', '.'))); - - // Build the module register - $register = array(); - for ($i=0; $i < count($mod_dirs); $i++) { - $mod_dir = $dir . $mod_dirs[$i] . "/"; - // If a moduleInfo.php exists, load it - if (file_exists($mod_dir . "/moduleInfo.php")) { - $cfg = (object) require($mod_dir . "/moduleInfo.php"); - $name = ""; - $name .= (!empty($cfg->author) ? strtolower($cfg->author)."/" : ""); - $name .= strtolower($cfg->module_name); - - // Append directory - $cfg->directory = $mod_dir; - if (isset($cfg->enabled)) { - if ($cfg->enabled) { - $register[$name] = (array) $cfg; - $this->mods->logger->log("[ON] '".$name."'"); - } else { - $this->mods->logger->log("[OFF] '".$name."'"); - } - } else { - $register[$name] = (array) $cfg; - $this->mods->logger->log("[ON] '".$name."'"); - } - - - } else { - // Get the name - $name = $mod_dirs[$i]; - - // Build a default module config - $cfg = new stdClass(); - $cfg->module_class = ucfirst($name); - $cfg->module_file = 'class.'.strtolower($name).".php"; - $cfg->module_name = $name; - $cfg->dependencies = array(); - $cfg->versions = array(); - $cfg->directory = $mod_dir; - $register[$name] = (array)$cfg; - $this->mods->logger->log("[ON] '".$name."'"); - } - } - - $this->register = $register; - $this->mods->logger->stopLevel(); - + public function addMod($moduleInfo_file) { + return $this->mods->modules->addModule($moduleInfo_file); } + + } diff --git a/Core/System/class.events.php b/Core/System/class.events.php index 0c11efb..2f01151 100644 --- a/Core/System/class.events.php +++ b/Core/System/class.events.php @@ -20,6 +20,7 @@ namespace FuzeWorks; class Events extends Bus{ private $listeners; + private $enabled = true; public function __construct(&$core) { parent::__construct($core); @@ -119,6 +120,13 @@ class Events extends Bus{ if (func_num_args() > 1) call_user_func_array(array($event, 'init'), array_slice(func_get_args(), 1)); + // Do not run if the event system is disabled + if (!$this->enabled) { + $this->logger->log("Event system is disabled"); + $this->logger->stopLevel(); + return $event; + } + $this->logger->log("Checking for Listeners"); // Read the event register for listeners @@ -165,7 +173,7 @@ class Events extends Bus{ // Event Preparation: public function buildEventRegister() { $event_register = array(); - foreach ($this->core->register as $key => $value) { + foreach ($this->modules->register as $key => $value) { if (isset($value['events'])) { if (!empty($value['events'])) { for ($i=0; $i < count($value['events']); $i++) { @@ -181,6 +189,22 @@ class Events extends Bus{ $this->register = $event_register; } + + /** + * Enables the event system + */ + public function enable() { + $this->logger->log("Enabled the Event system"); + $this->enabled = true; + } + + /** + * Disables the event system + */ + public function disable() { + $this->logger->log("Disabled the Event system"); + $this->enabled = false; + } } diff --git a/Core/System/class.logger.php b/Core/System/class.logger.php index c6e02c7..c14311b 100644 --- a/Core/System/class.logger.php +++ b/Core/System/class.logger.php @@ -26,7 +26,6 @@ class Logger extends Bus{ set_Exception_handler(array($this, "exceptionHandler")); error_reporting(false); } - $this->events->addListener(array($this, 'shutdown'), 'coreShutdownEvent', EventPriority::LOWEST); $this->debug = $this->config->error->debug; $this->newLevel("Logger Initiated"); } diff --git a/Core/System/class.modules.php b/Core/System/class.modules.php new file mode 100644 index 0000000..cb55f68 --- /dev/null +++ b/Core/System/class.modules.php @@ -0,0 +1,321 @@ +modules = array(); + } + + /** + * @throws FuzeWorks\ModuleException + */ + public function loadMod($name) { + // Where the modules are + $path = "Modules/"; + + // Check if the requested module is registered + if (isset($this->register[$name])) { + if (!empty($this->register[$name])) { + // Load the moduleInfo + $cfg = (object) $this->register[$name]; + + // Check if the module is disabled + if (isset($cfg->meta)) { + throw new ModuleException("Requested mod '".$name."' could not be loaded. Not enabled", 1); + return false; + } + + // Check if the module is already loaded. If so, only return a reference, if not, load the module + if (in_array($name, $this->loaded_modules)) { + // return the link + $msg = "Module '".ucfirst((isset($cfg->name) ? $cfg->name : $cfg->module_name)) . "' is already loaded"; + $this->logger->log($msg); + $c = &$this->core->mods->{strtolower($cfg->module_name)}; + return $c; + } else { + // Load the module + $file = $cfg->directory ."/". $cfg->module_file; + + // Load the dependencies before the module loads + $deps = (isset($cfg->dependencies) ? $cfg->dependencies : array()); + for ($i=0; $i < count($deps); $i++) { + $this->loadMod($deps[$i]); + } + + // Check if the file exists + if (file_exists($file)) { + // And load it + require_once($file); + $class_name = $cfg->module_class; + $msg = "Loading Module '".ucfirst((isset($cfg->name) ? $cfg->name : $cfg->module_name)) . "'"; + $msg .= (isset($cfg->version) ? "; version: ".$cfg->version : ""); + $msg .= (isset($cfg->author) ? "; made by ".$cfg->author : ""); + $msg .= (isset($cfg->website) ? "; from ".$cfg->website: ""); + $this->logger->log($msg); + } else { + // Throw Exception if the file does not exist + throw new ModuleException("Requested mod '".$name."' could not be loaded. Class file not found", 1); + return false; + } + + // If it is an abstract module, load an StdClass for the module address + if (isset($cfg->abstract)) { + if ($cfg->abstract) { + $CLASS = new stdClass(); + return $this->core->mods->{strtolower($cfg->module_name)} = &$CLASS; + } + } + + // Load the module class + $class_name = $cfg->module_class; + $CLASS = new $class_name($this); + + // Apply default methods + if (method_exists($CLASS, 'setModulePath')) { + $CLASS->setModulePath($cfg->directory); + } + if (method_exists($CLASS, 'setModuleLinkName')) { + $CLASS->setModuleLinkName(strtolower($cfg->module_name)); + } + if (method_exists($CLASS, 'setModuleName')) { + $CLASS->setModuleName($name); + } + if (method_exists($CLASS, 'setModuleConfig')) { + $CLASS->setModuleConfig($cfg); + } + + if (!method_exists($CLASS, 'onLoad')) { + throw new ModuleException("Module '".$name."' does not have an onLoad() method! Invalid module", 1); + } + $CLASS->onLoad(); + + // Add to the loaded modules + $this->loaded_modules[] = $name; + + // Return a reference + return $this->core->mods->{strtolower($cfg->module_name)} = &$CLASS; + } + } + } + } + + private function setModuleValue($file, $key, $value) { + if (file_exists($file) && is_writable($file)) { + $cfg = require($file); + $cfg[$key] = $value; + $config = var_export($cfg, true); + file_put_contents($file, "directory = $directory; + + // Define the module name + $name = ""; + $name .= (!empty($cfg->author) ? strtolower($cfg->author)."/" : ""); + $name .= strtolower($cfg->module_name); + + $this->logger->log("Adding module: '".$name."'"); + if (isset($this->register[$name])) { + $this->logger->logError("Module '".$name."' can not be added. Module is already loaded"); + return false; + } + + // Check wether the module is enabled or no + if (isset($cfg->enabled)) { + if ($cfg->enabled) { + // Copy all the data into the register and enable + $this->register[$name] = (array) $cfg; + $this->logger->log("[ON] '".$name."'"); + } else { + // If not, copy all the basic data so that it can be enabled in the future + $cfg2 = new StdClass(); + $cfg2->module_name = $cfg->module_name; + $cfg2->directory = $cfg->directory; + $cfg2->meta = $cfg; + $this->register[$name] = (array)$cfg2; + $this->logger->log("[OFF] '".$name."'"); + } + } else { + // Copy all the data into the register and enable + $this->register[$name] = (array) $cfg; + $this->logger->log("[ON] '".$name."'"); + } + } else { + throw new ModuleException("Could not add module. '$moduleInfo_file' does not exist", 1); + } + } + + /** + * @throws FuzeWorks\ModuleException + */ + public function enableModule($name, $permanent = true) { + if (isset($this->register[$name])) { + // Change the register + $info = (object) $this->register[$name]; + + // Do nothing if it is already enabled + if (isset($info->enabled)) { + if ($info->enabled) { + $this->logger->logWarning("Could not enable module '".$name."'. Module is already enabled."); + return false; + } + } + + // Otherwise move data from meta to the module config + $info = $info->meta; + $info->enabled = true; + $this->register[$name] = (array)$info; + + $this->logger->log("Enabled module '".$name."'"); + + // Enable it permanently if so desired + if ($permanent) { + $file = $info->directory . "/moduleInfo.php"; + $this->setModuleValue($file, 'enabled', true); + } + + // Reload the eventRegister + $this->events->buildEventRegister(); + } else { + throw new ModuleException("Could not enable module '".$name."'. Module does not exist.", 1); + } + } + + /** + * @throws FuzeWorks\ModuleException + */ + public function disableModule($name, $permanent = true) { + if (isset($this->register[$name])) { + $info = (object) $this->register[$name]; + + // Do nothing if it is already disabled + if (isset($info->meta)) { + $this->logger->logWarning("Could not disable module '".$name."'. Module is already disabled."); + return false; + } + + $disabled = new StdClass(); + $disabled->meta = $info; + $disabled->directory = $info->directory; + $disabled->module_name = $info->module_name; + + $this->register[$name] = (array)$disabled; + $this->logger->log("Disabled module '".$name."'"); + if ($permanent) { + $file = $info->directory . "/moduleInfo.php"; + $this->setModuleValue($file, 'enabled', false); + } + + // Reload the eventRegister + $this->events->buildEventRegister(); + + // Remove the existence of the module + unset($this->core->mods->{strtolower($info->module_name)}); + } else { + throw new ModuleException("Could not disable module '".$name."'. Module does not exist.", 1); + } + + } + + public function buildRegister() { + $this->logger->newLevel("Loading Module Headers", 'Core'); + + // Get all the module directories + $dir = "Modules/"; + $mod_dirs = array(); + $mod_dirs = array_values(array_diff(scandir($dir), array('..', '.'))); + + // Build the module register + $register = array(); + for ($i=0; $i < count($mod_dirs); $i++) { + $mod_dir = $dir . $mod_dirs[$i] . "/"; + // If a moduleInfo.php exists, load it + if (file_exists($mod_dir . "/moduleInfo.php")) { + // Load the configuration file + $cfg = (object) require($mod_dir . "/moduleInfo.php"); + + // Define the module name + $name = ""; + $name .= (!empty($cfg->author) ? strtolower($cfg->author)."/" : ""); + $name .= strtolower($cfg->module_name); + + // Get the module directory + $cfg->directory = $mod_dir; + + // Check wether the module is enabled or no + if (isset($cfg->enabled)) { + if ($cfg->enabled) { + // Copy all the data into the register and enable + $register[$name] = (array) $cfg; + $this->logger->log("[ON] '".$name."'"); + } else { + // If not, copy all the basic data so that it can be enabled in the future + $cfg2 = new StdClass(); + $cfg2->module_name = $cfg->module_name; + $cfg2->directory = $cfg->directory; + $cfg2->meta = $cfg; + $register[$name] = (array)$cfg2; + $this->logger->log("[OFF] '".$name."'"); + } + } else { + // Copy all the data into the register and enable + $register[$name] = (array) $cfg; + $this->logger->log("[ON] '".$name."'"); + } + } else { + // If no details are specified, create a basic module + $name = $mod_dirs[$i]; + + // Build a default module config + $cfg = new stdClass(); + $cfg->module_class = ucfirst($name); + $cfg->module_file = 'class.'.strtolower($name).".php"; + $cfg->module_name = $name; + $cfg->dependencies = array(); + $cfg->versions = array(); + $cfg->directory = $mod_dir; + + // Apply it + $register[$name] = (array)$cfg; + $this->logger->log("[ON] '".$name."'"); + } + } + + $this->register = $register; + $this->logger->stopLevel(); + + } + +} + +?> \ No newline at end of file diff --git a/tests/core_moduleTest.php b/tests/core_moduleTest.php index 2f0ca2e..ee77b41 100644 --- a/tests/core_moduleTest.php +++ b/tests/core_moduleTest.php @@ -16,8 +16,31 @@ class ModuleTest extends CoreTestAbstract $core = $this->createCore(); - $mod = $core->loadMod('mycorp/example'); - $this->assertInstanceOf('\Module\Example\Main', $mod); + $core->addMod('tests/modules/testFolderLoading/moduleInfo.php'); + $mod = $core->loadMod('ci/folderloading'); + $this->assertInstanceOf('\Module\FolderLoading\Main', $mod); + } + + /** + * Tests the enabling and disabling of modules + */ + public function testModuleEnabling(){ + + $core = $this->createCore(); + + $core->addMod('tests/modules/testFolderLoading/moduleInfo.php'); + + //Enable a module + $core->mods->modules->enableModule('ci/folderloading'); + $cfg = (object) require('tests/modules/testFolderLoading/moduleInfo.php'); + $this->assertEquals(true, $cfg->enabled); + + //Disable a module + $core->mods->modules->disableModule('ci/folderloading'); + $cfg = (object) require('tests/modules/testFolderLoading/moduleInfo.php'); + $this->assertEquals(false, $cfg->enabled); + + $core->mods->modules->enableModule('ci/folderloading'); } /** @@ -47,4 +70,16 @@ class ModuleTest extends CoreTestAbstract // The directory $this->assertEquals('Modules/example/', $mod->getModulePath()); } + + /** + * Tests the loading of a moduleInfo in an unknown directory + * @throws ModuleException + * @expectedException \FuzeWorks\moduleException + */ + public function testLoadingUnknownModuleInfoDirectory(){ + + $core = $this->createCore(); + + $core->addMod("tests/moduleInfo.php"); + } } \ No newline at end of file diff --git a/tests/modules/testFolderLoading/class.main.php b/tests/modules/testFolderLoading/class.main.php new file mode 100644 index 0000000..e7f5d22 --- /dev/null +++ b/tests/modules/testFolderLoading/class.main.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/tests/modules/testFolderLoading/moduleInfo.php b/tests/modules/testFolderLoading/moduleInfo.php new file mode 100644 index 0000000..1c0f185 --- /dev/null +++ b/tests/modules/testFolderLoading/moduleInfo.php @@ -0,0 +1,23 @@ + 'Module\\FolderLoading\\Main', + 'module_file' => 'class.main.php', + 'module_name' => 'FolderLoading', + 'abstract' => false, + 'dependencies' => + array ( + ), + 'events' => + array ( + ), + 'sections' => + array ( + ), + 'name' => 'FuzeWorks Example Module', + 'description' => 'A descriptive module that functions as an example', + 'author' => 'ci', + 'version' => '1.0.0', + 'website' => 'http://fuzeworks.techfuze.net/', + 'date_created' => '29-04-2015', + 'date_updated' => '29-04-2015', + 'enabled' => true, +) ; \ No newline at end of file