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