3v4l.org

run code in 300+ PHP versions simultaneously
<?php class A { } class B { public $a; public function __construct(A $a) { $this->a = $a; } } class C { public $b; public function __construct(B $b) { $this->b = $b; } } class D { public $c; public function __construct(C $c) { $this->c = $c; } } class E { public $d; public function __construct(D $d) { $this->d = $d; } } class F { public $e; public function __construct(E $e) { $this->e = $e; } } class G { public $f; public function __construct(F $f) { $this->f = $f; } } class H { public $g; public function __construct(G $g) { $this->g = $g; } } class I { public $h; public function __construct(H $h) { $this->h = $h; } } class J { public $i; public function __construct(I $i) { $this->i = $i; } } class K { public $j; public function __construct(K $k) { $this->k = $k; } } class Dice { private $rules = []; private $cache = []; private $instances = []; public function addRule($name, Rule $rule) { $this->rules[ltrim($name, '\\')] = $rule; } public function getRule($name) { if (isset($this->rules[ltrim($name, '\\')])) return $this->rules[ltrim($name, '\\')]; foreach ($this->rules as $key => $rule) { if ($rule->instanceOf === null && $key !== '*' && is_subclass_of($name, $key) && $rule->inherit === true) return $rule; } return isset($this->rules['*']) ? $this->rules['*'] : new Rule; } public function create($component, array $args = [], $forceNewInstance = false) { if (!$forceNewInstance && isset($this->instances[$component])) return $this->instances[$component]; if (!isset($this->cache[$component])) { $rule = $this->getRule($component); $class = new \ReflectionClass(empty($rule->instanceOf) ? $component : $rule->instanceOf); $constructor = $class->getConstructor(); $params = $constructor ? $this->getParams($constructor, $rule) : null; $this->cache[$component] = function($args, $forceNewInstance) use ($component, $rule, $class, $constructor, $params) { if ($rule->shared) { if ($constructor) { try { $this->instances[$component] = $object = $class->newInstanceWithoutConstructor(); $constructor->invokeArgs($object, $params($args)); } catch (\ReflectionException $r) { $this->instances[$component] = $object = $class->newInstanceArgs($params($args)); } } else { $this->instances[$component] = $object = $class->newInstanceWithoutConstructor(); } } else $object = $params ? $class->newInstanceArgs($params($args)) : new $class->name; if (!empty($rule->call)) foreach ($rule->call as $call) $class->getMethod($call[0])->invokeArgs($object, call_user_func($this->getParams($class->getMethod($this->expand($call[0])), new Rule), $call[1])); return $object; }; } return $this->cache[$component]($args, $forceNewInstance); } private function expand($param, array $share = []) { if (is_array($param)) return array_map(function($p) use($share) { return $this->expand($p, $share); }, $param); if ($param instanceof Instance) return $this->create($param->name, $share); else if (is_callable($param)) return $param($this); return $param; } private function getParams(\ReflectionMethod $method, Rule $rule) { $subs = empty($rule->substitutions) ? null :$rule->substitutions; $paramClasses = []; foreach ($method->getParameters() as $param) $paramClasses[] = $param->getClass() ? $param->getClass()->name : null; return function($args) use ($paramClasses, $rule, $subs) { $share = empty($rule->shareInstances) ? [] : array_map([$this, 'create'], $rule->shareInstances); if (!empty($share) || !empty($rule->constructParams)) $args = array_merge($args, $this->expand($rule->constructParams, $share), $share); $parameters = []; foreach ($paramClasses as $class) { if (!empty($args)) for ($i = 0; $i < count($args); $i++) { if ($class && $args[$i] instanceof $class) { $parameters[] = array_splice($args, $i, 1)[0]; continue 2; } } if (!empty($subs) && isset($subs[$class])) $parameters[] = is_string($subs[$class]) ? $this->create($subs[$class]) : $this->expand($subs[$class]); else if (!empty($class)) $parameters[] = $this->create($class, $share, !empty($rule->newInstances) && in_array(strtolower($class), array_map('strtolower', $rule->newInstances))); else if (!empty($args)) $parameters[] = array_shift($args); } return $parameters; }; } } class Rule { public $shared = false; public $constructParams = []; public $substitutions = []; public $newInstances = []; public $instanceOf; public $call = []; public $inherit = true; public $shareInstances = []; } class Instance { public $name; public function __construct($instance) { $this->name = $instance; } } class Dice2 { private $rules = []; private $cache = []; private $instances = []; public function addRule($name, Rule $rule) { $this->rules[ltrim($name, '\\')] = $rule; } public function getRule($name) { if (isset($this->rules[ltrim($name, '\\')])) return $this->rules[ltrim($name, '\\')]; foreach ($this->rules as $key => $rule) { if ($rule->instanceOf === null && $key !== '*' && is_subclass_of($name, $key) && $rule->inherit === true) return $rule; } return isset($this->rules['*']) ? $this->rules['*'] : new Rule; } public function create($component, array $args = [], $forceNewInstance = false) { if (!$forceNewInstance && isset($this->instances[$component])) return $this->instances[$component]; if (!isset($this->cache[$component])) { $rule = $this->getRule($component); $class = new \ReflectionClass(empty($rule->instanceOf) ? $component : $rule->instanceOf); $constructor = $class->getConstructor(); $params = $constructor ? $this->getParams($constructor, $rule) : null; $this->cache[$component] = function($args, $forceNewInstance) use ($component, $rule, $class, $constructor, $params) { if ($rule->shared) { $this->instances[$component] = $object = $class->isInternal() && $constructor ? $class->newInstanceArgs($params($args)) : $class->newInstanceWithoutConstructor(); if ($constructor && !$class->isInternal()) $constructor->invoke($object, ...$params($args)); } else $object = $params ? new $class->name(...$params($args)) : new $class->name; if (!empty($rule->call)) foreach ($rule->call as $call) $class->getMethod($call[0])->invokeArgs($object, call_user_func($this->getParams($class->getMethod($this->expand($call[0])), new Rule), $call[1])); return $object; }; } return $this->cache[$component]($args, $forceNewInstance); } private function expand($param, array $share = []) { if (is_array($param)) return array_map(function($p) use($share) { return $this->expand($p, $share); }, $param); if ($param instanceof Instance) return $this->create($param->name, $share); else if (is_callable($param)) return $param($this); return $param; } private function getParams(\ReflectionMethod $method, Rule $rule) { $subs = empty($rule->substitutions) ? null :$rule->substitutions; $paramClasses = []; foreach ($method->getParameters() as $param) $paramClasses[] = $param->getClass() ? $param->getClass()->name : null; return function($args) use ($paramClasses, $rule, $subs) { $share = empty($rule->shareInstances) ? [] : array_map([$this, 'create'], $rule->shareInstances); if (!empty($share) || !empty($rule->constructParams)) $args = array_merge($args, $this->expand($rule->constructParams, $share), $share); foreach ($paramClasses as $class) { if (!empty($args)) for ($i = 0; $i < count($args); $i++) { if ($class && $args[$i] instanceof $class) { yield array_splice($args, $i, 1)[0]; continue 2; } } if (!empty($subs) && isset($subs[$class])) yield is_string($subs[$class]) ? $this->create($subs[$class]) : $this->expand($subs[$class]); else if (!empty($class)) yield $this->create($class, $share, !empty($rule->newInstances) && in_array(strtolower($class), array_map('strtolower', $rule->newInstances))); else if (!empty($args)) yield array_shift($args); } }; } } $dice1 = new Dice; $dice2 = new Dice2; $j1 = $dice1->create('J'); $j2 = $dice2->create('J'); $t1 = microtime(true); for ($i = 0; $i < 10000; $i++) { $j = $dice1->create('J'); } $t2 = microtime(true); echo 'dice1: ' . ($t2 - $t1) . "\n\n"; $t1 = microtime(true); for ($i = 0; $i < 10000; $i++) { $j = $dice2->create('J'); } $t2 = microtime(true); echo 'dice1: ' . ($t2 - $t1) . "\n\n";

preferences:
33.46 ms | 402 KiB | 5 Q