<?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->invokeArgs($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);
$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;
};
}
}
$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:
31.67 ms | 402 KiB | 5 Q