'merge', 'bob', 'foo' ]; protected static $__defaultConfig = [ 'baz' => 1, 'bob' => 10, 'foo' => 100, ]; } CLASS B EXTENDS A { protected static $__autoConfig = [ 'baz', 'bob' => 'merge', 'bar' ]; protected static $__defaultConfig = [ 'baz' => 5, 'bob' => 50, 'bar' => 5000, ]; } /* A::baz => merge A::bob A::foo B : A B::baz B::bob => merge B::bar expected: B::baz B::bob => merge B::foo B::bar */ $fu = new B; var_dump($fu); return; /* object(root\B)#6 (2) { ["__initConfig":protected]=> array(5) { ["__run"]=> bool(true) ["baz"]=> int(5) ["bob"]=> int(50) ["foo"]=> int(100) ["bar"]=> int(5000) } ["debug"]=> &array(2) { ["__autoConfig"]=> array(4) { [0]=> string(3) "baz" ["bob"]=> string(5) "merge" [1]=> string(3) "bar" [2]=> string(3) "foo" } ["__defaultConfig"]=> array(4) { ["baz"]=> int(5) ["bob"]=> int(50) ["foo"]=> int(100) ["bar"]=> int(5000) } } } */ class fu { /** * @late-state */ protected static $__defaultConfig = []; /** * @late-state */ protected static $__autoConfig = []; /** * @read-only */ protected $__initConfig = []; /** * @cache */ private static $__configCache = []; /** * @cache */ private static $__parentClassCache = []; /** * @template */ private static $__baseConfig = [ '__run' => TRUE ]; final public function __construct(array $__options = []) { $this->collect()->__initConfig = $this->extend ( TRUE, self::$__baseConfig, self::$__configCache[get_called_class()]['__defaultConfig'], $__options ); $this->debug =& self::$__configCache[get_called_class()]; return TRUE === $this->__initConfig['__run'] ? $this->__run() : $this; } protected function __run() { } public static function parents($__className = NULL) { $parentClasses = &self::$__parentClassCache; $class = NULL === $__className ? get_called_class() : $__className; if(isset($parentClasses[$class])) return $parentClasses[$class]; $classes = []; do { $classes[] = $class; } while($class = get_parent_class($class)); $parentClasses[$class] = $classes; return $classes; } /** * * * replace: * $c->extend(['myNewBar' => ['foo' => 'blop']]); * $c->extend(['myNewBar' => ['fob' => 'baz']]); * * ["myNewBar"]=> * array(1) { * ["fob"]=> * string(3) "baz" * } * * * extend: * $c->extend(['myNewBar' => ['foo' => 'blop']]); * $c->extend(true, ['myNewBar' => ['fob' => 'baz']]); * * ["myNewBar"]=> * array(2) { * ["foo"]=> * string(4) "blop" * ["fob"]=> * string(3) "baz" * } * * extend multi: * $c->extend(['myNewBar' => ['foo' => 'blop']]); * $c->extend(true, $c, ['myNewBar' => ['fob' => 'baz']], ['myNewBar' => ['bla' => 'baz']]); * * ["myNewBar"]=> * array(3) { * ["foo"]=> * string(4) "blop" * ["fob"]=> * string(3) "baz" * ["bla"]=> * string(3) "baz" * } * * @callable array $__data * @callable array $__target, array $__data * @callable bool $__recursive, array $__data * @callable bool $__recursive, array $__target, array $__data * @param mixed * @return array|this * @note $tar is not a referenced array */ public function extend() { $args = func_get_args(); $tar = $args[0]; $i = 1; $length = count($args); $rec = false; if($tar === true || $tar === false) { $rec = $tar; $tar = isset($args[1]) ? $args[1] : []; $i = 2; } if(!is_array($tar) && !is_object($tar)) $tar = []; if($length === $i) { $tar = $this; --$i; } for( ; $i < $length; $i++) { if(NULL === ($opt = $args[$i])) continue; foreach($opt as $key => $value) { switch(TRUE): case is_array($tar): $tar[$key] = (TRUE === $rec && is_array($value) && isset($tar[$key]) && is_array($tar[$key])) ? $this->extend($rec, $tar[$key], $value) : $value; break; case is_object($tar): $tar->$key = (TRUE === $rec && is_array($value) && isset($tar->$key) && is_array($tar->$key)) ? $this->extend($rec, $tar->$key, $value) : $value; break; case is_string($tar): $tar::${$key} = (TRUE === $rec && is_array($value) && is_array($tar::${$key})) ? $this->extend($rec, $tar::${$key}, $value) : $value; break; endswitch; } } return $tar; } private function __collect() { $class = get_called_class(); if(isset(self::$__configCache[$class])) return $this; self::$__configCache[$class] = []; // merge + cache __autoConfig, __defaultConfig from childClass up to baseClass // __autoConfig: array_unique(array_merge()) // __defaultConfig: childClass extends recursively parentClass $cache = &self::$__configCache[$class]; foreach($this->parents($class) as $lookup) { foreach(['__autoConfig', '__defaultConfig'] as $property) { if(!isset($lookup::${$property})) continue; $value = $lookup::${$property}; if(!isset($cache[$property])) $cache[$property] = []; switch($property): case '__autoConfig': foreach($value as $key => $flag) { $search = is_string($key) ? $key : $flag; if(isset($cache[$property][$search])) continue; $cache[$property][$search] = is_string($key) ? $flag : ''; } break; case '__defaultConfig': if(is_array($cache[$property]) && is_array($value)) $cache[$property] = $this->extend(TRUE, $value, $cache[$property]); break; endswitch; } } return $this; } }