vendor/contao/core-bundle/src/Fragment/FragmentHandler.php line 44

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\Fragment;
  11. use Contao\CoreBundle\Exception\ResponseException;
  12. use Contao\CoreBundle\Fragment\Reference\FragmentReference;
  13. use Contao\PageModel;
  14. use Psr\Container\ContainerInterface;
  15. use Symfony\Component\HttpFoundation\RequestStack;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\HttpKernel\Fragment\FragmentHandler as BaseFragmentHandler;
  18. class FragmentHandler extends BaseFragmentHandler
  19. {
  20. private ContainerInterface $renderers;
  21. private BaseFragmentHandler $fragmentHandler;
  22. private FragmentRegistryInterface $fragmentRegistry;
  23. private ContainerInterface $preHandlers;
  24. private array $initialized = [];
  25. /**
  26. * @internal
  27. */
  28. public function __construct(ContainerInterface $renderers, BaseFragmentHandler $fragmentHandler, RequestStack $requestStack, FragmentRegistryInterface $fragmentRegistry, ContainerInterface $preHandlers, bool $debug = false)
  29. {
  30. $this->renderers = $renderers;
  31. $this->fragmentHandler = $fragmentHandler;
  32. $this->fragmentRegistry = $fragmentRegistry;
  33. $this->preHandlers = $preHandlers;
  34. parent::__construct($requestStack, [], $debug);
  35. }
  36. public function render($uri, string $renderer = 'inline', array $options = []): ?string
  37. {
  38. if (!$uri instanceof FragmentReference) {
  39. return $this->fragmentHandler->render($uri, $renderer, $options);
  40. }
  41. $config = $this->fragmentRegistry->get($uri->controller);
  42. if (null === $config) {
  43. throw new UnknownFragmentException(sprintf('Invalid fragment identifier "%s"', $uri->controller));
  44. }
  45. $this->preHandleFragment($uri, $config);
  46. $renderer = $config->getRenderer();
  47. if ('inline' !== $renderer && $this->containsNonScalars($uri->attributes)) {
  48. $renderer = 'forward';
  49. }
  50. if (!isset($this->initialized[$renderer]) && $this->renderers->has($renderer)) {
  51. $this->addRenderer($this->renderers->get($renderer));
  52. $this->initialized[$renderer] = true;
  53. }
  54. return parent::render($uri, $renderer, $config->getOptions());
  55. }
  56. protected function deliver(Response $response): ?string
  57. {
  58. try {
  59. return parent::deliver($response);
  60. } catch (\RuntimeException $e) {
  61. throw new ResponseException($response, $e);
  62. }
  63. }
  64. /**
  65. * Adds generic attributes and query parameters before rendering.
  66. */
  67. private function preHandleFragment(FragmentReference $uri, FragmentConfig $config): void
  68. {
  69. if (!isset($uri->attributes['pageModel']) && $this->hasGlobalPageObject()) {
  70. $uri->attributes['pageModel'] = $GLOBALS['objPage']->id;
  71. }
  72. if ($this->preHandlers->has($uri->controller)) {
  73. /** @var FragmentPreHandlerInterface $preHandler */
  74. $preHandler = $this->preHandlers->get($uri->controller);
  75. $preHandler->preHandleFragment($uri, $config);
  76. }
  77. }
  78. private function hasGlobalPageObject(): bool
  79. {
  80. return isset($GLOBALS['objPage']) && $GLOBALS['objPage'] instanceof PageModel;
  81. }
  82. private function containsNonScalars(array $values): bool
  83. {
  84. foreach ($values as $value) {
  85. if (\is_array($value)) {
  86. return $this->containsNonScalars($value);
  87. }
  88. if (!\is_scalar($value) && null !== $value) {
  89. return true;
  90. }
  91. }
  92. return false;
  93. }
  94. }