1<?php 2 3namespace App\Container; 4 5use App\Container\Exception\EntryNotFoundException; 6use App\Container\Exception\ContainerException; 7 8/** 9 * PSR-11 compatible dependency injection container. 10 */ 11class Container implements ContainerInterface 12{ 13 /** 14 * All defined services and parameters. 15 * 16 * @var array 17 */ 18 public $entries = []; 19 20 /** 21 * Already retrieved items are stored for faster retrievals in the same run. 22 * 23 * @var array 24 */ 25 private $store = []; 26 27 /** 28 * Services already created to prevent circular references. 29 * 30 * @var array 31 */ 32 private $locks = []; 33 34 /** 35 * Class constructor. 36 */ 37 public function __construct(array $configurations = []) 38 { 39 $this->entries = $configurations; 40 } 41 42 /** 43 * Set service. 44 */ 45 public function set(string $key, $entry): void 46 { 47 $this->entries[$key] = $entry; 48 } 49 50 /** 51 * Get entry. 52 * 53 * @return mixed 54 */ 55 public function get(string $id) 56 { 57 if (!$this->has($id)) { 58 throw new EntryNotFoundException($id.' entry not found.'); 59 } 60 61 if (!isset($this->store[$id])) { 62 $this->store[$id] = $this->createEntry($id); 63 } 64 65 return $this->store[$id]; 66 } 67 68 /** 69 * Check if entry is available in the container. 70 */ 71 public function has(string $id): bool 72 { 73 return isset($this->entries[$id]); 74 } 75 76 /** 77 * Create new entry - service or configuration parameter. 78 * 79 * @return mixed 80 */ 81 private function createEntry(string $id) 82 { 83 $entry = &$this->entries[$id]; 84 85 // Entry is a configuration parameter. 86 if (!class_exists($id) && !is_callable($entry)) { 87 return $entry; 88 } 89 90 // Entry is a service. 91 if (class_exists($id) && !is_callable($entry)) { 92 throw new ContainerException($id.' entry must be callable.'); 93 } elseif (class_exists($id) && isset($this->locks[$id])) { 94 throw new ContainerException($id.' entry contains a circular reference.'); 95 } 96 97 $this->locks[$id] = true; 98 99 return $entry($this); 100 } 101} 102