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