Source for file ServiceLocator.php

Documentation is available at ServiceLocator.php

  1. 1: <?php
  2. 2:  
  3. 3: /**
  4. 4:  * Nette Framework
  5. 5:  *
  6. 6:  * Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
  7. 7:  *
  8. 8:  * This source file is subject to the "Nette license" that is bundled
  9. 9:  * with this package in the file license.txt.
  10. 10:  *
  11. 11:  * For more information please see http://nettephp.com
  12. 12:  *
  13. 13:  * @copyright  Copyright (c) 2004, 2009 David Grudl
  14. 14:  * @license    http://nettephp.com/license  Nette license
  15. 15:  * @link       http://nettephp.com
  16. 16:  * @category   Nette
  17. 17:  * @package    Nette
  18. 18:  */
  19. 19:  
  20. 20:  
  21. 21:  
  22. 22: require_once dirname(__FILE__'/IServiceLocator.php';
  23. 23:  
  24. 24: require_once dirname(__FILE__'/Object.php';
  25. 25:  
  26. 26:  
  27. 27:  
  28. 28: /**
  29. 29:  * Service locator pattern implementation.
  30. 30:  *
  31. 31:  * @author     David Grudl
  32. 32:  * @copyright  Copyright (c) 2004, 2009 David Grudl
  33. 33:  * @package    Nette
  34. 34:  */
  35. 35: class ServiceLocator extends Object implements IServiceLocator
  36. 36: {
  37. 37:     /** @var IServiceLocator */
  38. 38:     private $parent;
  39. 39:  
  40. 40:     /** @var array  storage for shared objects */
  41. 41:     private $registry array();
  42. 42:  
  43. 43:     /** @var array  storage for service factories */
  44. 44:     private $factories array();
  45. 45:  
  46. 46:  
  47. 47:  
  48. 48:     /**
  49. 49:      * @param  IServiceLocator 
  50. 50:      */
  51. 51:     public function __construct(IServiceLocator $parent NULL)
  52. 52:     {
  53. 53:         $this->parent $parent;
  54. 54:     }
  55. 55:  
  56. 56:  
  57. 57:  
  58. 58:     /**
  59. 59:      * Adds the specified service to the service container.
  60. 60:      * @param  string service name
  61. 61:      * @param  mixed  object, class name or factory callback
  62. 62:      * @param  bool   is singleton?
  63. 63:      * @param  array  factory options
  64. 64:      * @return void 
  65. 65:      */
  66. 66:     public function addService($name$service$singleton TRUEarray $options NULL)
  67. 67:     {
  68. 68:         if (!is_string($name|| $name === ''{
  69. 69:             throw new InvalidArgumentException("Service name must be a non-empty string, " gettype($name" given.");
  70. 70:         }
  71. 71:  
  72. 72:         $lower strtolower($name);
  73. 73:         if (isset($this->registry[$lower])) // only for instantiated services?
  74. 74:             throw new AmbiguousServiceException("Service named '$name' has been already registered.");
  75. 75:         }
  76. 76:  
  77. 77:         if (is_object($service)) {
  78. 78:             if (!$singleton || $options{
  79. 79:                 throw new InvalidArgumentException("Service named '$name' is an instantiated object and must therefore be singleton without options.");
  80. 80:             }
  81. 81:             $this->registry[$lower$service;
  82. 82:  
  83. 83:         else {
  84. 84:             if (!$service{
  85. 85:                 throw new InvalidArgumentException("Service named '$name' is empty.");
  86. 86:             }
  87. 87:             $this->factories[$lowerarray($service$singleton$options);
  88. 88:         }
  89. 89:     }
  90. 90:  
  91. 91:  
  92. 92:  
  93. 93:     /**
  94. 94:      * Removes the specified service type from the service container.
  95. 95:      * @return void 
  96. 96:      */
  97. 97:     public function removeService($name)
  98. 98:     {
  99. 99:         if (!is_string($name|| $name === ''{
  100. 100:             throw new InvalidArgumentException("Service name must be a non-empty string, " gettype($name" given.");
  101. 101:         }
  102. 102:  
  103. 103:         $lower strtolower($name);
  104. 104:         unset($this->registry[$lower]$this->factories[$lower]);
  105. 105:     }
  106. 106:  
  107. 107:  
  108. 108:  
  109. 109:     /**
  110. 110:      * Gets the service object of the specified type.
  111. 111:      * @param  string service name
  112. 112:      * @param  array  options in case service is not singleton
  113. 113:      * @return mixed 
  114. 114:      */
  115. 115:     public function getService($namearray $options NULL)
  116. 116:     {
  117. 117:         if (!is_string($name|| $name === ''{
  118. 118:             throw new InvalidArgumentException("Service name must be a non-empty string, " gettype($name" given.");
  119. 119:         }
  120. 120:  
  121. 121:         $lower strtolower($name);
  122. 122:  
  123. 123:         if (isset($this->registry[$lower])) // instantiated singleton
  124. 124:             if ($options{
  125. 125:                 throw new InvalidArgumentException("Service named '$name' is singleton and therefore can not have options.");
  126. 126:             }
  127. 127:             return $this->registry[$lower];
  128. 128:  
  129. 129:         elseif (isset($this->factories[$lower])) {
  130. 130:             list($factory$singleton$defOptions$this->factories[$lower];
  131. 131:  
  132. 132:             if ($singleton && $options{
  133. 133:                 throw new InvalidArgumentException("Service named '$name' is singleton and therefore can not have options.");
  134. 134:  
  135. 135:             elseif ($defOptions{
  136. 136:                 $options $options $options $defOptions $defOptions;
  137. 137:             }
  138. 138:  
  139. 139:             if (is_string($factory&& strpos($factory':'=== FALSE// class name
  140. 140:                 fixNamespace($factory);
  141. 141:                 if (!class_exists($factory)) {
  142. 142:                     throw new AmbiguousServiceException("Cannot instantiate service '$name', class '$factory' not found.");
  143. 143:                 }
  144. 144:                 $service new $factory;
  145. 145:                 if ($options && method_exists($service'setOptions')) {
  146. 146:                     $service->setOptions($options)// TODO: better!
  147. 147:                 }
  148. 148:  
  149. 149:             else // factory callback
  150. 150:                 fixCallback($factory);
  151. 151:                 if (!is_callable($factory)) {
  152. 152:                     $able is_callable($factoryTRUE$textual);
  153. 153:                     throw new AmbiguousServiceException("Cannot instantiate service '$name', handler '$textual' is not ($able 'callable.' 'valid PHP callback.'));
  154. 154:                 }
  155. 155:                 $service call_user_func($factory$options);
  156. 156:                 if (!is_object($service)) {
  157. 157:                     $able is_callable($factoryTRUE$textual);
  158. 158:                     throw new AmbiguousServiceException("Cannot instantiate service '$name', value returned by '$textual' is not object.");
  159. 159:                 }
  160. 160:             }
  161. 161:  
  162. 162:             if ($singleton{
  163. 163:                 $this->registry[$lower$service;
  164. 164:                 unset($this->factories[$lower]);
  165. 165:             }
  166. 166:             return $service;
  167. 167:         }
  168. 168:  
  169. 169:         if ($this->parent !== NULL{
  170. 170:             return $this->parent->getService($name$options);
  171. 171:  
  172. 172:         else {
  173. 173:             throw new InvalidStateException("Service '$name' not found.");
  174. 174:         }
  175. 175:     }
  176. 176:  
  177. 177:  
  178. 178:  
  179. 179:     /**
  180. 180:      * Exists the service?
  181. 181:      * @param  string service name
  182. 182:      * @param  bool   must be created yet?
  183. 183:      * @return bool 
  184. 184:      */
  185. 185:     public function hasService($name$created FALSE)
  186. 186:     {
  187. 187:         if (!is_string($name|| $name === ''{
  188. 188:             throw new InvalidArgumentException("Service name must be a non-empty string, " gettype($name" given.");
  189. 189:         }
  190. 190:  
  191. 191:         $lower strtolower($name);
  192. 192:         return isset($this->registry[$lower]|| (!$created && isset($this->factories[$lower])) || ($this->parent !== NULL && $this->parent->hasService($name$created));
  193. 193:     }
  194. 194:  
  195. 195:  
  196. 196:  
  197. 197:     /**
  198. 198:      * Returns the parent container if any.
  199. 199:      * @return IServiceLocator|NULL
  200. 200:      */
  201. 201:     public function getParent()
  202. 202:     {
  203. 203:         return $this->parent;
  204. 204:     }
  205. 205:  
  206. 207:  
  207. 208:  
  208. 209:  
  209. 210: /**
  210. 211:  * Ambiguous service resolution exception.
  211. 212:  *
  212. 213:  * @author     David Grudl
  213. 214:  * @copyright  Copyright (c) 2004, 2009 David Grudl
  214. 215:  * @package    Nette
  215. 216:  */
  216. 217: class AmbiguousServiceException extends Exception