vendor/pimcore/pimcore/bundles/CoreBundle/EventListener/Frontend/GoogleTagManagerListener.php line 76

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4.  * Pimcore
  5.  *
  6.  * This source file is available under two different licenses:
  7.  * - GNU General Public License version 3 (GPLv3)
  8.  * - Pimcore Commercial License (PCL)
  9.  * Full copyright and license information is available in
  10.  * LICENSE.md which is distributed with this source code.
  11.  *
  12.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  13.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  14.  */
  15. namespace Pimcore\Bundle\CoreBundle\EventListener\Frontend;
  16. use Pimcore\Analytics\Code\CodeBlock;
  17. use Pimcore\Analytics\SiteId\SiteIdProvider;
  18. use Pimcore\Bundle\CoreBundle\EventListener\Traits\EnabledTrait;
  19. use Pimcore\Bundle\CoreBundle\EventListener\Traits\PimcoreContextAwareTrait;
  20. use Pimcore\Bundle\CoreBundle\EventListener\Traits\PreviewRequestTrait;
  21. use Pimcore\Bundle\CoreBundle\EventListener\Traits\ResponseInjectionTrait;
  22. use Pimcore\Config;
  23. use Pimcore\Event\Analytics\Google\TagManager\CodeEvent;
  24. use Pimcore\Event\Analytics\GoogleTagManagerEvents;
  25. use Pimcore\Http\Request\Resolver\PimcoreContextResolver;
  26. use Pimcore\Tool;
  27. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  28. use Symfony\Component\Templating\EngineInterface;
  29. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  30. /**
  31.  * @internal
  32.  */
  33. class GoogleTagManagerListener
  34. {
  35.     const BLOCK_HEAD_BEFORE_SCRIPT_TAG 'beforeScriptTag';
  36.     const BLOCK_HEAD_AFTER_SCRIPT_TAG 'afterScriptTag';
  37.     const BLOCK_BODY_BEFORE_NOSCRIPT_TAG 'beforeNoscriptTag';
  38.     const BLOCK_BODY_AFTER_NOSCRIPT_TAG 'afterNoscriptTag';
  39.     use EnabledTrait;
  40.     use ResponseInjectionTrait;
  41.     use PimcoreContextAwareTrait;
  42.     use PreviewRequestTrait;
  43.     /**
  44.      * @var array
  45.      */
  46.     private $headBlocks = [
  47.         self::BLOCK_HEAD_BEFORE_SCRIPT_TAG,
  48.         self::BLOCK_HEAD_AFTER_SCRIPT_TAG,
  49.     ];
  50.     /**
  51.      * @var array
  52.      */
  53.     private $bodyBlocks = [
  54.         self::BLOCK_BODY_BEFORE_NOSCRIPT_TAG,
  55.         self::BLOCK_BODY_AFTER_NOSCRIPT_TAG,
  56.     ];
  57.     public function __construct(
  58.         private SiteIdProvider $siteIdProvider,
  59.         private EventDispatcherInterface $eventDispatcher,
  60.         private EngineInterface $templatingEngine
  61.     ) {
  62.     }
  63.     public function onKernelResponse(ResponseEvent $event)
  64.     {
  65.         if (!$this->isEnabled()) {
  66.             return;
  67.         }
  68.         $request $event->getRequest();
  69.         if (!$event->isMainRequest()) {
  70.             return;
  71.         }
  72.         // only inject tag manager code on non-admin requests
  73.         if (!$this->matchesPimcoreContext($requestPimcoreContextResolver::CONTEXT_DEFAULT)) {
  74.             return;
  75.         }
  76.         if (!Tool::useFrontendOutputFilters()) {
  77.             return;
  78.         }
  79.         if ($this->isPreviewRequest($request)) {
  80.             return;
  81.         }
  82.         $siteId $this->siteIdProvider->getForRequest($event->getRequest());
  83.         $siteKey $siteId->getConfigKey();
  84.         $reportConfig Config::getReportConfig();
  85.         if (!isset($reportConfig->get('tagmanager')->sites->$siteKey->containerId)) {
  86.             return;
  87.         }
  88.         $containerId $reportConfig->get('tagmanager')->sites->$siteKey->containerId;
  89.         if (!$containerId) {
  90.             return;
  91.         }
  92.         $response $event->getResponse();
  93.         if (!$this->isHtmlResponse($response)) {
  94.             return;
  95.         }
  96.         $codeHead $this->generateCode(
  97.             GoogleTagManagerEvents::CODE_HEAD,
  98.             '@PimcoreCore/Google/TagManager/codeHead.html.twig',
  99.             $this->headBlocks,
  100.             [
  101.                 'containerId' => $containerId,
  102.             ]
  103.         );
  104.         $codeBody $this->generateCode(
  105.             GoogleTagManagerEvents::CODE_BODY,
  106.             '@PimcoreCore/Google/TagManager/codeBody.html.twig',
  107.             $this->bodyBlocks,
  108.             [
  109.                 'containerId' => $containerId,
  110.             ]
  111.         );
  112.         $content $response->getContent();
  113.         if (!empty($codeHead)) {
  114.             // search for the end <head> tag, and insert the google tag manager code before
  115.             // this method is much faster than using simple_html_dom and uses less memory
  116.             $headEndPosition stripos($content'</head>');
  117.             if ($headEndPosition !== false) {
  118.                 $content substr_replace($content$codeHead '</head>'$headEndPosition7);
  119.             }
  120.         }
  121.         if (!empty($codeBody)) {
  122.             // insert code after the opening <body> tag
  123.             $content preg_replace('@<body(>|.*?[^?]>)@'"<body$1\n\n" $codeBody$content);
  124.         }
  125.         $response->setContent($content);
  126.     }
  127.     private function generateCode(string $eventNamestring $template, array $blockNames, array $data): string
  128.     {
  129.         $blocks = [];
  130.         foreach ($blockNames as $blockName) {
  131.             $blocks[$blockName] = new CodeBlock();
  132.         }
  133.         $event = new CodeEvent($data$blocks$template);
  134.         $this->eventDispatcher->dispatch($event$eventName);
  135.         return $this->renderTemplate($event);
  136.     }
  137.     private function renderTemplate(CodeEvent $event): string
  138.     {
  139.         $data $event->getData();
  140.         $data['blocks'] = $event->getBlocks();
  141.         $code $this->templatingEngine->render(
  142.             $event->getTemplate(),
  143.             $data
  144.         );
  145.         $code trim($code);
  146.         if (!empty($code)) {
  147.             $code "\n" $code "\n";
  148.         }
  149.         return $code;
  150.     }
  151. }