1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\DI;
9:
10: use Nette;
11:
12:
13: 14: 15:
16: class Container
17: {
18: use Nette\SmartObject;
19:
20: const TAGS = 'tags';
21: const TYPES = 'types';
22: const SERVICES = 'services';
23: const ALIASES = 'aliases';
24:
25:
26: public $parameters = [];
27:
28:
29: protected $meta = [];
30:
31:
32: private $registry = [];
33:
34:
35: private $creating;
36:
37:
38: public function __construct(array $params = [])
39: {
40: $this->parameters = $params + $this->parameters;
41: }
42:
43:
44: 45: 46:
47: public function getParameters()
48: {
49: return $this->parameters;
50: }
51:
52:
53: 54: 55: 56: 57: 58:
59: public function addService($name, $service)
60: {
61: if (!is_string($name) || !$name) {
62: throw new Nette\InvalidArgumentException(sprintf('Service name must be a non-empty string, %s given.', gettype($name)));
63: }
64: $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name;
65: if (isset($this->registry[$name])) {
66: throw new Nette\InvalidStateException("Service '$name' already exists.");
67:
68: } elseif (!is_object($service)) {
69: throw new Nette\InvalidArgumentException(sprintf("Service '%s' must be a object, %s given.", $name, gettype($service)));
70:
71: } elseif (isset($this->meta[self::SERVICES][$name]) && !$service instanceof $this->meta[self::SERVICES][$name]) {
72: throw new Nette\InvalidArgumentException(sprintf("Service '%s' must be instance of %s, %s given.", $name, $this->meta[self::SERVICES][$name], get_class($service)));
73: }
74:
75: $this->registry[$name] = $service;
76: return $this;
77: }
78:
79:
80: 81: 82: 83: 84:
85: public function removeService($name)
86: {
87: $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name;
88: unset($this->registry[$name]);
89: }
90:
91:
92: 93: 94: 95: 96: 97:
98: public function getService($name)
99: {
100: if (!isset($this->registry[$name])) {
101: if (isset($this->meta[self::ALIASES][$name])) {
102: return $this->getService($this->meta[self::ALIASES][$name]);
103: }
104: $this->registry[$name] = $this->createService($name);
105: }
106: return $this->registry[$name];
107: }
108:
109:
110: 111: 112: 113: 114: 115:
116: public function getServiceType($name)
117: {
118: if (isset($this->meta[self::ALIASES][$name])) {
119: return $this->getServiceType($this->meta[self::ALIASES][$name]);
120:
121: } elseif (isset($this->meta[self::SERVICES][$name])) {
122: return $this->meta[self::SERVICES][$name];
123:
124: } else {
125: throw new MissingServiceException("Service '$name' not found.");
126: }
127: }
128:
129:
130: 131: 132: 133: 134:
135: public function hasService($name)
136: {
137: $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name;
138: return isset($this->registry[$name])
139: || (method_exists($this, $method = self::getMethodName($name))
140: && (new \ReflectionMethod($this, $method))->getName() === $method);
141: }
142:
143:
144: 145: 146: 147: 148:
149: public function isCreated($name)
150: {
151: if (!$this->hasService($name)) {
152: throw new MissingServiceException("Service '$name' not found.");
153: }
154: $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name;
155: return isset($this->registry[$name]);
156: }
157:
158:
159: 160: 161: 162: 163: 164:
165: public function createService($name, array $args = [])
166: {
167: $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name;
168: $method = self::getMethodName($name);
169: if (isset($this->creating[$name])) {
170: throw new Nette\InvalidStateException(sprintf('Circular reference detected for services: %s.', implode(', ', array_keys($this->creating))));
171:
172: } elseif (!method_exists($this, $method) || (new \ReflectionMethod($this, $method))->getName() !== $method) {
173: throw new MissingServiceException("Service '$name' not found.");
174: }
175:
176: try {
177: $this->creating[$name] = true;
178: $service = $this->$method(...$args);
179:
180: } finally {
181: unset($this->creating[$name]);
182: }
183:
184: if (!is_object($service)) {
185: throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by method $method() is not object.");
186: }
187:
188: return $service;
189: }
190:
191:
192: 193: 194: 195: 196: 197: 198:
199: public function getByType($type, $throw = true)
200: {
201: $type = Helpers::normalizeClass($type);
202: if (!empty($this->meta[self::TYPES][$type][true])) {
203: if (count($names = $this->meta[self::TYPES][$type][true]) === 1) {
204: return $this->getService($names[0]);
205: }
206: throw new MissingServiceException("Multiple services of type $type found: " . implode(', ', $names) . '.');
207:
208: } elseif ($throw) {
209: throw new MissingServiceException("Service of type $type not found.");
210: }
211: }
212:
213:
214: 215: 216: 217: 218:
219: public function findByType($type)
220: {
221: $type = Helpers::normalizeClass($type);
222: return empty($this->meta[self::TYPES][$type])
223: ? []
224: : array_merge(...array_values($this->meta[self::TYPES][$type]));
225: }
226:
227:
228: 229: 230: 231: 232:
233: public function findByTag($tag)
234: {
235: return isset($this->meta[self::TAGS][$tag]) ? $this->meta[self::TAGS][$tag] : [];
236: }
237:
238:
239:
240:
241:
242: 243: 244: 245: 246: 247: 248:
249: public function createInstance($class, array $args = [])
250: {
251: $rc = new \ReflectionClass($class);
252: if (!$rc->isInstantiable()) {
253: throw new ServiceCreationException("Class $class is not instantiable.");
254:
255: } elseif ($constructor = $rc->getConstructor()) {
256: return $rc->newInstanceArgs(Helpers::autowireArguments($constructor, $args, $this));
257:
258: } elseif ($args) {
259: throw new ServiceCreationException("Unable to pass arguments, class $class has no constructor.");
260: }
261: return new $class;
262: }
263:
264:
265: 266: 267: 268: 269:
270: public function callInjects($service)
271: {
272: Extensions\InjectExtension::callInjects($this, $service);
273: }
274:
275:
276: 277: 278: 279:
280: public function callMethod(callable $function, array $args = [])
281: {
282: return $function(...Helpers::autowireArguments(Nette\Utils\Callback::toReflection($function), $args, $this));
283: }
284:
285:
286:
287:
288:
289: 290: 291: 292: 293: 294:
295: public function expand($s)
296: {
297: return Helpers::expand($s, $this->parameters);
298: }
299:
300:
301:
302: public function &__get($name)
303: {
304: trigger_error(__METHOD__ . ' is deprecated; use getService().', E_USER_DEPRECATED);
305: $tmp = $this->getService($name);
306: return $tmp;
307: }
308:
309:
310:
311: public function __set($name, $service)
312: {
313: trigger_error(__METHOD__ . ' is deprecated; use addService().', E_USER_DEPRECATED);
314: $this->addService($name, $service);
315: }
316:
317:
318:
319: public function __isset($name)
320: {
321: trigger_error(__METHOD__ . ' is deprecated; use hasService().', E_USER_DEPRECATED);
322: return $this->hasService($name);
323: }
324:
325:
326:
327: public function __unset($name)
328: {
329: trigger_error(__METHOD__ . ' is deprecated; use removeService().', E_USER_DEPRECATED);
330: $this->removeService($name);
331: }
332:
333:
334: public static function getMethodName($name)
335: {
336: $uname = ucfirst($name);
337: return 'createService' . ((string) $name === $uname ? '__' : '') . str_replace('.', '__', $uname);
338: }
339: }
340: