DecoratorServicePass.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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\Compiler;
  11. use Symfony\Component\DependencyInjection\Alias;
  12. use Symfony\Component\DependencyInjection\ContainerBuilder;
  13. use Symfony\Component\DependencyInjection\ContainerInterface;
  14. use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  15. use Symfony\Component\DependencyInjection\Reference;
  16. /**
  17. * Overwrites a service but keeps the overridden one.
  18. *
  19. * @author Christophe Coevoet <stof@notk.org>
  20. * @author Fabien Potencier <fabien@symfony.com>
  21. * @author Diego Saint Esteben <diego@saintesteben.me>
  22. */
  23. class DecoratorServicePass extends AbstractRecursivePass
  24. {
  25. private $innerId = '.inner';
  26. public function __construct(?string $innerId = '.inner')
  27. {
  28. $this->innerId = $innerId;
  29. }
  30. public function process(ContainerBuilder $container)
  31. {
  32. $definitions = new \SplPriorityQueue();
  33. $order = \PHP_INT_MAX;
  34. foreach ($container->getDefinitions() as $id => $definition) {
  35. if (!$decorated = $definition->getDecoratedService()) {
  36. continue;
  37. }
  38. $definitions->insert([$id, $definition], [$decorated[2], --$order]);
  39. }
  40. $decoratingDefinitions = [];
  41. foreach ($definitions as [$id, $definition]) {
  42. $decoratedService = $definition->getDecoratedService();
  43. [$inner, $renamedId] = $decoratedService;
  44. $invalidBehavior = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
  45. $definition->setDecoratedService(null);
  46. if (!$renamedId) {
  47. $renamedId = $id.'.inner';
  48. }
  49. $this->currentId = $renamedId;
  50. $this->processValue($definition);
  51. $definition->innerServiceId = $renamedId;
  52. $definition->decorationOnInvalid = $invalidBehavior;
  53. // we create a new alias/service for the service we are replacing
  54. // to be able to reference it in the new one
  55. if ($container->hasAlias($inner)) {
  56. $alias = $container->getAlias($inner);
  57. $public = $alias->isPublic();
  58. $private = $alias->isPrivate();
  59. $container->setAlias($renamedId, new Alias((string) $alias, false));
  60. } elseif ($container->hasDefinition($inner)) {
  61. $decoratedDefinition = $container->getDefinition($inner);
  62. $public = $decoratedDefinition->isPublic();
  63. $private = $decoratedDefinition->isPrivate();
  64. $decoratedDefinition->setPublic(false);
  65. $container->setDefinition($renamedId, $decoratedDefinition);
  66. $decoratingDefinitions[$inner] = $decoratedDefinition;
  67. } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) {
  68. $container->removeDefinition($id);
  69. continue;
  70. } elseif (ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) {
  71. $public = $definition->isPublic();
  72. $private = $definition->isPrivate();
  73. } else {
  74. throw new ServiceNotFoundException($inner, $id);
  75. }
  76. if (isset($decoratingDefinitions[$inner])) {
  77. $decoratingDefinition = $decoratingDefinitions[$inner];
  78. $decoratingTags = $decoratingDefinition->getTags();
  79. $resetTags = [];
  80. if (isset($decoratingTags['container.service_locator'])) {
  81. // container.service_locator has special logic and it must not be transferred out to decorators
  82. $resetTags = ['container.service_locator' => $decoratingTags['container.service_locator']];
  83. unset($decoratingTags['container.service_locator']);
  84. }
  85. $definition->setTags(array_merge($decoratingTags, $definition->getTags()));
  86. $decoratingDefinition->setTags($resetTags);
  87. $decoratingDefinitions[$inner] = $definition;
  88. }
  89. $container->setAlias($inner, $id)->setPublic($public);
  90. }
  91. }
  92. protected function processValue($value, bool $isRoot = false)
  93. {
  94. if ($value instanceof Reference && $this->innerId === (string) $value) {
  95. return new Reference($this->currentId, $value->getInvalidBehavior());
  96. }
  97. return parent::processValue($value, $isRoot);
  98. }
  99. }