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