1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20:
21: class NServiceLocator extends NObject implements IServiceLocator
22: {
23:
24: private $parent;
25:
26:
27: private $registry = array();
28:
29:
30: private $factories = array();
31:
32:
33:
34: 35: 36:
37: public function __construct(IServiceLocator $parent = NULL)
38: {
39: $this->parent = $parent;
40: }
41:
42:
43:
44: 45: 46: 47: 48: 49: 50: 51:
52: public function addService($name, $service, $singleton = TRUE, array $options = NULL)
53: {
54: if (!is_string($name) || $name === '') {
55: throw new InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
56: }
57:
58: $lower = strtolower($name);
59: if (isset($this->registry[$lower])) {
60: throw new NAmbiguousServiceException("Service named '$name' has been already registered.");
61: }
62:
63: if (is_object($service) && !($service instanceof Closure || $service instanceof NCallback)) {
64: if (!$singleton || $options) {
65: throw new InvalidArgumentException("Service named '$name' is an instantiated object and must therefore be singleton without options.");
66: }
67: $this->registry[$lower] = $service;
68:
69: } else {
70: if (!$service) {
71: throw new InvalidArgumentException("Service named '$name' is empty.");
72: }
73: $this->factories[$lower] = array($service, $singleton, $options);
74: }
75: }
76:
77:
78:
79: 80: 81: 82:
83: public function removeService($name)
84: {
85: if (!is_string($name) || $name === '') {
86: throw new InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
87: }
88:
89: $lower = strtolower($name);
90: unset($this->registry[$lower], $this->factories[$lower]);
91: }
92:
93:
94:
95: 96: 97: 98: 99: 100:
101: public function getService($name, array $options = NULL)
102: {
103: if (!is_string($name) || $name === '') {
104: throw new InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
105: }
106:
107: $lower = strtolower($name);
108:
109: if (isset($this->registry[$lower])) {
110: if ($options) {
111: throw new InvalidArgumentException("Service named '$name' is singleton and therefore can not have options.");
112: }
113: return $this->registry[$lower];
114:
115: } elseif (isset($this->factories[$lower])) {
116: list($factory, $singleton, $defOptions) = $this->factories[$lower];
117:
118: if ($singleton && $options) {
119: throw new InvalidArgumentException("Service named '$name' is singleton and therefore can not have options.");
120:
121: } elseif ($defOptions) {
122: $options = $options ? $options + $defOptions : $defOptions;
123: }
124:
125: if (is_string($factory) && strpos($factory, ':') === FALSE) {
126: if ($a = strrpos($factory, '\\')) $factory = substr($factory, $a + 1);
127: if (!class_exists($factory)) {
128: throw new NAmbiguousServiceException("Cannot instantiate service '$name', class '$factory' not found.");
129: }
130: $service = new $factory;
131: if ($options) {
132: if (method_exists($service, 'setOptions')) {
133: $service->setOptions($options);
134: } else {
135: throw new InvalidStateException("Unable to set options, method $factory::setOptions() is missing.");
136: }
137: }
138:
139: } else {
140: $factory = callback($factory);
141: if (!$factory->isCallable()) {
142: throw new InvalidStateException("Cannot instantiate service '$name', handler '$factory' is not callable.");
143: }
144: $service = $factory->invoke($options);
145: if (!is_object($service)) {
146: throw new NAmbiguousServiceException("Cannot instantiate service '$name', value returned by '$factory' is not object.");
147: }
148: }
149:
150: if ($singleton) {
151: $this->registry[$lower] = $service;
152: unset($this->factories[$lower]);
153: }
154: return $service;
155: }
156:
157: if ($this->parent !== NULL) {
158: return $this->parent->getService($name, $options);
159:
160: } else {
161: throw new InvalidStateException("Service '$name' not found.");
162: }
163: }
164:
165:
166:
167: 168: 169: 170: 171: 172:
173: public function hasService($name, $created = FALSE)
174: {
175: if (!is_string($name) || $name === '') {
176: throw new InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
177: }
178:
179: $lower = strtolower($name);
180: return isset($this->registry[$lower]) || (!$created && isset($this->factories[$lower])) || ($this->parent !== NULL && $this->parent->hasService($name, $created));
181: }
182:
183:
184:
185: 186: 187: 188:
189: public function getParent()
190: {
191: return $this->parent;
192: }
193:
194: }
195:
196:
197:
198: 199: 200: 201: 202: 203:
204: class NAmbiguousServiceException extends Exception
205: {
206: }
207: