3v4l.org

run code in 150+ php & hhvm versions
Bugs & Features
<?php class Database { public function query($query) { return "Database#query($query) result."; } } class SimpleKeyValCache implements Cache { private $caches = array(); public function get($key) { if (!array_key_exists($key, $this->caches)) { throw new Exception("Key Does not exist"); } return $this->caches[$key]; } public function set($key, $value) { $this->caches[$key] = $value; } } class UselessKeyValCache implements Cache { public function get($key) { throw new Exception("Key Does not exist"); } public function set($key, $value) { // NOOP; } } interface Cache { public function get($key); public function set($key, $value); } interface CacheManager { public function get($key); } class SimpleCacheManager implements CacheManager { private $cache; private $db; public function __construct(Cache $cache, Database $db) { $this->cache = $cache; $this->db = $db; } public function get($key) { try { return $this->cache->get($key); } catch (Exception $e) { $value = $this->db->query("$key"); $this->cache->set($key, $value); return $value; } } } class Api { private $manager; public function __construct(CacheManager $manager) { $this->manager = $manager; } public function getResponse() { $result["A"] = $this->manager->get("One"); $result["B"] = $this->manager->get("Two"); $result["C"] = $this->manager->get("One"); return $result; } } class DoStuff { private $api; public function __construct(Api $api, array $scalarValue) { $this->api = $api; } public function doThatStuff() { var_dump($this->api->getResponse()); } } class DoSomeOtherStuff { private $api; public function __construct(Api $api) { $this->api = $api; } public function doThatOtherStuff() { var_dump($this->api->getResponse() + $this->api->getResponse()); } } class ConstructorInjectionContainer { private $types; private $config; public function __construct($config) { $this->config = $config; } /** * Construct a type using configuration information. * * The worst case time complexity of this algorithm is O(n^n) as we are making n recursive calls in n loops. Although in practice this _should_ never be a huge issue as n will never be larger than the sum of all dependency's constructor's arguments. * */ public function resolve($type, $overrides = null) { // TODO: error handling for invalid types. // Get the concrete type name. $concreteType = $this->config[$type]["concrete"]; // Reflect the concrete type so we can dynamically create arguments and subsequently instantiate it. $reflectedType = new ReflectionClass($concreteType); $constructor = $reflectedType->getConstructor(); $paramArray = array(); // If the class has a constructor implementation then generate and instantiate the dependencies dynamically. if ($constructor !== null) { $constructorParams = $constructor->getParameters(); foreach($constructorParams as $param) { $paramArray[$param->name] = $this->getParamValue($type, $param, $overrides); } } return $reflectedType->newInstanceArgs($paramArray); } private function getParamValue($type, $param, $overrides) { // Check overrides first, then check config. if ($overrides !== null && isset($overrides[$type]) && array_key_exists($param->name, $overrides[$type])) { return $overrides[$type][$param->name]; } elseif (array_key_exists($param->name, $this->config[$type]["constructor"])) { return $this->resolve($this->config[$type]["constructor"][$param->name], $overrides); } elseif (array_key_exists($this->config[$type]["concrete"], $this->config)) { return $this->resolve($this->config[$type]["concrete"], $overrides); } else { throw new Exception("Value for $type constructor parameter {$param->name} was not defined in overrides or configuration."); } } } $config = <<<'JSON' { "Cache": { "concrete": "SimpleKeyValCache", "constructor": { } }, "CacheManager": { "concrete": "SimpleCacheManager", "constructor": { "cache": "Cache" } }, "Api": { "concrete": "Api", "constructor": { "manager": "CacheManager" } }, "DoStuff": { "concrete": "DoStuff", "constructor": { "api": "Api" } }, "DoSomeOtherStuff": { "concrete": "DoSomeOtherStuff", "constructor": { "api": "CacheManager" } } } JSON; $di = new ConstructorInjectionContainer(json_decode($config, true)); $database = new Database(); $dostuff = $di->resolve("DoStuff", array( "CacheManager" => array( "db" => $database ), "DoStuff" => array( "scalarValue" => array() ) ) )->doThatStuff(); exit;
Output for 5.3.4 - 7.1.0
array(3) { ["A"]=> string(27) "Database#query(One) result." ["B"]=> string(27) "Database#query(Two) result." ["C"]=> string(27) "Database#query(One) result." }
Output for 5.3.0 - 5.3.3
Fatal error: Uncaught exception 'ReflectionException' with message 'Class SimpleKeyValCache does not have a constructor, so you cannot pass any constructor arguments' in /in/cWYtB:158 Stack trace: #0 /in/cWYtB(158): ReflectionClass->newInstanceArgs(Array) #1 /in/cWYtB(167): ConstructorInjectionContainer->resolve('Cache', Array) #2 /in/cWYtB(154): ConstructorInjectionContainer->getParamValue('CacheManager', Object(ReflectionParameter), Array) #3 /in/cWYtB(167): ConstructorInjectionContainer->resolve('CacheManager', Array) #4 /in/cWYtB(154): ConstructorInjectionContainer->getParamValue('Api', Object(ReflectionParameter), Array) #5 /in/cWYtB(167): ConstructorInjectionContainer->resolve('Api', Array) #6 /in/cWYtB(154): ConstructorInjectionContainer->getParamValue('DoStuff', Object(ReflectionParameter), Array) #7 /in/cWYtB(231): ConstructorInjectionContainer->resolve('DoStuff', Array) #8 {main} thrown in /in/cWYtB on line 158
Process exited with code 255.