PageRenderTime 38ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/protected/extensions/doctrine/vendors/Symfony/Component/DependencyInjection/Container.php

https://bitbucket.org/NordLabs/yiidoctrine
PHP | 459 lines | 188 code | 51 blank | 220 comment | 24 complexity | 22e0c18c39d25a084cefd5fc48c378c4 MD5 | raw file
Possible License(s): BSD-2-Clause, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\DependencyInjection;
  11. use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  12. use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
  13. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  14. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
  15. use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
  16. /**
  17. * Container is a dependency injection container.
  18. *
  19. * It gives access to object instances (services).
  20. *
  21. * Services and parameters are simple key/pair stores.
  22. *
  23. * Parameter and service keys are case insensitive.
  24. *
  25. * A service id can contain lowercased letters, digits, underscores, and dots.
  26. * Underscores are used to separate words, and dots to group services
  27. * under namespaces:
  28. *
  29. * <ul>
  30. * <li>request</li>
  31. * <li>mysql_session_storage</li>
  32. * <li>symfony.mysql_session_storage</li>
  33. * </ul>
  34. *
  35. * A service can also be defined by creating a method named
  36. * getXXXService(), where XXX is the camelized version of the id:
  37. *
  38. * <ul>
  39. * <li>request -> getRequestService()</li>
  40. * <li>mysql_session_storage -> getMysqlSessionStorageService()</li>
  41. * <li>symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()</li>
  42. * </ul>
  43. *
  44. * The container can have three possible behaviors when a service does not exist:
  45. *
  46. * * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default)
  47. * * NULL_ON_INVALID_REFERENCE: Returns null
  48. * * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference
  49. * (for instance, ignore a setter if the service does not exist)
  50. *
  51. * @author Fabien Potencier <fabien@symfony.com>
  52. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  53. *
  54. * @api
  55. */
  56. class Container implements ContainerInterface
  57. {
  58. protected $parameterBag;
  59. protected $services;
  60. protected $scopes;
  61. protected $scopeChildren;
  62. protected $scopedServices;
  63. protected $scopeStacks;
  64. protected $loading = array();
  65. /**
  66. * Constructor.
  67. *
  68. * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance
  69. *
  70. * @api
  71. */
  72. public function __construct(ParameterBagInterface $parameterBag = null)
  73. {
  74. $this->parameterBag = null === $parameterBag ? new ParameterBag() : $parameterBag;
  75. $this->services = array();
  76. $this->scopes = array();
  77. $this->scopeChildren = array();
  78. $this->scopedServices = array();
  79. $this->scopeStacks = array();
  80. $this->set('service_container', $this);
  81. }
  82. /**
  83. * Compiles the container.
  84. *
  85. * This method does two things:
  86. *
  87. * * Parameter values are resolved;
  88. * * The parameter bag is frozen.
  89. *
  90. * @api
  91. */
  92. public function compile()
  93. {
  94. $this->parameterBag->resolve();
  95. $this->parameterBag = new FrozenParameterBag($this->parameterBag->all());
  96. }
  97. /**
  98. * Returns true if the container parameter bag are frozen.
  99. *
  100. * @return Boolean true if the container parameter bag are frozen, false otherwise
  101. *
  102. * @api
  103. */
  104. public function isFrozen()
  105. {
  106. return $this->parameterBag instanceof FrozenParameterBag;
  107. }
  108. /**
  109. * Gets the service container parameter bag.
  110. *
  111. * @return ParameterBagInterface A ParameterBagInterface instance
  112. *
  113. * @api
  114. */
  115. public function getParameterBag()
  116. {
  117. return $this->parameterBag;
  118. }
  119. /**
  120. * Gets a parameter.
  121. *
  122. * @param string $name The parameter name
  123. *
  124. * @return mixed The parameter value
  125. *
  126. * @throws \InvalidArgumentException if the parameter is not defined
  127. *
  128. * @api
  129. */
  130. public function getParameter($name)
  131. {
  132. return $this->parameterBag->get($name);
  133. }
  134. /**
  135. * Checks if a parameter exists.
  136. *
  137. * @param string $name The parameter name
  138. *
  139. * @return Boolean The presence of parameter in container
  140. *
  141. * @api
  142. */
  143. public function hasParameter($name)
  144. {
  145. return $this->parameterBag->has($name);
  146. }
  147. /**
  148. * Sets a parameter.
  149. *
  150. * @param string $name The parameter name
  151. * @param mixed $value The parameter value
  152. *
  153. * @api
  154. */
  155. public function setParameter($name, $value)
  156. {
  157. $this->parameterBag->set($name, $value);
  158. }
  159. /**
  160. * Sets a service.
  161. *
  162. * @param string $id The service identifier
  163. * @param object $service The service instance
  164. * @param string $scope The scope of the service
  165. *
  166. * @api
  167. */
  168. public function set($id, $service, $scope = self::SCOPE_CONTAINER)
  169. {
  170. if (self::SCOPE_PROTOTYPE === $scope) {
  171. throw new \InvalidArgumentException('You cannot set services of scope "prototype".');
  172. }
  173. $id = strtolower($id);
  174. if (self::SCOPE_CONTAINER !== $scope) {
  175. if (!isset($this->scopedServices[$scope])) {
  176. throw new \RuntimeException('You cannot set services of inactive scopes.');
  177. }
  178. $this->scopedServices[$scope][$id] = $service;
  179. }
  180. $this->services[$id] = $service;
  181. }
  182. /**
  183. * Returns true if the given service is defined.
  184. *
  185. * @param string $id The service identifier
  186. *
  187. * @return Boolean true if the service is defined, false otherwise
  188. *
  189. * @api
  190. */
  191. public function has($id)
  192. {
  193. $id = strtolower($id);
  194. return isset($this->services[$id]) || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_')).'Service');
  195. }
  196. /**
  197. * Gets a service.
  198. *
  199. * If a service is both defined through a set() method and
  200. * with a set*Service() method, the former has always precedence.
  201. *
  202. * @param string $id The service identifier
  203. * @param integer $invalidBehavior The behavior when the service does not exist
  204. *
  205. * @return object The associated service
  206. *
  207. * @throws \InvalidArgumentException if the service is not defined
  208. *
  209. * @see Reference
  210. *
  211. * @api
  212. */
  213. public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE)
  214. {
  215. $id = strtolower($id);
  216. if (isset($this->services[$id])) {
  217. return $this->services[$id];
  218. }
  219. if (isset($this->loading[$id])) {
  220. throw new ServiceCircularReferenceException($id, array_keys($this->loading));
  221. }
  222. if (method_exists($this, $method = 'get'.strtr($id, array('_' => '', '.' => '_')).'Service')) {
  223. $this->loading[$id] = true;
  224. try {
  225. $service = $this->$method();
  226. } catch (\Exception $e) {
  227. unset($this->loading[$id]);
  228. throw $e;
  229. }
  230. unset($this->loading[$id]);
  231. return $service;
  232. }
  233. if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
  234. throw new ServiceNotFoundException($id);
  235. }
  236. }
  237. /**
  238. * Gets all service ids.
  239. *
  240. * @return array An array of all defined service ids
  241. */
  242. public function getServiceIds()
  243. {
  244. $ids = array();
  245. $r = new \ReflectionClass($this);
  246. foreach ($r->getMethods() as $method) {
  247. if (preg_match('/^get(.+)Service$/', $method->getName(), $match)) {
  248. $ids[] = self::underscore($match[1]);
  249. }
  250. }
  251. return array_unique(array_merge($ids, array_keys($this->services)));
  252. }
  253. /**
  254. * This is called when you enter a scope
  255. *
  256. * @param string $name
  257. *
  258. * @return void
  259. *
  260. * @api
  261. */
  262. public function enterScope($name)
  263. {
  264. if (!isset($this->scopes[$name])) {
  265. throw new \InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name));
  266. }
  267. if (self::SCOPE_CONTAINER !== $this->scopes[$name] && !isset($this->scopedServices[$this->scopes[$name]])) {
  268. throw new \RuntimeException(sprintf('The parent scope "%s" must be active when entering this scope.', $this->scopes[$name]));
  269. }
  270. // check if a scope of this name is already active, if so we need to
  271. // remove all services of this scope, and those of any of its child
  272. // scopes from the global services map
  273. if (isset($this->scopedServices[$name])) {
  274. $services = array($this->services, $name => $this->scopedServices[$name]);
  275. unset($this->scopedServices[$name]);
  276. foreach ($this->scopeChildren[$name] as $child) {
  277. $services[$child] = $this->scopedServices[$child];
  278. unset($this->scopedServices[$child]);
  279. }
  280. // update global map
  281. $this->services = call_user_func_array('array_diff_key', $services);
  282. array_shift($services);
  283. // add stack entry for this scope so we can restore the removed services later
  284. if (!isset($this->scopeStacks[$name])) {
  285. $this->scopeStacks[$name] = new \SplStack();
  286. }
  287. $this->scopeStacks[$name]->push($services);
  288. }
  289. $this->scopedServices[$name] = array();
  290. }
  291. /**
  292. * This is called to leave the current scope, and move back to the parent
  293. * scope.
  294. *
  295. * @param string $name The name of the scope to leave
  296. *
  297. * @return void
  298. *
  299. * @throws \InvalidArgumentException if the scope is not active
  300. *
  301. * @api
  302. */
  303. public function leaveScope($name)
  304. {
  305. if (!isset($this->scopedServices[$name])) {
  306. throw new \InvalidArgumentException(sprintf('The scope "%s" is not active.', $name));
  307. }
  308. // remove all services of this scope, or any of its child scopes from
  309. // the global service map
  310. $services = array($this->services, $this->scopedServices[$name]);
  311. unset($this->scopedServices[$name]);
  312. foreach ($this->scopeChildren[$name] as $child) {
  313. if (!isset($this->scopedServices[$child])) {
  314. continue;
  315. }
  316. $services[] = $this->scopedServices[$child];
  317. unset($this->scopedServices[$child]);
  318. }
  319. $this->services = call_user_func_array('array_diff_key', $services);
  320. // check if we need to restore services of a previous scope of this type
  321. if (isset($this->scopeStacks[$name]) && count($this->scopeStacks[$name]) > 0) {
  322. $services = $this->scopeStacks[$name]->pop();
  323. $this->scopedServices += $services;
  324. array_unshift($services, $this->services);
  325. $this->services = call_user_func_array('array_merge', $services);
  326. }
  327. }
  328. /**
  329. * Adds a scope to the container.
  330. *
  331. * @param ScopeInterface $scope
  332. *
  333. * @return void
  334. *
  335. * @api
  336. */
  337. public function addScope(ScopeInterface $scope)
  338. {
  339. $name = $scope->getName();
  340. $parentScope = $scope->getParentName();
  341. if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) {
  342. throw new \InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name));
  343. }
  344. if (isset($this->scopes[$name])) {
  345. throw new \InvalidArgumentException(sprintf('A scope with name "%s" already exists.', $name));
  346. }
  347. if (self::SCOPE_CONTAINER !== $parentScope && !isset($this->scopes[$parentScope])) {
  348. throw new \InvalidArgumentException(sprintf('The parent scope "%s" does not exist, or is invalid.', $parentScope));
  349. }
  350. $this->scopes[$name] = $parentScope;
  351. $this->scopeChildren[$name] = array();
  352. // normalize the child relations
  353. while ($parentScope !== self::SCOPE_CONTAINER) {
  354. $this->scopeChildren[$parentScope][] = $name;
  355. $parentScope = $this->scopes[$parentScope];
  356. }
  357. }
  358. /**
  359. * Returns whether this container has a certain scope
  360. *
  361. * @param string $name The name of the scope
  362. *
  363. * @return Boolean
  364. *
  365. * @api
  366. */
  367. public function hasScope($name)
  368. {
  369. return isset($this->scopes[$name]);
  370. }
  371. /**
  372. * Returns whether this scope is currently active
  373. *
  374. * This does not actually check if the passed scope actually exists.
  375. *
  376. * @param string $name
  377. *
  378. * @return Boolean
  379. *
  380. * @api
  381. */
  382. public function isScopeActive($name)
  383. {
  384. return isset($this->scopedServices[$name]);
  385. }
  386. /**
  387. * Camelizes a string.
  388. *
  389. * @param string $id A string to camelize
  390. *
  391. * @return string The camelized string
  392. */
  393. static public function camelize($id)
  394. {
  395. return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { return ('.' === $match[1] ? '_' : '').strtoupper($match[2]); }, $id);
  396. }
  397. /**
  398. * A string to underscore.
  399. *
  400. * @param string $id The string to underscore
  401. *
  402. * @return string The underscored string
  403. */
  404. static public function underscore($id)
  405. {
  406. return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($id, '_', '.')));
  407. }
  408. }