PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/wwwroot/phpbb/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Container.php

https://github.com/spring/spring-website
PHP | 568 lines | 384 code | 24 blank | 160 comment | 12 complexity | cdbd467af7199e0005566880716543e3 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0, LGPL-3.0, 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\InactiveScopeException;
  12. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  13. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  14. use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  15. use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
  16. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  17. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
  18. use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
  19. /**
  20. * Container is a dependency injection container.
  21. *
  22. * It gives access to object instances (services).
  23. *
  24. * Services and parameters are simple key/pair stores.
  25. *
  26. * Parameter and service keys are case insensitive.
  27. *
  28. * A service id can contain lowercased letters, digits, underscores, and dots.
  29. * Underscores are used to separate words, and dots to group services
  30. * under namespaces:
  31. *
  32. * <ul>
  33. * <li>request</li>
  34. * <li>mysql_session_storage</li>
  35. * <li>symfony.mysql_session_storage</li>
  36. * </ul>
  37. *
  38. * A service can also be defined by creating a method named
  39. * getXXXService(), where XXX is the camelized version of the id:
  40. *
  41. * <ul>
  42. * <li>request -> getRequestService()</li>
  43. * <li>mysql_session_storage -> getMysqlSessionStorageService()</li>
  44. * <li>symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()</li>
  45. * </ul>
  46. *
  47. * The container can have three possible behaviors when a service does not exist:
  48. *
  49. * * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default)
  50. * * NULL_ON_INVALID_REFERENCE: Returns null
  51. * * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference
  52. * (for instance, ignore a setter if the service does not exist)
  53. *
  54. * @author Fabien Potencier <fabien@symfony.com>
  55. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  56. *
  57. * @api
  58. */
  59. class Container implements IntrospectableContainerInterface
  60. {
  61. /**
  62. * @var ParameterBagInterface
  63. */
  64. protected $parameterBag;
  65. protected $services;
  66. protected $methodMap;
  67. protected $aliases;
  68. protected $scopes;
  69. protected $scopeChildren;
  70. protected $scopedServices;
  71. protected $scopeStacks;
  72. protected $loading = array();
  73. /**
  74. * Constructor.
  75. *
  76. * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance
  77. *
  78. * @api
  79. */
  80. public function __construct(ParameterBagInterface $parameterBag = null)
  81. {
  82. $this->parameterBag = null === $parameterBag ? new ParameterBag() : $parameterBag;
  83. $this->services = array();
  84. $this->aliases = array();
  85. $this->scopes = array();
  86. $this->scopeChildren = array();
  87. $this->scopedServices = array();
  88. $this->scopeStacks = array();
  89. }
  90. /**
  91. * Compiles the container.
  92. *
  93. * This method does two things:
  94. *
  95. * * Parameter values are resolved;
  96. * * The parameter bag is frozen.
  97. *
  98. * @api
  99. */
  100. public function compile()
  101. {
  102. $this->parameterBag->resolve();
  103. $this->parameterBag = new FrozenParameterBag($this->parameterBag->all());
  104. }
  105. /**
  106. * Returns true if the container parameter bag are frozen.
  107. *
  108. * @return bool true if the container parameter bag are frozen, false otherwise
  109. *
  110. * @api
  111. */
  112. public function isFrozen()
  113. {
  114. return $this->parameterBag instanceof FrozenParameterBag;
  115. }
  116. /**
  117. * Gets the service container parameter bag.
  118. *
  119. * @return ParameterBagInterface A ParameterBagInterface instance
  120. *
  121. * @api
  122. */
  123. public function getParameterBag()
  124. {
  125. return $this->parameterBag;
  126. }
  127. /**
  128. * Gets a parameter.
  129. *
  130. * @param string $name The parameter name
  131. *
  132. * @return mixed The parameter value
  133. *
  134. * @throws InvalidArgumentException if the parameter is not defined
  135. *
  136. * @api
  137. */
  138. public function getParameter($name)
  139. {
  140. return $this->parameterBag->get($name);
  141. }
  142. /**
  143. * Checks if a parameter exists.
  144. *
  145. * @param string $name The parameter name
  146. *
  147. * @return bool The presence of parameter in container
  148. *
  149. * @api
  150. */
  151. public function hasParameter($name)
  152. {
  153. return $this->parameterBag->has($name);
  154. }
  155. /**
  156. * Sets a parameter.
  157. *
  158. * @param string $name The parameter name
  159. * @param mixed $value The parameter value
  160. *
  161. * @api
  162. */
  163. public function setParameter($name, $value)
  164. {
  165. $this->parameterBag->set($name, $value);
  166. }
  167. /**
  168. * Sets a service.
  169. *
  170. * Setting a service to null resets the service: has() returns false and get()
  171. * behaves in the same way as if the service was never created.
  172. *
  173. * @param string $id The service identifier
  174. * @param object $service The service instance
  175. * @param string $scope The scope of the service
  176. *
  177. * @throws RuntimeException When trying to set a service in an inactive scope
  178. * @throws InvalidArgumentException When trying to set a service in the prototype scope
  179. *
  180. * @api
  181. */
  182. public function set($id, $service, $scope = self::SCOPE_CONTAINER)
  183. {
  184. if (self::SCOPE_PROTOTYPE === $scope) {
  185. throw new InvalidArgumentException(sprintf('You cannot set service "%s" of scope "prototype".', $id));
  186. }
  187. $id = strtolower($id);
  188. if ('service_container' === $id) {
  189. // BC: 'service_container' is no longer a self-reference but always
  190. // $this, so ignore this call.
  191. // @todo Throw InvalidArgumentException in next major release.
  192. return;
  193. }
  194. if (self::SCOPE_CONTAINER !== $scope) {
  195. if (!isset($this->scopedServices[$scope])) {
  196. throw new RuntimeException(sprintf('You cannot set service "%s" of inactive scope.', $id));
  197. }
  198. $this->scopedServices[$scope][$id] = $service;
  199. }
  200. $this->services[$id] = $service;
  201. if (method_exists($this, $method = 'synchronize'.strtr($id, array('_' => '', '.' => '_', '\\' => '_')).'Service')) {
  202. $this->$method();
  203. }
  204. if (self::SCOPE_CONTAINER !== $scope && null === $service) {
  205. unset($this->scopedServices[$scope][$id]);
  206. }
  207. if (null === $service) {
  208. unset($this->services[$id]);
  209. }
  210. }
  211. /**
  212. * Returns true if the given service is defined.
  213. *
  214. * @param string $id The service identifier
  215. *
  216. * @return bool true if the service is defined, false otherwise
  217. *
  218. * @api
  219. */
  220. public function has($id)
  221. {
  222. $id = strtolower($id);
  223. if ('service_container' === $id) {
  224. return true;
  225. }
  226. return isset($this->services[$id])
  227. || array_key_exists($id, $this->services)
  228. || isset($this->aliases[$id])
  229. || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_', '\\' => '_')).'Service')
  230. ;
  231. }
  232. /**
  233. * Gets a service.
  234. *
  235. * If a service is defined both through a set() method and
  236. * with a get{$id}Service() method, the former has always precedence.
  237. *
  238. * @param string $id The service identifier
  239. * @param int $invalidBehavior The behavior when the service does not exist
  240. *
  241. * @return object The associated service
  242. *
  243. * @throws InvalidArgumentException if the service is not defined
  244. * @throws ServiceCircularReferenceException When a circular reference is detected
  245. * @throws ServiceNotFoundException When the service is not defined
  246. * @throws \Exception if an exception has been thrown when the service has been resolved
  247. *
  248. * @see Reference
  249. *
  250. * @api
  251. */
  252. public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE)
  253. {
  254. // Attempt to retrieve the service by checking first aliases then
  255. // available services. Service IDs are case insensitive, however since
  256. // this method can be called thousands of times during a request, avoid
  257. // calling strtolower() unless necessary.
  258. foreach (array(false, true) as $strtolower) {
  259. if ($strtolower) {
  260. $id = strtolower($id);
  261. }
  262. if ('service_container' === $id) {
  263. return $this;
  264. }
  265. if (isset($this->aliases[$id])) {
  266. $id = $this->aliases[$id];
  267. }
  268. // Re-use shared service instance if it exists.
  269. if (isset($this->services[$id]) || array_key_exists($id, $this->services)) {
  270. return $this->services[$id];
  271. }
  272. }
  273. if (isset($this->loading[$id])) {
  274. throw new ServiceCircularReferenceException($id, array_keys($this->loading));
  275. }
  276. if (isset($this->methodMap[$id])) {
  277. $method = $this->methodMap[$id];
  278. } elseif (method_exists($this, $method = 'get'.strtr($id, array('_' => '', '.' => '_', '\\' => '_')).'Service')) {
  279. // $method is set to the right value, proceed
  280. } else {
  281. if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
  282. if (!$id) {
  283. throw new ServiceNotFoundException($id);
  284. }
  285. $alternatives = array();
  286. foreach (array_keys($this->services) as $key) {
  287. $lev = levenshtein($id, $key);
  288. if ($lev <= strlen($id) / 3 || false !== strpos($key, $id)) {
  289. $alternatives[] = $key;
  290. }
  291. }
  292. throw new ServiceNotFoundException($id, null, null, $alternatives);
  293. }
  294. return;
  295. }
  296. $this->loading[$id] = true;
  297. try {
  298. $service = $this->$method();
  299. } catch (\Exception $e) {
  300. unset($this->loading[$id]);
  301. if (array_key_exists($id, $this->services)) {
  302. unset($this->services[$id]);
  303. }
  304. if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
  305. return;
  306. }
  307. throw $e;
  308. }
  309. unset($this->loading[$id]);
  310. return $service;
  311. }
  312. /**
  313. * Returns true if the given service has actually been initialized
  314. *
  315. * @param string $id The service identifier
  316. *
  317. * @return bool true if service has already been initialized, false otherwise
  318. */
  319. public function initialized($id)
  320. {
  321. $id = strtolower($id);
  322. if ('service_container' === $id) {
  323. // BC: 'service_container' was a synthetic service previously.
  324. // @todo Change to false in next major release.
  325. return true;
  326. }
  327. return isset($this->services[$id]) || array_key_exists($id, $this->services);
  328. }
  329. /**
  330. * Gets all service ids.
  331. *
  332. * @return array An array of all defined service ids
  333. */
  334. public function getServiceIds()
  335. {
  336. $ids = array();
  337. $r = new \ReflectionClass($this);
  338. foreach ($r->getMethods() as $method) {
  339. if (preg_match('/^get(.+)Service$/', $method->name, $match)) {
  340. $ids[] = self::underscore($match[1]);
  341. }
  342. }
  343. $ids[] = 'service_container';
  344. return array_unique(array_merge($ids, array_keys($this->services)));
  345. }
  346. /**
  347. * This is called when you enter a scope
  348. *
  349. * @param string $name
  350. *
  351. * @throws RuntimeException When the parent scope is inactive
  352. * @throws InvalidArgumentException When the scope does not exist
  353. *
  354. * @api
  355. */
  356. public function enterScope($name)
  357. {
  358. if (!isset($this->scopes[$name])) {
  359. throw new InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name));
  360. }
  361. if (self::SCOPE_CONTAINER !== $this->scopes[$name] && !isset($this->scopedServices[$this->scopes[$name]])) {
  362. throw new RuntimeException(sprintf('The parent scope "%s" must be active when entering this scope.', $this->scopes[$name]));
  363. }
  364. // check if a scope of this name is already active, if so we need to
  365. // remove all services of this scope, and those of any of its child
  366. // scopes from the global services map
  367. if (isset($this->scopedServices[$name])) {
  368. $services = array($this->services, $name => $this->scopedServices[$name]);
  369. unset($this->scopedServices[$name]);
  370. foreach ($this->scopeChildren[$name] as $child) {
  371. if (isset($this->scopedServices[$child])) {
  372. $services[$child] = $this->scopedServices[$child];
  373. unset($this->scopedServices[$child]);
  374. }
  375. }
  376. // update global map
  377. $this->services = call_user_func_array('array_diff_key', $services);
  378. array_shift($services);
  379. // add stack entry for this scope so we can restore the removed services later
  380. if (!isset($this->scopeStacks[$name])) {
  381. $this->scopeStacks[$name] = new \SplStack();
  382. }
  383. $this->scopeStacks[$name]->push($services);
  384. }
  385. $this->scopedServices[$name] = array();
  386. }
  387. /**
  388. * This is called to leave the current scope, and move back to the parent
  389. * scope.
  390. *
  391. * @param string $name The name of the scope to leave
  392. *
  393. * @throws InvalidArgumentException if the scope is not active
  394. *
  395. * @api
  396. */
  397. public function leaveScope($name)
  398. {
  399. if (!isset($this->scopedServices[$name])) {
  400. throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $name));
  401. }
  402. // remove all services of this scope, or any of its child scopes from
  403. // the global service map
  404. $services = array($this->services, $this->scopedServices[$name]);
  405. unset($this->scopedServices[$name]);
  406. foreach ($this->scopeChildren[$name] as $child) {
  407. if (!isset($this->scopedServices[$child])) {
  408. continue;
  409. }
  410. $services[] = $this->scopedServices[$child];
  411. unset($this->scopedServices[$child]);
  412. }
  413. $this->services = call_user_func_array('array_diff_key', $services);
  414. // check if we need to restore services of a previous scope of this type
  415. if (isset($this->scopeStacks[$name]) && count($this->scopeStacks[$name]) > 0) {
  416. $services = $this->scopeStacks[$name]->pop();
  417. $this->scopedServices += $services;
  418. foreach ($services as $array) {
  419. foreach ($array as $id => $service) {
  420. $this->set($id, $service, $name);
  421. }
  422. }
  423. }
  424. }
  425. /**
  426. * Adds a scope to the container.
  427. *
  428. * @param ScopeInterface $scope
  429. *
  430. * @throws InvalidArgumentException
  431. *
  432. * @api
  433. */
  434. public function addScope(ScopeInterface $scope)
  435. {
  436. $name = $scope->getName();
  437. $parentScope = $scope->getParentName();
  438. if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) {
  439. throw new InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name));
  440. }
  441. if (isset($this->scopes[$name])) {
  442. throw new InvalidArgumentException(sprintf('A scope with name "%s" already exists.', $name));
  443. }
  444. if (self::SCOPE_CONTAINER !== $parentScope && !isset($this->scopes[$parentScope])) {
  445. throw new InvalidArgumentException(sprintf('The parent scope "%s" does not exist, or is invalid.', $parentScope));
  446. }
  447. $this->scopes[$name] = $parentScope;
  448. $this->scopeChildren[$name] = array();
  449. // normalize the child relations
  450. while ($parentScope !== self::SCOPE_CONTAINER) {
  451. $this->scopeChildren[$parentScope][] = $name;
  452. $parentScope = $this->scopes[$parentScope];
  453. }
  454. }
  455. /**
  456. * Returns whether this container has a certain scope
  457. *
  458. * @param string $name The name of the scope
  459. *
  460. * @return bool
  461. *
  462. * @api
  463. */
  464. public function hasScope($name)
  465. {
  466. return isset($this->scopes[$name]);
  467. }
  468. /**
  469. * Returns whether this scope is currently active
  470. *
  471. * This does not actually check if the passed scope actually exists.
  472. *
  473. * @param string $name
  474. *
  475. * @return bool
  476. *
  477. * @api
  478. */
  479. public function isScopeActive($name)
  480. {
  481. return isset($this->scopedServices[$name]);
  482. }
  483. /**
  484. * Camelizes a string.
  485. *
  486. * @param string $id A string to camelize
  487. *
  488. * @return string The camelized string
  489. */
  490. public static function camelize($id)
  491. {
  492. return strtr(ucwords(strtr($id, array('_' => ' ', '.' => '_ ', '\\' => '_ '))), array(' ' => ''));
  493. }
  494. /**
  495. * A string to underscore.
  496. *
  497. * @param string $id The string to underscore
  498. *
  499. * @return string The underscored string
  500. */
  501. public static function underscore($id)
  502. {
  503. return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($id, '_', '.')));
  504. }
  505. }