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: