[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/ -> Container.php (source)

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


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1