<?PHP
NAMESPACE ILLI\Core;
USE ILLI\Core\Exception;
USE ILLI\Util\Inflector;
USE ILLI\Util\String;
USE ILLI\Core\Proto\I_Boot;
CLASS Proto EXTENDS \ILLI\Core\A_Proto IMPLEMENTS \ILLI\Core\I_Proto
{
private static $__defaultOptions =
[
I_Proto::DI_RUN => 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;
}
}
<?PHP
NAMESPACE ILLI\Core\Proto;
USE ILLI\Core\Exception;
USE ILLI\Core\Proto\I_Boot;
USE SplFixedArray;
TRAIT T_Boot
{
private static $__Core_Proto_T_Boot_Static = [];
protected function Core_Proto_T_Boot_handleLookup($propertyName, $lookupClassName, $classCache)
{
if(FALSE === isset($classCache[$propertyName]))
return
[
I_Boot::BOOT_IDX_EXISTS => 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;
}
}
<?PHP
NAMESPACE ILLI\Core\Proto;
INTERFACE I_Boot
{
// offsets in SplFixedArray
CONST BOOT_IDX_EXISTS = 0;
CONST BOOT_IDX_DEFINED = 1;
CONST BOOT_IDX_REDEFINED = 2;
CONST BOOT_IDX_INSPECT = 3;
// events
CONST ON_LOOKUP = 'ILLI\Core\Proto\I_Boot::ON_LOOKUP';
CONST ON_COMPLETE = 'ILLI\Core\Proto\I_Boot::ON_COMPLETE';
CONST ON_INIT = 'ILLI\Core\Proto\I_Boot::ON_INIT';
}
<?PHP
NAMESPACE ILLI\Core;
INTERFACE I_Proto
{
// offsets in SplFixedArray
CONST BOOT_IDX_ASP_DI_LIST = 4;
CONST BOOT_IDX_ASP_ENABLED = 5;
CONST BOOT_IDX_ASP_DI_INIT_NAME = 6;
CONST BOOT_IDX_ASP_DI_NAMES = 7;
CONST BOOT_IDX_ASP_DI_DISABLED = 8;
CONST BOOT_IDX_ASP_DI_ENABLED = 9;
CONST BOOT_IDX_CONFIG_FLAG = 4;
CONST BOOT_IDX_DEFAULT_VALUE = 4;
// bootstrapping properties
CONST DEFINE_ASPECTABLE = '__DEFINE_propAsp';
CONST DEFINE_CONFIGURABLE = '__DEFINE_propCom';
CONST DEFINE_DEFAULTS = '__DEFINE_propDef';
// default options
CONST DI_BOOT_LOAD = 'ILLI\Core\I_Proto::DI_BOOT_LOAD';
CONST DI_BOOT_INIT = 'ILLI\Core\I_Proto::DI_BOOT_INIT';
CONST DI_BOOT_EXEC = 'ILLI\Core\I_Proto::DI_BOOT_EXEC';
CONST DI_RUN = 'ILLI\Core\I_Proto::DI_RUN';
CONST DI_MAIN = 'ILLI\Core\I_Proto::DI_MAIN';
CONST DI_CLASS = 'ILLI\Core\I_Proto::DI_CLASS';
CONST DI_DELEGATE = 'ILLI\Core\I_Proto::DI_DELEGATE';
CONST DI_FILTER = 'ILLI\Core\I_Proto::DI_FILTER';
CONST DI_METHOD = 'ILLI\Core\I_Proto::DI_METHOD';
CONST DI_METHOD_FACADE = 'ILLI\Core\I_Proto::DI_METHOD_FACADE';
CONST DI_METHOD_PROTO = 'ILLI\Core\I_Proto::DI_METHOD_PROTO';
CONST DI_OBSERVER = 'ILLI\Core\I_Proto::DI_OBSERVER';
CONST DI_SIGNAL = 'ILLI\Core\I_Proto::DI_SIGNAL';
CONST DI_TO = 'ILLI\Core\I_Proto::DI_TO';
// asp type handle def
CONST ASP_TYPE_ARRAY = 'ILLI\Core\I_Proto::ASP_TYPE_ARRAY';
CONST ASP_TYPE_PROP = 'ILLI\Core\I_Proto::ASP_TYPE_PROP';
// asp method handle names
CONST ASP_FN_PRFX_ADD = 'add';
CONST ASP_FN_PRFX_CLEAR = 'clear';
CONST ASP_FN_PRFX_GET = 'get';
CONST ASP_FN_PRFX_REMOVE = 'remove';
CONST ASP_FN_PRFX_RESTORE = 'restore';
CONST ASP_FN_PRFX_SET = 'set';
// default aspectable method name
CONST ASP_TPL_FN_NAME = 'aspectable{:aspFnPrefix}{:propertyName}';
// config flag handle names
CONST CONFIG_HANDLE_ARR_ADD = 'ILLI\Core\I_Proto::CONFIG_HANDLE_ARR_ADD';
CONST CONFIG_HANDLE_ARR_EXTEND = 'ILLI\Core\I_Proto::CONFIG_HANDLE_ARR_EXTEND';
CONST CONFIG_HANDLE_ARR_MERGE = 'ILLI\Core\I_Proto::CONFIG_HANDLE_ARR_MERGE';
CONST CONFIG_HANDLE_ARR_RECURSIVE = 'ILLI\Core\I_Proto::CONFIG_HANDLE_ARR_RECURSIVE';
CONST CONFIG_HANDLE_STR_APPEND = 'ILLI\Core\I_Proto::CONFIG_HANDLE_STR_APPEND';
CONST CONFIG_HANDLE_STR_PREPEND = 'ILLI\Core\I_Proto::CONFIG_HANDLE_STR_PREPEND';
}
Comments