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