Skip to content

Instantly share code, notes, and snippets.

@kapv89
Created February 24, 2014 10:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kapv89/9184949 to your computer and use it in GitHub Desktop.
Save kapv89/9184949 to your computer and use it in GitHub Desktop.
<?php namespace div\Core;
use Illuminate\Container\Container;
use ReflectionFunction;
use Closure;
use div;
class Module extends Container {
protected static $scriptClosureMap = [];
protected $name;
protected $root;
protected $config;
protected $vent;
protected $modules = [];
protected $starter = null;
protected $initialized = false;
protected $started = false;
const initializing = 'initializing';
const initialized = 'initialized';
const starting = 'starting';
const started = 'started';
const stopping = 'stopping';
const stopped = 'stopped';
public function __construct($name, $root, $config = null, Closure $starter = null, Vent $vent = null)
{
$this->name = $name;
$this->root = realpath($root);
$this->config = $config instanceof Config ? $config : new Config((array) $config);
$this->starter = $starter;
$this->vent = $vent ? : new Vent;
}
public function getName()
{
return $this->name;
}
public function path($path = '')
{
return realpath(strpos($path, '/') === 0 ? $path : $this['root'] . '/' . $path);
}
public function config($key = null, $default = null)
{
return $this->config->get($key, $default);
}
/** event methods **/
public function on($event, Closure $callback)
{
$this->vent->on($event, $callback);
return $this;
}
public function off($event)
{
$this->vent->off($event);
}
public function fire($event, $args = array())
{
$args = array_merge([$this], $args);
$this->vent->fire($event, $args);
return $this;
}
/** module nesting methods **/
public function loadModules(array $modules)
{
foreach ($modules as $key => $module) {
if(is_string($key)) {
$module = (array) $module;
$module['name'] = $key;
}
$this->loadModule($module);
}
return $this;
}
public function loadModule($module)
{
if (! $module instanceof Module) {
if (is_string($module)) {
$module = ['name' => $module];
}
$module = $this->createModule((array) $module);
}
if (in_array($module->getName(), $this->modules)) {
// stop existing module
$this[$module->getName()]->stop();
} else {
// append the module to the list of modules
$this->modules[] = $module->getName();
}
$this[$module->getName()] = $module;
return $this;
}
protected function createModule(array $conf)
{
if (! isset($conf['name'])) {
throw new \InvalidArgumentException('A Module needs a name ' : json_encode($conf));
}
$name = $conf['name'];
$baseDir = $this->config('path.modules', 'modules');
$root = isset($conf['root']) ? $conf['root'] : $this->path($baseDir.'/'.$name);
$configFile = isset($conf['config']) ? $root.'/'.$conf['config'] : $root.'/'.'config.php';
if (file_exists($configFile)) {
$config = include $configFile;
} else {
$config = [];
}
$type = isset($conf['type']) ? $conf['type'] : __CLASS__;
$class = $this->getAlias($type);
if (trim('\\', $class) !== __CLASS__ and ! is_subclass_of($class, __CLASS__)) {
throw new \InvalidArgumentException('Module Class should be a subtype of div\Core\Module : ' . $class);
}
$starter = isset($conf['starter']) ? $conf['starter'] : null;
$vent = isset($conf['vent']) ? $conf['vent'] : null
return $this->make($type, [$name, $root, $config, $starter, $vent]);
}
/** lifecycle methods **/
protected function setStarter(Closure $starter = null)
{
if ($starter instanceof Closure) {
$this->starter = $starter;
} elseif (file_exists($startFile = $this->path($this->config('path.start', 'start.php')))) {
$this->starter = include $startFile;
} elseif(! $this->starter instanceof Closure) {
$this->starter = function () { };
}
// bind the module as $this to starter
$this->starter = $this->starter->bindTo($this, null);
}
protected function initModules()
{
// initialize all the submodules too
foreach($this->modules as $name) {
$this[$name]->init();
}
}
public function init(Closure $starter = null)
{
if ($this->initialized) {
return $this;
}
$this->fire(static::initializing);
$this->setStarter($starter);
$this->initModules();
$this->initialized = true;
$this->fire(static::initialized);
return $this
}
/////
public function getStarter()
{
return $this->starter;
}
/////
protected function runStarter($arg)
{
if(is_array($arg)) {
$deps = $arg;
$parent = null;
} elseif($arg instanceof Module) {
$deps = [];
$parent = $arg;
} else {
throw new \InvalidArgumentException(
'Module::start() only takes dependencies or a parent module for supplying dependencies as argument'
);
}
// start the module itself
if($parent) {
$deps = array_map(
function ($p) use ($parent) { return $parent[$p->getName()]; },
((new ReflectionFunction($this->starter))->getParameters()
);
}
div\call($this->starter, $deps);
}
protected function startModules()
{
// start all the submodules
foreach ($this->modules as $name) {
$this[$name]->start($this);
}
}
public function start($arg)
{
// don't start the module if it is already started
if ($this->started) {
return $this;
}
$this->init();
$this->fire(static::starting);
$this->startModules();
$this->runStarter($arg);
$this->started = true;
$this->fire(static::started);
return $this;
}
/////
protected function stopModules()
{
foreach ($this->modules as $name) {
$this[$name]->stop();
}
}
public function stop()
{
$this->fire(static::stopping);
$this->stopModules();
$this->initialized = false;
$this->started = false;
$this->fire(static::stopped);
return $this;
}
/////
public function execScript($script, array $args = array()) {
$script = realpath($script);
if (! isset(static::$scriptClosureMap[$script])) {
static::$scriptClosureMap[$script] = include $script;
}
return $this->exec(static::$scriptClosureMap[$script], $args);
}
public function exec(Closure $closure, array $args = array()) {
div\call($closure->bindTo($this), $args);
return $this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment