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 TAG_TYPEHINT = 'typeHint';
26:
27:
28: public $params = array();
29:
30:
31: private $registry = array();
32:
33:
34: private $factories = array();
35:
36:
37: private $tags = array();
38:
39:
40: private $creating;
41:
42:
43:
44: 45: 46: 47: 48: 49: 50:
51: public function addService($name, $service, $tags = NULL)
52: {
53: $this->updating();
54: if (!is_string($name) || $name === '') {
55: throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
56: }
57:
58: if (isset($this->registry[$name]) || method_exists($this, "createService$name")) {
59: throw new Nette\InvalidStateException("Service '$name' has already been registered.");
60: }
61:
62: if (is_string($tags)) {
63: $tags = array(self::TAG_TYPEHINT => array($tags));
64: } elseif (is_array($tags)) {
65: foreach ($tags as $id => $attrs) {
66: if (is_int($id) && is_string($attrs)) {
67: $tags[$attrs] = array();
68: unset($tags[$id]);
69: } elseif (!is_array($attrs)) {
70: $tags[$id] = (array) $attrs;
71: }
72: }
73: }
74:
75: if (is_string($service) && strpos($service, ':') === FALSE) {
76: if (!isset($tags[self::TAG_TYPEHINT][0])) {
77: $tags[self::TAG_TYPEHINT][0] = $service;
78: }
79: $service = new ServiceBuilder($service);
80: }
81:
82: if ($service instanceof IServiceBuilder) {
83: $factory = array($service, 'createService');
84:
85: } elseif (is_object($service) && !$service instanceof \Closure && !$service instanceof Nette\Callback) {
86: $this->registry[$name] = $service;
87: $this->tags[$name] = $tags;
88: return $this;
89:
90: } else {
91: $factory = $service;
92: }
93:
94: $this->factories[$name] = array(callback($factory));
95: $this->tags[$name] = $tags;
96: $this->registry[$name] = & $this->factories[$name][1];
97: return $service;
98: }
99:
100:
101:
102: 103: 104: 105:
106: public function removeService($name)
107: {
108: $this->updating();
109: unset($this->registry[$name], $this->factories[$name]);
110: }
111:
112:
113:
114: 115: 116: 117: 118:
119: public function getService($name)
120: {
121: if (isset($this->registry[$name])) {
122: return $this->registry[$name];
123:
124: } elseif (isset($this->creating[$name])) {
125: throw new Nette\InvalidStateException("Circular reference detected for services: "
126: . implode(', ', array_keys($this->creating)) . ".");
127: }
128:
129: if (isset($this->factories[$name])) {
130: list($factory) = $this->factories[$name];
131: if (!$factory->isCallable()) {
132: throw new Nette\InvalidStateException("Unable to create service '$name', factory '$factory' is not callable.");
133: }
134:
135: $this->creating[$name] = TRUE;
136: try {
137: $service = $factory($this);
138: } catch (\Exception $e) {}
139:
140: } elseif (method_exists($this, $factory = 'createService' . ucfirst($name))) {
141: $this->creating[$name] = TRUE;
142: try {
143: $service = $this->$factory();
144: } catch (\Exception $e) {}
145:
146: } else {
147: throw new MissingServiceException("Service '$name' not found.");
148: }
149:
150: unset($this->creating[$name]);
151:
152: if (isset($e)) {
153: throw $e;
154:
155: } elseif (!is_object($service)) {
156: throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by factory '$factory' is not object.");
157:
158: } elseif (isset($this->tags[$name][self::TAG_TYPEHINT][0]) && !$service instanceof $this->tags[$name][self::TAG_TYPEHINT][0]) {
159: throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by factory '$factory' is not '{$this->tags[$name][self::TAG_TYPEHINT][0]}' type.");
160: }
161:
162: unset($this->factories[$name]);
163: return $this->registry[$name] = $service;
164: }
165:
166:
167:
168: 169: 170: 171: 172:
173: public function getServiceByType($type)
174: {
175: foreach ($this->registry as $name => $service) {
176: if (isset($this->tags[$name][self::TAG_TYPEHINT][0]) ? !strcasecmp($this->tags[$name][self::TAG_TYPEHINT][0], $type) : $service instanceof $type) {
177: $found[] = $name;
178: }
179: }
180: if (!isset($found)) {
181: throw new MissingServiceException("Service matching '$type' type not found.");
182:
183: } elseif (count($found) > 1) {
184: throw new AmbiguousServiceException("Found more than one service ('" . implode("', '", $found) . "') matching '$type' type.");
185: }
186: return $this->getService($found[0]);
187: }
188:
189:
190:
191: 192: 193: 194: 195:
196: public function getServiceNamesByTag($tag)
197: {
198: $found = array();
199: foreach ($this->registry as $name => $service) {
200: if (isset($this->tags[$name][$tag])) {
201: $found[$name] = $this->tags[$name][$tag];
202: }
203: }
204: return $found;
205: }
206:
207:
208:
209: 210: 211: 212: 213:
214: public function hasService($name)
215: {
216: return isset($this->registry[$name])
217: || isset($this->factories[$name])
218: || method_exists($this, "createService$name");
219: }
220:
221:
222:
223: 224: 225: 226: 227: 228:
229: public function checkServiceType($name, $type)
230: {
231: return isset($this->tags[$name][self::TAG_TYPEHINT][0])
232: ? !strcasecmp($this->tags[$name][self::TAG_TYPEHINT][0], $type)
233: : (isset($this->registry[$name]) && $this->registry[$name] instanceof $type);
234: }
235:
236:
237:
238:
239:
240:
241:
242: 243: 244: 245: 246:
247: public function expand($s)
248: {
249: return is_string($s) ? Nette\Utils\Strings::expand($s, $this->params) : $s;
250: }
251:
252:
253:
254: 255: 256: 257: 258:
259: public function &__get($name)
260: {
261: if (!isset($this->registry[$name])) {
262: $this->getService($name);
263: }
264: return $this->registry[$name];
265: }
266:
267:
268:
269: 270: 271: 272: 273: 274:
275: public function __set($name, $service)
276: {
277: $this->updating();
278: if (!is_string($name) || $name === '') {
279: throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
280:
281: } elseif (isset($this->registry[$name]) || method_exists($this, "createService$name")) {
282: throw new Nette\InvalidStateException("Service '$name' has already been registered.");
283:
284: } elseif (!is_object($service)) {
285: throw new Nette\InvalidArgumentException("Service must be a object, " . gettype($service) . " given.");
286: }
287: $this->registry[$name] = $service;
288: }
289:
290:
291:
292: 293: 294: 295: 296:
297: public function __isset($name)
298: {
299: return $this->hasService($name);
300: }
301:
302:
303:
304: 305: 306: 307:
308: public function __unset($name)
309: {
310: $this->removeService($name);
311: }
312:
313: }
314: