vendor/contao/core-bundle/src/Routing/ResponseContext/ResponseContext.php line 18

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of Contao.
  5. *
  6. * (c) Leo Feyer
  7. *
  8. * @license LGPL-3.0-or-later
  9. */
  10. namespace Contao\CoreBundle\Routing\ResponseContext;
  11. use Contao\CoreBundle\Event\AbstractResponseContextEvent;
  12. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  13. final class ResponseContext
  14. {
  15. public const REQUEST_ATTRIBUTE_NAME = '_contao_response_context';
  16. private array $services = [];
  17. private array $current = [];
  18. private ?PartialResponseHeaderBag $headerBag = null;
  19. public function dispatchEvent(AbstractResponseContextEvent $event): void
  20. {
  21. if (!$this->has(EventDispatcherInterface::class)) {
  22. return;
  23. }
  24. $event->setResponseContext($this);
  25. /** @var EventDispatcherInterface $eventDispatcher */
  26. $eventDispatcher = $this->get(EventDispatcherInterface::class);
  27. $eventDispatcher->dispatch($event);
  28. }
  29. public function add(object $service): self
  30. {
  31. $this->registerService(\get_class($service), $service);
  32. return $this;
  33. }
  34. public function addLazy(string $classname, ?\Closure $factory = null): self
  35. {
  36. $factory ??= fn () => new $classname($this);
  37. $this->registerService($classname, $factory);
  38. return $this;
  39. }
  40. public function has(string $serviceId): bool
  41. {
  42. return isset($this->current[$serviceId]);
  43. }
  44. public function isInitialized(string $serviceId): bool
  45. {
  46. if (!$this->has($serviceId)) {
  47. return false;
  48. }
  49. return !$this->services[$serviceId] instanceof \Closure;
  50. }
  51. /**
  52. * @template T
  53. *
  54. * @param class-string<T> $serviceId
  55. *
  56. * @return object
  57. *
  58. * @phpstan-return T
  59. */
  60. public function get(string $serviceId)
  61. {
  62. if (!$this->has($serviceId)) {
  63. throw new \InvalidArgumentException(sprintf('Service "%s" does not exist.', $serviceId));
  64. }
  65. $serviceId = $this->current[$serviceId];
  66. // Lazy load the ones with factories
  67. if (!$this->isInitialized($serviceId)) {
  68. $service = $this->services[$serviceId]();
  69. $this->services[$serviceId] = $service;
  70. }
  71. return $this->services[$serviceId];
  72. }
  73. public function getHeaderBag(): PartialResponseHeaderBag
  74. {
  75. return $this->headerBag ??= new PartialResponseHeaderBag();
  76. }
  77. /**
  78. * @param \Closure|object $objectOrFactory
  79. */
  80. private function registerService(string $serviceId, $objectOrFactory): void
  81. {
  82. $this->services[$serviceId] = $objectOrFactory;
  83. $this->current[$serviceId] = $serviceId;
  84. foreach ($this->getAliases($serviceId) as $alias) {
  85. $this->current[$alias] = $serviceId;
  86. }
  87. }
  88. private function getAliases(string $classname): array
  89. {
  90. $aliases = [];
  91. $ref = new \ReflectionClass($classname);
  92. // Automatically add aliases for all interfaces and parents (last one added automatically wins by overriding here)
  93. foreach ($ref->getInterfaceNames() as $interfaceName) {
  94. $aliases[] = $interfaceName;
  95. }
  96. while ($ref = $ref->getParentClass()) {
  97. $aliases[] = $ref->getName();
  98. }
  99. return $aliases;
  100. }
  101. }