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 = 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: public function createInstance($class, array $args = array())
254: {
255: $rc = Nette\Reflection\ClassType::from($class);
256: if (!$rc->isInstantiable()) {
257: throw new Nette\InvalidArgumentException("Class $class is not instantiable.");
258:
259: } elseif ($constructor = $rc->getConstructor()) {
260: return $rc->newInstanceArgs(Helpers::autowireArguments($constructor, $args, $this));
261:
262: } elseif ($args) {
263: throw new Nette\InvalidArgumentException("Unable to pass arguments, class $class has no constructor.");
264: }
265: return new $class;
266: }
267:
268:
269:
270: 271: 272: 273: 274: 275:
276: public function callMethod($function, array $args = array())
277: {
278: $callback = callback($function);
279: return $callback->invokeArgs(Helpers::autowireArguments($callback->toReflection(), $args, $this));
280: }
281:
282:
283:
284:
285:
286:
287:
288: 289: 290: 291: 292:
293: public function expand($s)
294: {
295: return Helpers::expand($s, $this->parameters);
296: }
297:
298:
299:
300: 301: 302: 303: 304:
305: public function &__get($name)
306: {
307: if (!isset($this->registry[$name])) {
308: $this->getService($name);
309: }
310: return $this->registry[$name];
311: }
312:
313:
314:
315: 316: 317: 318: 319: 320:
321: public function __set($name, $service)
322: {
323: $this->updating();
324: if (!is_string($name) || $name === '') {
325: throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
326:
327: } elseif (isset($this->registry[$name])) {
328: throw new Nette\InvalidStateException("Service '$name' has already been registered.");
329:
330: } elseif (!is_object($service)) {
331: throw new Nette\InvalidArgumentException("Service must be a object, " . gettype($service) . " given.");
332: }
333: $this->registry[$name] = $service;
334: }
335:
336:
337:
338: 339: 340: 341: 342:
343: public function __isset($name)
344: {
345: return $this->hasService($name);
346: }
347:
348:
349:
350: 351: 352: 353:
354: public function __unset($name)
355: {
356: $this->removeService($name);
357: }
358:
359:
360:
361: public static function getMethodName($name, $isService = TRUE)
362: {
363: $uname = ucfirst($name);
364: return ($isService ? 'createService' : 'create') . ($name === $uname ? '_' : '') . strtr($uname, '.', '_');
365: }
366:
367: }
368: