fruffl icon

prot. props Bootstrapper

fruffl | 02/05/13 09:30:10 AM UTC | 0 ⭐ | 291 👁️ | Never ⏰ | []
PHP |

18.16 KB

|

None

|

0 👍

/

0 👎

<?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