TRUE, I_Proto::DI_MAIN => '__main', I_Proto::DI_CLASS => [], I_Proto::DI_DELEGATE => [], I_Proto::DI_FILTER => [], I_Proto::DI_METHOD => [], I_Proto::DI_OBSERVER => [], I_Proto::DI_METHOD_PROTO => [], I_Proto::DI_SIGNAL => [], I_Proto::DI_TO => [ 'string' => 'toString', 'array' => 'toArray' ] ]; protected $__initConfig = []; USE \ILLI\Core\Proto\T_Boot; /** * bootstrap static protected vars and store default class-setup in a static dict * default strapping: * ::$propertyConfigurable: a list of accessible/configurable properties including constructor-options * ::$propertyDefaults: the default Values of defined class-properties * ::$propertyAspectable: a list of injectable class-properties (observer, signal, facade, strategy etc.) * * @discus: diff for constructor-configurable and runtime-configurable: * unset from whitelist on @__exec() ?? */ final public function __construct(array $__options = []) { // $obj->$prop not exist: // bypass property... public $obj->$commonProp is evil: the value is avail in __initConfig and never modifiable from outside // modify prop: // not defined as CONFIGURABLE -> not public -> not modifiable // __construct(array $__options): // not defined as CONFIGURABLE: $__options[prop] is stored in __initConfig[prop] -> bypass $obj->$prop // DEFAULT VALUE, construct $__options, modify prop: // not defined as ASPECTABLE: use direct access // @__boot // ON_LOOKUP | ON_COMPLETE => static results // ON_INIT $obj::__construct-event $__boot = [ // DEFAULT VALUE // the default values // static $obj::$__DEFINE_propDef I_Proto::DEFINE_DEFAULTS => [ // merge DEF -> value: called class overwrites base-class except $__defaultOptions (extend) // write $c to static cache I_Boot::ON_LOOKUP => function($c, $n, array $l) { array_walk($l, function($v, $p) use (&$c, $n) { $c[$p] = $this->Core_Proto_T_Boot_handleLookup($p, $n, $c) + [ I_Proto::BOOT_IDX_DEFAULT_VALUE => $v ]; $m = &$c[$p][I_Proto::BOOT_IDX_DEFAULT_VALUE]; if(isset($m) && isset(self::$__defaultOptions[$p]) && is_array($m) && is_array($v)) $m = $this->extend(TRUE, $v, $m); }); return $c; }, // load $c from static cache // save merged defaults in __initConfig on __construct() I_Boot::ON_INIT => function($c) { array_walk($c, function($s, $p) { $this->__initConfig[$p] = $s[I_Proto::BOOT_IDX_DEFAULT_VALUE]; }); return $c; } ], // ASPECTABLE PROP // aspProperty is injectable // [$prop => false] overwrites parent::$prop to bypass asp // [$prop => flag] overwrites parent::$prop: 'array', 'property' OR ['myFunc1', 'myFunc2'] OR 'myFunc1|myFunc2' // inject-accessor is the camelized version of property-name + event-name: aspectable{:Event}{:Property}() -> aspectableSetParam // protected accessor is property({:event}, {:property}[, {:value}]) // public accessor is property{:Event}([{:property}, {:value}]) @see CONFIGURABLE // to apply asp-events for DEFAULT-values register all events before calling @__exec()! ($__option[__run] = FALSE) // static $obj::$__DEFINE_propAsp I_Proto::DEFINE_ASPECTABLE => [ // merge ASP -> flag: called class extends base-class // write $c to static cache I_Boot::ON_LOOKUP => function($c, $n, $l) { $d = [ I_Proto::ASP_TYPE_PROP => [I_Proto::ASP_FN_PRFX_SET, I_Proto::ASP_FN_PRFX_RESTORE], I_Proto::ASP_TYPE_ARRAY => [I_Proto::ASP_FN_PRFX_SET, I_Proto::ASP_FN_PRFX_RESTORE, I_Proto::ASP_FN_PRFX_ADD, I_Proto::ASP_FN_PRFX_REMOVE, I_Proto::ASP_FN_PRFX_CLEAR], ]; array_walk($l, function($v, $p) use (&$c, $n, $d) { if(is_integer($p)) { $p = $v; $v = TRUE; } $c[$p] = $this->Core_Proto_T_Boot_handleLookup($p, $n, $c) + [ I_Proto::BOOT_IDX_ASP_ENABLED => is_string($v) ? TRUE : $v, I_Proto::BOOT_IDX_ASP_DI_LIST => $a = is_string($u = is_bool($v) ? I_Proto::ASP_TYPE_PROP : $v) ? isset($d[$u]) ? $d[$u] : explode('|', $u) : $u, I_Proto::BOOT_IDX_ASP_DI_INIT_NAME => !isset($a[0]) ? isset($d[$u]) ? $d[$u][0] : I_Proto::ASP_FN_PRFX_SET : $a[0], I_Proto::BOOT_IDX_ASP_DI_NAMES => [], I_Proto::BOOT_IDX_ASP_DI_DISABLED => [], I_Proto::BOOT_IDX_ASP_DI_ENABLED => [] ]; }); return $c; }, // create accessor tuplet // write $c to static cache I_Boot::ON_COMPLETE => function($c) { array_walk($c, function(&$s, $p) { if(FALSE === $s[I_Boot::BOOT_IDX_EXISTS] || FALSE === $s[I_Proto::BOOT_IDX_ASP_ENABLED]) return; array_walk($s[I_Proto::BOOT_IDX_ASP_DI_LIST], function($a) use (&$s, $p) { $s[I_Proto::BOOT_IDX_ASP_DI_NAMES] += [$a => $m = String::insert(I_Proto::ASP_TPL_FN_NAME, ['aspFnPrefix' => Inflector::camelize($a), 'propertyName' => Inflector::camelize($p)])]; $s[(($e = method_exists($this, $m)) ? I_Proto::BOOT_IDX_ASP_DI_ENABLED : I_Proto::BOOT_IDX_ASP_DI_DISABLED)] += [$a => ($e ? $m : $p)]; }); }); return $c; } ], // CONFIGURABLE // comProperty is a runtimeProperty // accessible from public scope // accessibble from __construct // [$prop => flag] overwrites parent::$prop: behavior (merge, extend...) // public accessor is property{:Event}([{:property}, {:value}]) // static $obj::$__DEFINE_propCom I_Proto::DEFINE_CONFIGURABLE => [ // merge CONFIGURABLE -> flag: called class extends base-class // write $c to static cache I_Boot::ON_LOOKUP => function($c, $n, array $l) { array_walk($l, function($v, $p) use (&$c, $n) { if(is_integer($p)) { $p = $v; $v = FALSE; } $c[$p] = $this->Core_Proto_T_Boot_handleLookup($p, $n, $c) + [ I_Proto::BOOT_IDX_CONFIG_FLAG => $v ]; }); return $c; } ] ]; // @__dest // finalizer $__dest = function() { $this->__initConfig[I_Proto::DI_RUN] = TRUE; unset($this->__initConfig[I_Proto::DI_BOOT_EXEC]); }; // @__init // write property-value $__init = isset($__options[I_Proto::DI_BOOT_INIT]) && is_callable($__options[I_Proto::DI_BOOT_INIT]) ? function($p, $v) use ($__options) { $__options[I_Proto::DI_BOOT_INIT]($p, $v, self::$__Core_Proto_T_Boot_Static[get_called_class()]); } // write $obj->$prop when prop is not defined as ASP // write $obj->$prop when prop is defined as direct-access // write $obj->$prop when prop ASP is disabled // delegate to $obj->property{:Event}($value) : function($p, $v) { $a = self::$__Core_Proto_T_Boot_Static[get_called_class()][I_Proto::DEFINE_ASPECTABLE]; if(!isset($a[$p])) return $this->$p = $v; $s = $a[$p]; if(FALSE === $s[I_Proto::BOOT_IDX_ASP_ENABLED]) return $this->$p = $v; if(!isset($s[I_Proto::BOOT_IDX_ASP_DI_INIT_NAME])) throw new Exception('The default handle is "set". No handler found.'); $u = $s[I_Proto::BOOT_IDX_ASP_DI_INIT_NAME]; if(isset($s[I_Proto::BOOT_IDX_ASP_DI_DISABLED][$u])) return $this->$p = $v; if(isset($s[I_Proto::BOOT_IDX_ASP_DI_ENABLED][$u])) return $this->$s[I_Proto::BOOT_IDX_ASP_DI_ENABLED][$u]($v); throw new Exception('Setter {:u} not avail.', compact('u')); }; // @__exec // load default property-values from DEFAULTS // load accessible properties from CONFIGURABLE // handle data by CONFIGURABLE-flag // write initial property-values using @__init // unset accessible properties from __initConfig $__exec = isset($__options[I_Proto::DI_BOOT_EXEC]) && is_callable($__options[I_Proto::DI_BOOT_EXEC]) ? function() use ($__init, $__dest, $__options) { $__options[I_Proto::DI_BOOT_EXEC]($__init, self::$__Core_Proto_T_Boot_Static[get_called_class()]); $__dest(); } : function() use ($__init, $__dest) { $_ = self::$__Core_Proto_T_Boot_Static[get_called_class()]; $c = $_[I_Proto::DEFINE_CONFIGURABLE]; $d = $_[I_Proto::DEFINE_DEFAULTS]; $i = &$this->__initConfig; $t = []; // load DEFAULT VALUE when prop exists as $obj->$prop array_walk($d, function($s, $p) use (&$t) { if(FALSE === $s[I_Boot::BOOT_IDX_EXISTS]) return; $t[$p] = $s[I_Proto::BOOT_IDX_DEFAULT_VALUE]; }); // load CONFIGURABLE when prop exists in __initConfig // load CONFIGURABLE when prop exists as $obj->$prop // handle __initConfig[prop] by CONFIGURABLE-flag array_walk($c, function($s, $p) use (&$i, &$t) { if(FALSE === isset($i[$p]) || FALSE === $s[I_Boot::BOOT_IDX_EXISTS]) return; $v = $i[$p]; switch(TRUE): case is_array($v) && is_array($this->$p): switch($s[I_Proto::BOOT_IDX_CONFIG_FLAG]): case I_Proto::CONFIG_HANDLE_ARR_ADD: $v = $this->$p + $v; break; case I_Proto::CONFIG_HANDLE_ARR_EXTEND: $v = $this->extend($this->$p, $v); break; case I_Proto::CONFIG_HANDLE_ARR_MERGE: $v = array_merge($this->$p, $v); break; case I_Proto::CONFIG_HANDLE_ARR_RECURSIVE: $v = $this->extend(TRUE, $this->$p, $v); break; endswitch; break; case is_string($v) && is_string($this->$p): switch($s[I_Proto::BOOT_IDX_CONFIG_FLAG]): case I_Proto::CONFIG_HANDLE_STR_APPEND: $v = $this->$p.$v; break; case I_Proto::CONFIG_HANDLE_STR_PREPEND: $v = $v.$this->$p; break; endswitch; break; endswitch; $t[$p] = &$v; }); // $__init() array_walk($t, function(&$v, $p) use (&$i, $__init) { unset($i[$p]); $__init($p, $v); }); $__dest(); }; // *magic* $this->Core_Proto_T_Boot_emit($this->extend(TRUE, (isset($__options[I_Proto::DI_BOOT_LOAD]) ? $__options[I_Proto::DI_BOOT_LOAD] : []), $__boot)); unset($__options[I_Proto::DI_BOOT_LOAD]); unset($__options[I_Proto::DI_BOOT_EXEC]); unset($__options[I_Proto::DI_BOOT_INIT]); $this->__initConfig = $this->extend(TRUE, $this->__initConfig, self::$__defaultOptions, $__options); TRUE === $this->__initConfig[I_Proto::DI_RUN] ? $__exec() : $this->__initConfig[I_Proto::DI_BOOT_EXEC] = $__exec; // @see ::run() unset($__boot); unset($__init); unset($__exec); unset($__dest); } public function run() { if(isset($this->__initConfig[I_Proto::DI_BOOT_EXEC])) $this->__initConfig[I_Proto::DI_BOOT_EXEC](); return $this; } public function extend() { $a = func_get_args(); $t = $a[0]; $i = 1; $l = count($a); $r = FALSE; if(is_bool($t)) { $r = $t; $t = isset($a[1]) ? $a[1] : []; $i = 2; } if(FALSE === is_array($t) && FALSE === is_object($t) && FALSE === is_string($t)) $t = []; if($l === $i) { $t = $this; --$i; } for( ; $i < $l; $i++) { if(NULL === ($o = $a[$i])) continue; foreach($o as $p => $v) { switch(TRUE): case is_array($t): $t[$p] = (TRUE === $r && is_array($v) && isset($t[$p]) && is_array($t[$p])) ? $this->extend($r, $t[$p], $v) : $v; break; case is_object($t): $t->$p = (TRUE === $r && is_array($v) && isset($t->$p) && is_array($t->$p)) ? $this->extend($r, $t->$p, $v) : $v; break; case is_string($t): if(FALSE === class_exists($t)) throw new Exception('Class {:t} not found.', compact('t')); if(FALSE === property_exists($t, $p)) throw new Exception('Property {:t}::${:p} not found.', compact('t', 'p')); $t::$$p = (TRUE === $r && is_array($v) && is_array($t::${$p})) ? $this->extend($r, $t::$$p, $v) : $v; break; default: throw new Exception('Target not writable.'); endswitch; } } return $t; } // test public function propertySet($property, $value) { $_ = self::$__Core_Proto_T_Boot_Static[get_called_class()]; $c = $_[I_Proto::DEFINE_CONFIGURABLE]; if(!isset($c[$property])) throw new Exception('{:property} is not public.', compact('property')); $this->$property = $value; // stub return $this; } public function dump() { return self::$__Core_Proto_T_Boot_Static; } } isset($this->$propertyName) || property_exists($this, $propertyName), I_Boot::BOOT_IDX_DEFINED => $lookupClassName, I_Boot::BOOT_IDX_REDEFINED => FALSE, I_Boot::BOOT_IDX_INSPECT => $lookupClassName.'::$'.$propertyName ]; $r = $classCache[$propertyName]; if(FALSE === $r[I_Boot::BOOT_IDX_REDEFINED]) { $r[I_Boot::BOOT_IDX_REDEFINED] = $r[I_Boot::BOOT_IDX_DEFINED]; $r[I_Boot::BOOT_IDX_INSPECT] = $r[I_Boot::BOOT_IDX_DEFINED].'::$'.$propertyName; } $r[I_Boot::BOOT_IDX_DEFINED] = $lookupClassName; return $r; } protected function Core_Proto_T_Boot_emit(array $__lookupTable = []) { array_walk($__lookupTable, function($t, $p) { if(is_integer($p)) { $p = $t; $t = []; } $l = get_called_class(); $s = &self::$__Core_Proto_T_Boot_Static[$l][$p]; if(isset($s)) goto _init; $c = []; do { if(FALSE === isset($l::$$p)) continue; $c = is_callable($h = isset($t[I_Boot::ON_LOOKUP]) ? $t[I_Boot::ON_LOOKUP] : NULL) ? $h($c, $l, $l::$$p) : $l::$$p; } while($l = get_parent_class($l)); $c = is_callable($h = isset($t[I_Boot::ON_COMPLETE]) ? $t[I_Boot::ON_COMPLETE] : NULL) ? $h($c) : $c; array_walk($c, function($v, $p) use (&$s) { $s[$p] = SplFixedArray::fromArray($v, TRUE); }); _init: if(is_callable($h = isset($t[I_Boot::ON_INIT]) ? $t[I_Boot::ON_INIT] : NULL)) $h($s); }); return $this; } }