Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • None
  • PHP

Classes

  • Container
  • ContainerBuilder
  • ServiceBuilder

Interfaces

  • IContainer
  • IServiceBuilder

Exceptions

  • AmbiguousServiceException
  • MissingServiceException
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  *
  6:  * Copyright (c) 2004, 2011 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  */
 11: 
 12: namespace Nette\DI;
 13: 
 14: use Nette;
 15: 
 16: 
 17: 
 18: /**
 19:  * The dependency injection container default implementation.
 20:  *
 21:  * @author     David Grudl
 22:  */
 23: class Container extends Nette\FreezableObject implements IContainer
 24: {
 25:     const TAG_TYPEHINT = 'typeHint';
 26: 
 27:     /** @var array  user parameters */
 28:     public $params = array();
 29: 
 30:     /** @var array  storage for shared objects */
 31:     private $registry = array();
 32: 
 33:     /** @var array  storage for service factories */
 34:     private $factories = array();
 35: 
 36:     /** @var array  */
 37:     private $tags = array();
 38: 
 39:     /** @var array circular reference detector */
 40:     private $creating;
 41: 
 42: 
 43: 
 44:     /**
 45:      * Adds the specified service or service factory to the container.
 46:      * @param  string
 47:      * @param  mixed   object, class name or callback
 48:      * @param  mixed   array of tags or string typeHint
 49:      * @return Container|ServiceBuilder  provides a fluent interface
 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) { // class name
 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]; // forces cloning using reference
 97:         return $service;
 98:     }
 99: 
100: 
101: 
102:     /**
103:      * Removes the specified service type from the container.
104:      * @return void
105:      */
106:     public function removeService($name)
107:     {
108:         $this->updating();
109:         unset($this->registry[$name], $this->factories[$name]);
110:     }
111: 
112: 
113: 
114:     /**
115:      * Gets the service object by name.
116:      * @param  string
117:      * @return object
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))) { // static method
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:      * Gets the service object of the specified type.
170:      * @param  string
171:      * @return object
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:      * Gets the service objects of the specified tag.
193:      * @param  string
194:      * @return array of [service name => tag attributes]
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:      * Does the service exist?
211:      * @param  string service name
212:      * @return bool
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:      * Checks the service type.
225:      * @param  string
226:      * @param  string
227:      * @return bool
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:     /********************* shortcuts ****************d*g**/
239: 
240: 
241: 
242:     /**
243:      * Expands %placeholders% in string.
244:      * @param  mixed
245:      * @return mixed
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:      * Gets the service object, shortcut for getService().
256:      * @param  string
257:      * @return object
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:      * Adds the service object.
271:      * @param  string
272:      * @param  object
273:      * @return void
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:      * Does the service exist?
294:      * @param  string
295:      * @return bool
296:      */
297:     public function __isset($name)
298:     {
299:         return $this->hasService($name);
300:     }
301: 
302: 
303: 
304:     /**
305:      * Removes the service, shortcut for removeService().
306:      * @return void
307:      */
308:     public function __unset($name)
309:     {
310:         $this->removeService($name);
311:     }
312: 
313: }
314: 
Nette Framework 2.0beta1 API API documentation generated by ApiGen 2.3.0