The code itself is divided into 3 logical parts:
- Core modules (I call them Kernel modules, my love for Linux) - those are basic modules a web-developer would need, e.g. database wrapper class (PDO),
User
class that works with user session etc. - Developer modules - the modules a web-developer might write for themselves.
- Output modules - the HTML output that might include other modules -
Kernel
,Developer
orOutput
modules.
I am showing only the Kernel
class here, since it does all the job handling everything. I will also show a small example of how to work with this framework.
<?php
namespace Qobox;
class Kernel {
private $modules;
public function __construct() {
// Get the $_REQUEST and filter it into $request
$request = array();
foreach($_REQUEST as $key => $value) {
$key = htmlspecialchars($key, ENT_QUOTES|ENT_DISALLOWED|ENT_HTML5, "UTF-8");
$value = htmlspecialchars($value, ENT_QUOTES|ENT_DISALLOWED|ENT_HTML5, "UTF-8");
if(empty($value)) {
$request[$key] = $key;
} else {
$request[$key] = $value;
}
}
// Done
// Now let's load the output modules
// Load the Config and select the module we will load
$this->modules = require_once "Config/Modules.php";
if (!empty($request)) {
if(key_exists(array_keys($request)[0], $this->modules)) {
$load = array_shift($request);
// Load is the name of the module now
// and it removed the name of the module from $request
} else {
$load = array_keys($this->modules)[0];
}
} else {
$load = array_keys($this->modules)[0];
// Or just load the first in list module
}
// Done
// Set this module as the class parameter
$this->moduleLoaded = $load;
// Done
// The module may have multiple files in it
// We need to load the right one
if(is_array($this->modules[$load]) && !empty($this->modules[$load])) {
if(!empty($request)) {
if(in_array(array_keys($request)[0], $this->modules[$load])) {
$load .= "\\".array_shift($request);
} else {
$load .= "\\".$this->modules[$load][0];
}
} else {
$load .= "\\".$this->modules[$load][0];
}
}
// Done
// Initialize the module with all it's parameters
$module = "\Output\\" . $load;
try {
$moduleReflection = new \ReflectionClass($module);
} catch (\ReflectionException $e) {
print_r($e);
}
if($moduleReflection->isInstantiable() === TRUE) {
$module = $moduleReflection->newInstance();
// get the array od module properties
$moduleProperties = $moduleReflection->getProperties();
foreach($moduleProperties as $property) {
$moduleName = $property->name;
$property->setAccessible(true);
$propertyValue = $property->getValue($module);
// if a property is an array, then load each array element into $Property->ArrayElement etc....
if(is_array($propertyValue) === TRUE) {
$propertyClass = $this->PropertyRecursiveLoad($propertyValue, $moduleName);
$property->setValue($module, $propertyClass);
} else {
// ... or load a simple "string" property as the module
$property->setValue($module, $this->KernelModuleLoad($moduleName."\\".$moduleName));
}
}
// Done
// Initialize the needed method
// We grab only public methods
$moduleMethods = $moduleReflection->getMethods(\ReflectionMethod::IS_PUBLIC);
if(!empty($request) && $moduleReflection->hasMethod(array_keys($request)[0])) {
// the method exists, load it
$moduleMethod = $moduleReflection->getMethod(array_shift($request));
} else {
// failed to locate the method, load the default
if($moduleMethods[0]->name == "__construct") {
$moduleMethod = $moduleMethods[1];
} else {
$moduleMethod = $moduleMethods[0];
}
}
$argumentArray = array();
$moduleMethodArguments = $moduleMethod->getParameters();
foreach($moduleMethodArguments as $Parameter) {
if(isset($request[$Parameter->name]) && !empty($request[$Parameter->name]) && $request[$Parameter->name] != $Parameter->name) {
$argumentArray[$Parameter->name] = $request[$Parameter->name];
} else {
if($Parameter->isDefaultValueAvailable() === TRUE) {
$argumentArray[$Parameter->name] = $Parameter->getDefaultValue();
} else {
$argumentArray[$Parameter->name] = "";
}
}
}
$moduleMethod->invokeArgs($module, $argumentArray);
}
}
private function KernelModuleLoad(String $moduleName) {
$moduleNamespace = "\Qobox\\" . $moduleName;
try {
$kernelModuleReflection = new \ReflectionClass($moduleNamespace);
} catch (\ReflectionException $e) {
// The needed kernel module does not exist. Try to connect same module component
// Module components should not have same names as Kernel Modules!!!
$moduleNamespace = "\Modules\\" . $moduleName;
try {
$kernelModuleReflection = new \ReflectionClass($moduleNamespace);
} catch (\ReflectionException $e) {
// Even a user module does not exist. Try to connect output module component
$moduleNamespace = "\Output\\" . $moduleName;
try {
$kernelModuleReflection = new \ReflectionClass($moduleNamespace);
} catch (\ReflectionException $e) {
// nothing found... Maybe it's just an empty private property
// do nothing
}
}
}
$kernelModule = $kernelModuleReflection->newInstance();
$kernelModuleProperties = $kernelModuleReflection->getProperties(\ReflectionProperty::IS_PROTECTED);
foreach($kernelModuleProperties as $kernelDependency) {
$kernelDependency->setAccessible(true);
if(empty($kernelDependency->getValue($kernelModule))) {
$kernelModuleName = $kernelDependency->name;
$kernelDependency->setValue($kernelModule, $this->KernelModuleLoad($kernelModuleName));
}
$kernelDependency->setAccessible(false);
}
return $kernelModule;
}
private function PropertyRecursiveLoad(Array $propertyValue, String $moduleName) {
$propertyClass = new \stdClass();
foreach($propertyValue as $moduleCaller => $moduleSubname) {
if(is_array($moduleSubname) === TRUE) {
$propertyClass->$moduleCaller = $this->PropertyRecursiveLoad($moduleSubname, $moduleName."\\".$moduleCaller);
} else {
$propertyClass->$moduleSubname = $this->KernelModuleLoad($moduleName."\\".$moduleSubname);
}
}
return $propertyClass;
}
}
First of all, Kernel
grabs everything from $_REQUEST
- all the Get
and Post
data. It then sanitizes it for further use.
Next, the first key/value output module is loaded and instantiated. The Kernel
checks what private
properties it has and tries to load the respective components from Kernel
modules, Dev
modules or Output
modules. In the end, each instantiated class has private
properties containing objects.
Finally, Kernel
looks at what parameters a called public
function has. It then sets each parameter to it's value.
An example
An example to show you how it's all being processed. This goes to Modules/Test/Test.php.
<?php
namespace Modules\Test;
class Test {
public function printWorld() {
return 'World!';
}
}
And this goes into Output/Example/Example.php:
<?php
namespace Output\Example;
class Example {
private $Database;
private $Test;
public function runExample($hello) {
echo $hello. ', ';
echo $this->Test->printWorld();
echo '<br><pre>';
print_r($this);
}
}
Now, private $Test
in class Example
hold the instantiated object of class Test
.
If you query for "index.php?Example&hello=Hello", the output would be Hello, World! with printing out the database connection info afterwards.
The final statement
I've been working on this for over a year, polishing the idea, thinking through the structure etc. I would like to publish the whole framework as open-source on a new domain, qobox.net.
But I am still not sure whether I achieved perfection (which is a very relative thing) or there are still things to improve (of course there are). I'd be grateful if any of you could point out what could be improved before the first release of Qobox Framework ever.