The big great rewrite of modules. Hope it will merge well

This commit is contained in:
Abel Hoogeveen 2015-05-14 12:43:11 +02:00
parent dd93772704
commit 71c9904746
9 changed files with 509 additions and 150 deletions

View File

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

View File

@ -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, "<?php return $config ;");
}
}
/**
* Return a value from the module configuration
* @access public
* @param Mixed config Key
* @return Mixed config value
*/
public function getConfigValue($key) {
return $this->cfg->$key;
}
}

View File

@ -10,16 +10,16 @@ use \stdClass;
*/
class Core {
public $mods;
public $register;
private $loaded = false;
/**
* 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;
/**
* 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);
}
}

View File

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

View File

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

View File

@ -0,0 +1,321 @@
<?php
/**
* @author FuzeNetwork
*/
namespace FuzeWorks;
use \stdClass;
/**
* Modules Class
*
*/
class Modules extends Bus{
public $register;
public $modules;
/**
* An array which modules are loaded, and should not be loaded again
* @access private
* @var Array of module names
*/
private $loaded_modules = array();
public function __construct(&$core){
parent::__construct($core);
$this->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, "<?php return $config ;");
}
}
/**
* @throws FuzeWorks\ModuleException
*/
public function addModule($moduleInfo_file) {
$file = $moduleInfo_file;
$directory = dirname($file);
if (file_exists($file)) {
$cfg = (object) require($file);
$cfg->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();
}
}
?>

View File

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

View File

@ -0,0 +1,22 @@
<?php
namespace Module\FolderLoading;
use \FuzeWorks\Module;
/**
* Sections module, see usage documentation
* @author TechFuze
*/
class Main extends Module {
/**
* Loads the module and registers the events
* @access public
*/
public function onLoad() {
}
}
?>

View File

@ -0,0 +1,23 @@
<?php return array (
'module_class' => '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,
) ;