DecoratorServicePass.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  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 implements CompilerPassInterface
  24. {
  25. public function process(ContainerBuilder $container)
  26. {
  27. $definitions = new \SplPriorityQueue();
  28. $order = PHP_INT_MAX;
  29. foreach ($container->getDefinitions() as $id => $definition) {
  30. if (!$decorated = $definition->getDecoratedService()) {
  31. continue;
  32. }
  33. $definitions->insert([$id, $definition], [$decorated[2], --$order]);
  34. }
  35. $decoratingDefinitions = [];
  36. foreach ($definitions as list($id, $definition)) {
  37. $decoratedService = $definition->getDecoratedService();
  38. list($inner, $renamedId) = $decoratedService;
  39. $invalidBehavior = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
  40. $definition->setDecoratedService(null);
  41. if (!$renamedId) {
  42. $renamedId = $id.'.inner';
  43. }
  44. $definition->innerServiceId = $renamedId;
  45. $definition->decorationOnInvalid = $invalidBehavior;
  46. // we create a new alias/service for the service we are replacing
  47. // to be able to reference it in the new one
  48. if ($container->hasAlias($inner)) {
  49. $alias = $container->getAlias($inner);
  50. $public = $alias->isPublic();
  51. $private = $alias->isPrivate();
  52. $container->setAlias($renamedId, new Alias((string) $alias, false));
  53. } elseif ($container->hasDefinition($inner)) {
  54. $decoratedDefinition = $container->getDefinition($inner);
  55. $public = $decoratedDefinition->isPublic();
  56. $private = $decoratedDefinition->isPrivate();
  57. $decoratedDefinition->setPublic(false);
  58. $container->setDefinition($renamedId, $decoratedDefinition);
  59. $decoratingDefinitions[$inner] = $decoratedDefinition;
  60. } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) {
  61. $container->removeDefinition($id);
  62. continue;
  63. } elseif (ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) {
  64. $public = $definition->isPublic();
  65. $private = $definition->isPrivate();
  66. } else {
  67. throw new ServiceNotFoundException($inner, $id);
  68. }
  69. if (isset($decoratingDefinitions[$inner])) {
  70. $decoratingDefinition = $decoratingDefinitions[$inner];
  71. $decoratingTags = $decoratingDefinition->getTags();
  72. $resetTags = [];
  73. if (isset($decoratingTags['container.service_locator'])) {
  74. // container.service_locator has special logic and it must not be transferred out to decorators
  75. $resetTags = ['container.service_locator' => $decoratingTags['container.service_locator']];
  76. unset($decoratingTags['container.service_locator']);
  77. }
  78. $definition->setTags(array_merge($decoratingTags, $definition->getTags()));
  79. $decoratingDefinition->setTags($resetTags);
  80. $decoratingDefinitions[$inner] = $definition;
  81. }
  82. $container->setAlias($inner, $id)->setPublic($public)->setPrivate($private);
  83. }
  84. }
  85. }