Verzeichnisstruktur phpBB-3.1.0
- Veröffentlicht
- 27.10.2014
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Container.php
001 <?php
002
003 /*
004 * This file is part of the Symfony package.
005 *
006 * (c) Fabien Potencier <fabien@symfony.com>
007 *
008 * For the full copyright and license information, please view the LICENSE
009 * file that was distributed with this source code.
010 */
011
012 namespace Symfony\Component\DependencyInjection;
013
014 use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
015 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
016 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
017 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
018 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
019 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
020 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
021 use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
022
023 /**
024 * Container is a dependency injection container.
025 *
026 * It gives access to object instances (services).
027 *
028 * Services and parameters are simple key/pair stores.
029 *
030 * Parameter and service keys are case insensitive.
031 *
032 * A service id can contain lowercased letters, digits, underscores, and dots.
033 * Underscores are used to separate words, and dots to group services
034 * under namespaces:
035 *
036 * <ul>
037 * <li>request</li>
038 * <li>mysql_session_storage</li>
039 * <li>symfony.mysql_session_storage</li>
040 * </ul>
041 *
042 * A service can also be defined by creating a method named
043 * getXXXService(), where XXX is the camelized version of the id:
044 *
045 * <ul>
046 * <li>request -> getRequestService()</li>
047 * <li>mysql_session_storage -> getMysqlSessionStorageService()</li>
048 * <li>symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()</li>
049 * </ul>
050 *
051 * The container can have three possible behaviors when a service does not exist:
052 *
053 * * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default)
054 * * NULL_ON_INVALID_REFERENCE: Returns null
055 * * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference
056 * (for instance, ignore a setter if the service does not exist)
057 *
058 * @author Fabien Potencier <fabien@symfony.com>
059 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
060 *
061 * @api
062 */
063 class Container implements IntrospectableContainerInterface
064 {
065 /**
066 * @var ParameterBagInterface
067 */
068 protected $parameterBag;
069
070 protected $services;
071 protected $methodMap;
072 protected $aliases;
073 protected $scopes;
074 protected $scopeChildren;
075 protected $scopedServices;
076 protected $scopeStacks;
077 protected $loading = array();
078
079 /**
080 * Constructor.
081 *
082 * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance
083 *
084 * @api
085 */
086 public function __construct(ParameterBagInterface $parameterBag = null)
087 {
088 $this->parameterBag = null === $parameterBag ? new ParameterBag() : $parameterBag;
089
090 $this->services = array();
091 $this->aliases = array();
092 $this->scopes = array();
093 $this->scopeChildren = array();
094 $this->scopedServices = array();
095 $this->scopeStacks = array();
096 }
097
098 /**
099 * Compiles the container.
100 *
101 * This method does two things:
102 *
103 * * Parameter values are resolved;
104 * * The parameter bag is frozen.
105 *
106 * @api
107 */
108 public function compile()
109 {
110 $this->parameterBag->resolve();
111
112 $this->parameterBag = new FrozenParameterBag($this->parameterBag->all());
113 }
114
115 /**
116 * Returns true if the container parameter bag are frozen.
117 *
118 * @return bool true if the container parameter bag are frozen, false otherwise
119 *
120 * @api
121 */
122 public function isFrozen()
123 {
124 return $this->parameterBag instanceof FrozenParameterBag;
125 }
126
127 /**
128 * Gets the service container parameter bag.
129 *
130 * @return ParameterBagInterface A ParameterBagInterface instance
131 *
132 * @api
133 */
134 public function getParameterBag()
135 {
136 return $this->parameterBag;
137 }
138
139 /**
140 * Gets a parameter.
141 *
142 * @param string $name The parameter name
143 *
144 * @return mixed The parameter value
145 *
146 * @throws InvalidArgumentException if the parameter is not defined
147 *
148 * @api
149 */
150 public function getParameter($name)
151 {
152 return $this->parameterBag->get($name);
153 }
154
155 /**
156 * Checks if a parameter exists.
157 *
158 * @param string $name The parameter name
159 *
160 * @return bool The presence of parameter in container
161 *
162 * @api
163 */
164 public function hasParameter($name)
165 {
166 return $this->parameterBag->has($name);
167 }
168
169 /**
170 * Sets a parameter.
171 *
172 * @param string $name The parameter name
173 * @param mixed $value The parameter value
174 *
175 * @api
176 */
177 public function setParameter($name, $value)
178 {
179 $this->parameterBag->set($name, $value);
180 }
181
182 /**
183 * Sets a service.
184 *
185 * Setting a service to null resets the service: has() returns false and get()
186 * behaves in the same way as if the service was never created.
187 *
188 * @param string $id The service identifier
189 * @param object $service The service instance
190 * @param string $scope The scope of the service
191 *
192 * @throws RuntimeException When trying to set a service in an inactive scope
193 * @throws InvalidArgumentException When trying to set a service in the prototype scope
194 *
195 * @api
196 */
197 public function set($id, $service, $scope = self::SCOPE_CONTAINER)
198 {
199 if (self::SCOPE_PROTOTYPE === $scope) {
200 throw new InvalidArgumentException(sprintf('You cannot set service "%s" of scope "prototype".', $id));
201 }
202
203 $id = strtolower($id);
204
205 if ('service_container' === $id) {
206 // BC: 'service_container' is no longer a self-reference but always
207 // $this, so ignore this call.
208 // @todo Throw InvalidArgumentException in next major release.
209 return;
210 }
211 if (self::SCOPE_CONTAINER !== $scope) {
212 if (!isset($this->scopedServices[$scope])) {
213 throw new RuntimeException(sprintf('You cannot set service "%s" of inactive scope.', $id));
214 }
215
216 $this->scopedServices[$scope][$id] = $service;
217 }
218
219 $this->services[$id] = $service;
220
221 if (method_exists($this, $method = 'synchronize'.strtr($id, array('_' => '', '.' => '_', '\\' => '_')).'Service')) {
222 $this->$method();
223 }
224
225 if (self::SCOPE_CONTAINER !== $scope && null === $service) {
226 unset($this->scopedServices[$scope][$id]);
227 }
228
229 if (null === $service) {
230 unset($this->services[$id]);
231 }
232 }
233
234 /**
235 * Returns true if the given service is defined.
236 *
237 * @param string $id The service identifier
238 *
239 * @return bool true if the service is defined, false otherwise
240 *
241 * @api
242 */
243 public function has($id)
244 {
245 $id = strtolower($id);
246
247 if ('service_container' === $id) {
248 return true;
249 }
250
251 return isset($this->services[$id])
252 || array_key_exists($id, $this->services)
253 || isset($this->aliases[$id])
254 || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_', '\\' => '_')).'Service')
255 ;
256 }
257
258 /**
259 * Gets a service.
260 *
261 * If a service is defined both through a set() method and
262 * with a get{$id}Service() method, the former has always precedence.
263 *
264 * @param string $id The service identifier
265 * @param int $invalidBehavior The behavior when the service does not exist
266 *
267 * @return object The associated service
268 *
269 * @throws InvalidArgumentException if the service is not defined
270 * @throws ServiceCircularReferenceException When a circular reference is detected
271 * @throws ServiceNotFoundException When the service is not defined
272 * @throws \Exception if an exception has been thrown when the service has been resolved
273 *
274 * @see Reference
275 *
276 * @api
277 */
278 public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE)
279 {
280 // Attempt to retrieve the service by checking first aliases then
281 // available services. Service IDs are case insensitive, however since
282 // this method can be called thousands of times during a request, avoid
283 // calling strtolower() unless necessary.
284 foreach (array(false, true) as $strtolower) {
285 if ($strtolower) {
286 $id = strtolower($id);
287 }
288 if ('service_container' === $id) {
289 return $this;
290 }
291 if (isset($this->aliases[$id])) {
292 $id = $this->aliases[$id];
293 }
294 // Re-use shared service instance if it exists.
295 if (isset($this->services[$id]) || array_key_exists($id, $this->services)) {
296 return $this->services[$id];
297 }
298 }
299
300 if (isset($this->loading[$id])) {
301 throw new ServiceCircularReferenceException($id, array_keys($this->loading));
302 }
303
304 if (isset($this->methodMap[$id])) {
305 $method = $this->methodMap[$id];
306 } elseif (method_exists($this, $method = 'get'.strtr($id, array('_' => '', '.' => '_', '\\' => '_')).'Service')) {
307 // $method is set to the right value, proceed
308 } else {
309 if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
310 if (!$id) {
311 throw new ServiceNotFoundException($id);
312 }
313
314 $alternatives = array();
315 foreach (array_keys($this->services) as $key) {
316 $lev = levenshtein($id, $key);
317 if ($lev <= strlen($id) / 3 || false !== strpos($key, $id)) {
318 $alternatives[] = $key;
319 }
320 }
321
322 throw new ServiceNotFoundException($id, null, null, $alternatives);
323 }
324
325 return;
326 }
327
328 $this->loading[$id] = true;
329
330 try {
331 $service = $this->$method();
332 } catch (\Exception $e) {
333 unset($this->loading[$id]);
334
335 if (array_key_exists($id, $this->services)) {
336 unset($this->services[$id]);
337 }
338
339 if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
340 return;
341 }
342
343 throw $e;
344 }
345
346 unset($this->loading[$id]);
347
348 return $service;
349 }
350
351 /**
352 * Returns true if the given service has actually been initialized
353 *
354 * @param string $id The service identifier
355 *
356 * @return bool true if service has already been initialized, false otherwise
357 */
358 public function initialized($id)
359 {
360 $id = strtolower($id);
361
362 if ('service_container' === $id) {
363 // BC: 'service_container' was a synthetic service previously.
364 // @todo Change to false in next major release.
365 return true;
366 }
367
368 return isset($this->services[$id]) || array_key_exists($id, $this->services);
369 }
370
371 /**
372 * Gets all service ids.
373 *
374 * @return array An array of all defined service ids
375 */
376 public function getServiceIds()
377 {
378 $ids = array();
379 $r = new \ReflectionClass($this);
380 foreach ($r->getMethods() as $method) {
381 if (preg_match('/^get(.+)Service$/', $method->name, $match)) {
382 $ids[] = self::underscore($match[1]);
383 }
384 }
385 $ids[] = 'service_container';
386
387 return array_unique(array_merge($ids, array_keys($this->services)));
388 }
389
390 /**
391 * This is called when you enter a scope
392 *
393 * @param string $name
394 *
395 * @throws RuntimeException When the parent scope is inactive
396 * @throws InvalidArgumentException When the scope does not exist
397 *
398 * @api
399 */
400 public function enterScope($name)
401 {
402 if (!isset($this->scopes[$name])) {
403 throw new InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name));
404 }
405
406 if (self::SCOPE_CONTAINER !== $this->scopes[$name] && !isset($this->scopedServices[$this->scopes[$name]])) {
407 throw new RuntimeException(sprintf('The parent scope "%s" must be active when entering this scope.', $this->scopes[$name]));
408 }
409
410 // check if a scope of this name is already active, if so we need to
411 // remove all services of this scope, and those of any of its child
412 // scopes from the global services map
413 if (isset($this->scopedServices[$name])) {
414 $services = array($this->services, $name => $this->scopedServices[$name]);
415 unset($this->scopedServices[$name]);
416
417 foreach ($this->scopeChildren[$name] as $child) {
418 if (isset($this->scopedServices[$child])) {
419 $services[$child] = $this->scopedServices[$child];
420 unset($this->scopedServices[$child]);
421 }
422 }
423
424 // update global map
425 $this->services = call_user_func_array('array_diff_key', $services);
426 array_shift($services);
427
428 // add stack entry for this scope so we can restore the removed services later
429 if (!isset($this->scopeStacks[$name])) {
430 $this->scopeStacks[$name] = new \SplStack();
431 }
432 $this->scopeStacks[$name]->push($services);
433 }
434
435 $this->scopedServices[$name] = array();
436 }
437
438 /**
439 * This is called to leave the current scope, and move back to the parent
440 * scope.
441 *
442 * @param string $name The name of the scope to leave
443 *
444 * @throws InvalidArgumentException if the scope is not active
445 *
446 * @api
447 */
448 public function leaveScope($name)
449 {
450 if (!isset($this->scopedServices[$name])) {
451 throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $name));
452 }
453
454 // remove all services of this scope, or any of its child scopes from
455 // the global service map
456 $services = array($this->services, $this->scopedServices[$name]);
457 unset($this->scopedServices[$name]);
458 foreach ($this->scopeChildren[$name] as $child) {
459 if (!isset($this->scopedServices[$child])) {
460 continue;
461 }
462
463 $services[] = $this->scopedServices[$child];
464 unset($this->scopedServices[$child]);
465 }
466 $this->services = call_user_func_array('array_diff_key', $services);
467
468 // check if we need to restore services of a previous scope of this type
469 if (isset($this->scopeStacks[$name]) && count($this->scopeStacks[$name]) > 0) {
470 $services = $this->scopeStacks[$name]->pop();
471 $this->scopedServices += $services;
472
473 foreach ($services as $array) {
474 foreach ($array as $id => $service) {
475 $this->set($id, $service, $name);
476 }
477 }
478 }
479 }
480
481 /**
482 * Adds a scope to the container.
483 *
484 * @param ScopeInterface $scope
485 *
486 * @throws InvalidArgumentException
487 *
488 * @api
489 */
490 public function addScope(ScopeInterface $scope)
491 {
492 $name = $scope->getName();
493 $parentScope = $scope->getParentName();
494
495 if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) {
496 throw new InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name));
497 }
498 if (isset($this->scopes[$name])) {
499 throw new InvalidArgumentException(sprintf('A scope with name "%s" already exists.', $name));
500 }
501 if (self::SCOPE_CONTAINER !== $parentScope && !isset($this->scopes[$parentScope])) {
502 throw new InvalidArgumentException(sprintf('The parent scope "%s" does not exist, or is invalid.', $parentScope));
503 }
504
505 $this->scopes[$name] = $parentScope;
506 $this->scopeChildren[$name] = array();
507
508 // normalize the child relations
509 while ($parentScope !== self::SCOPE_CONTAINER) {
510 $this->scopeChildren[$parentScope][] = $name;
511 $parentScope = $this->scopes[$parentScope];
512 }
513 }
514
515 /**
516 * Returns whether this container has a certain scope
517 *
518 * @param string $name The name of the scope
519 *
520 * @return bool
521 *
522 * @api
523 */
524 public function hasScope($name)
525 {
526 return isset($this->scopes[$name]);
527 }
528
529 /**
530 * Returns whether this scope is currently active
531 *
532 * This does not actually check if the passed scope actually exists.
533 *
534 * @param string $name
535 *
536 * @return bool
537 *
538 * @api
539 */
540 public function isScopeActive($name)
541 {
542 return isset($this->scopedServices[$name]);
543 }
544
545 /**
546 * Camelizes a string.
547 *
548 * @param string $id A string to camelize
549 *
550 * @return string The camelized string
551 */
552 public static function camelize($id)
553 {
554 return strtr(ucwords(strtr($id, array('_' => ' ', '.' => '_ ', '\\' => '_ '))), array(' ' => ''));
555 }
556
557 /**
558 * A string to underscore.
559 *
560 * @param string $id The string to underscore
561 *
562 * @return string The underscored string
563 */
564 public static function underscore($id)
565 {
566 return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($id, '_', '.')));
567 }
568 }
569