Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@auroraeosrose
Created March 13, 2012 14:29
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 auroraeosrose/53201bd866b30cea523a to your computer and use it in GitHub Desktop.
Save auroraeosrose/53201bd866b30cea523a to your computer and use it in GitHub Desktop.
Some braindumping - pseudocode
<?php
namespace Garnet/CMS/Core;
class App {
use Signals, Logging, Config;
protected $signals = array('configure', 'loadPluggables', 'startup', 'shutdown', 'run', 'route');
public function __construct() {
$this->register($this->signals);
$this->emit('configure');
$this->emit('loadPluggables');
$this->emit('startup');
}
public function run() {
$this->emit('run');
$this->emit('route');
}
public function __destruct() {
$this->emit('unloadPluggables');
$this->emit('shutdown');
}
// other auto handlers for items might be here
public function __doConfigure() {
// magically gets called and does configuration nonsense?
}
}
/**
* Emit the signal - runs 7 stages
*
* 1 - Run all slots attached to a signal (default slot) marked RUN_FIRST
* 2 - Run any hooks - slots assigned to ALL instances for a specific signal
* 3 - Run all normal slots marked RUN_FIRST
* 4 - Run all the default slots marked RUN_LAST
* 5 - Run all the normal slots marked RUN_LAST
* 6 - Run all the normal slots marked RUN_CLEANUP
* 7 - Run all slots attached to a signal (default slot) marked RUN_CLEANUP
* note these always run regardless of bubble
*
* This series is almost identical to the Gobjects emission list
* Slots are called in the order they were connected in - any slot
* may stop emission of a stage by returning a TRUE value except in default cleanup
*
* If, at any point during emission except for the cleanup state, one of the closures or emission hook emits
* the same signal on the same instance, emission is restarted from the RUN_FIRST state
*
* @return void
*/
public function emit($name) {
if (is_null($this->state)) {
$this->state = array();
}
// retrieve our signal from our hash map
$signal = self::getSignal($name);
if (is_null($signal)) {
// bail, bad signal
throw new InvalidArgumentException('Signal ' . $name . ' is not a registered signal of ' . get_class($this));
}
// fast check for recursion - if we're in an emit and signal matches
// signal then we set the state to restart and return
if (isset($this->state[$name]) && $this->state[$name] === self::EMISSION_RUN) {
$this->state[$name] = self::EMISSION_RESTART;
return;
}
// check for blocked signal
if ($signal->isBlocked()) {
$this->state[$signal] = self::EMISSION_STOP;
return;
}
// prepare args
$args = func_get_args();
// take off signal name
array_shift($args);
// set up some other variables
$slot = $signal->getSlot();
$class = get_class($this);
if ($slot) {
$position = $slot->getPosition();
} else {
$position = null;
}
// Where we restart emissions for recursion
emit_restart:
$bubble = false;
$this->state[$name] = self::EMISSION_RUN;
// stage 1: RUN_FIRST default handlers
if ($position == Slot::RUN_FIRST) {
// magic of closures and __invoke
$slot($args);
// handle our possible states
if ($this->state[$name] === self::EMISSION_STOP) {
goto emit_cleanup;
}
if ($this->state[$name] === self::EMISSION_RESTART) {
goto emit_restart;
}
}
// stage 2 - run any hooked slots
if (isset(self::$hooks[$class]) && isset(self::$hooks[$class][$name])) {
foreach(self::$hooks[$class][$name] as $slot => $hook_args) {
$bubble = $slot($args + $slot_args);
$bubble = $signal->callAccumulator($bubble);
if ($bubble === true || $this->state[$name] === self::EMISSION_STOP) {
goto emit_cleanup;
} else if ($this->state[$name] === self::EMISSION_RESTART) {
goto emit_restart;
}
}
unset($slot, $hook_args);
}
// stage 3 - normally connected handlers not marked with runlast
if (isset($this->slots[$name])) {
foreach($this->slots[$name] as $slot) {
if ($slot->getPosition() !== SLOT::RUN_FIRST) {
continue;
}
$bubble = $slot($args + $this->slots[$name][$slot]);
$bubble = $signal->callAccumulator($bubble);
// handle our possible states
if ($bubble === true || $this->state[$name] === self::EMISSION_STOP) {
goto emit_cleanup;
} else if ($this->state[$name] === self::EMISSION_RESTART) {
goto emit_restart;
}
}
unset($slot);
}
// stage 4 - RUN_LAST default handlers
if ($position == Slot::RUN_LAST) {
// magic of closures and __invoke
$slot($args);
// handle our possible states
if ($this->state[$name] === self::EMISSION_STOP) {
goto emit_cleanup;
}
if ($this->state[$name] === self::EMISSION_RESTART) {
goto emit_restart;
}
}
// stage 5 - connected slots marked RUN_LAST
if (isset($this->slots[$name])) {
foreach($this->slots[$name] as $slot) {
if ($slot->getPosition() !== SLOT::RUN_LAST) {
continue;
}
$bubble = $slot($args + $this->slots[$name][$slot]);
$bubble = $signal->callAccumulator($bubble);
// handle our possible states
if ($bubble === true || $this->state[$name] === self::EMISSION_STOP) {
goto emit_cleanup;
} else if ($this->state[$name] === self::EMISSION_RESTART) {
goto emit_restart;
}
}
unset($slot);
}
// stage 6 - connected slots marked RUN_CLEANUP
if (isset($this->slots[$name])) {
foreach($this->slots[$name] as $slot) {
if ($slot->getPosition() !== SLOT::RUN_CLEANUP) {
continue;
}
$bubble = $slot($args + $this->slots[$name][$slot]);
$bubble = $signal->callAccumulator($bubble);
// handle our possible states
if ($bubble === true || $this->state[$name] === self::EMISSION_STOP) {
goto emit_cleanup;
} else if ($this->state[$name] === self::EMISSION_RESTART) {
goto emit_restart;
}
}
unset($slot);
}
// stage 7 - RUN_CLEANUP default slots
emit_cleanup:
if ($position == Slot::RUN_CLEANUP) {
// magic of closures and __invoke
$slot($args);
$bubble = $signal->callAccumulator($bubble);
// handle our possible states
if ($this->state[$name] === self::EMISSION_RESTART) {
goto emit_restart;
}
}
$this->state[$name] = self::EMISSION_STOP;
return $bubble;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment