custom/plugins/WeedesignPageSpeed/src/Listener/ResponseListener.php line 36

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Weedesign\PageSpeed\Listener;
  3. use Composer\Autoload\ClassLoader;
  4. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  5. use Symfony\Component\HttpFoundation\Response;
  6. use Symfony\Component\HttpFoundation\StreamedResponse;
  7. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  8. use JSMin\JSMin;
  9. use Shopware\Core\System\SystemConfig\SystemConfigService;
  10. use Weedesign\PageSpeed\Service\SaveHtaccessFile;
  11. class ResponseListener
  12. {
  13.     private $javascriptPlaceholder '';
  14.     private $spacePlaceholder ' ';
  15.     private $environment;
  16.     private SaveHtaccessFile $saveHtaccessFile;
  17.     private SystemConfigService $systemConfigService;
  18.     public function __construct(
  19.         string $environment
  20.         SystemConfigService $systemConfigService,
  21.         SaveHtaccessFile $saveHtaccessFile
  22.     )
  23.     {
  24.         $this->systemConfigService $systemConfigService;
  25.         $this->environment $environment;
  26.         $this->saveHtaccessFile $saveHtaccessFile;
  27.     }
  28.     /**
  29.      * @param ResponseEvent $event
  30.      */
  31.     public function onKernelResponse(ResponseEvent $event): void
  32.     {
  33.         if($this->systemConfigService->get('WeedesignPageSpeed.config.prod')) {
  34.             if ($this->environment !== 'prod') {
  35.                 return;
  36.             }
  37.         }
  38.         if (!$event->isMainRequest()) {
  39.             return;
  40.         }
  41.         $response $event->getResponse();
  42.         if ($response instanceof BinaryFileResponse ||
  43.             $response instanceof StreamedResponse) {
  44.             return;
  45.         }
  46.         if ($response->getStatusCode() === Response::HTTP_NO_CONTENT) {
  47.             return;
  48.         }
  49.         if (strpos($response->headers->get('Content-Type'''), 'text/html') === false) {
  50.             return;
  51.         }
  52.         $this->minify($response);
  53.     }
  54.     private function resetClassLoader(): void
  55.     {
  56.         $file __DIR__.'/../../vendor/autoload.php';
  57.         if (!is_file($file)) {
  58.             return;
  59.         }
  60.         $classLoader = require_once $file;
  61.         if ($classLoader instanceof ClassLoader) {
  62.             $classLoader->unregister();
  63.             $classLoader->register(false);
  64.         }
  65.     }
  66.     private function minify(Response $response): void
  67.     {
  68.         $startTime microtime(true);
  69.         $content $response->getContent();
  70.         $lengthInitialContent mb_strlen($content'utf8');
  71.         if ($lengthInitialContent === 0) {
  72.             return;
  73.         }
  74.         $htaccess $this->saveHtaccessFile->save();
  75.         
  76.         if ($this->systemConfigService->get('WeedesignPageSpeed.config.webp')) {
  77.             if ($this->systemConfigService->get('WeedesignPageSpeed.config.webpFrontend')) {
  78.                 $this->systemConfigService->set('WeedesignPageSpeed.config.webpCount'0);
  79.             } else {
  80.                 $this->systemConfigService->set('WeedesignPageSpeed.config.webpCount'$this->systemConfigService->get('WeedesignPageSpeed.config.webpInt'));
  81.             }
  82.         }
  83.         if($this->systemConfigService->get('WeedesignPageSpeed.config.css')) {
  84.             if(!$this->systemConfigService->get('WeedesignPageSpeed.config.cssinline')) {
  85.                 $this->minifySourceTypes($content);
  86.                 $javascripts $this->extractCombinedInlineScripts($content);
  87.             }
  88.         }
  89.         if($this->systemConfigService->get('WeedesignPageSpeed.config.html')) {
  90.             $this->minifyHtml($content);
  91.         }
  92.         if($this->systemConfigService->get('WeedesignPageSpeed.config.css')) {
  93.             if(!$this->systemConfigService->get('WeedesignPageSpeed.config.cssinline')) {
  94.                 $content str_replace($this->javascriptPlaceholder'<script>' $javascripts '</script>'$content);
  95.             }
  96.         }
  97.         // $this->assignCompressionHeader($response, $content, $lengthInitialContent, $startTime);
  98.         $response->setContent($content);
  99.     }
  100.     private function minifyJavascript(string $content): string {
  101.         $this->resetClassLoader();
  102.         $jsMin = new JSMin($content);
  103.         return $jsMin->min();
  104.     }
  105.     private function minifyHtml(string &$content): void {
  106.         $search = [
  107.             '/(\n|^)(\x20+|\t)/',
  108.             '/(\n|^)\/\/(.*?)(\n|$)/',
  109.             '/\n/',
  110.             '/\<\!--.*?-->/',
  111.             '/(\x20+|\t)/'# Delete multispace (Without \n)
  112.             '/\s+\<label/'# keep whitespace before label tags
  113.             '/span\>\s+/'# keep whitespace after span tags
  114.             '/\s+\<span/'# keep whitespace before span tags
  115.             '/button\>\s+/'# keep whitespace after button tags
  116.             '/\s+\<button/'# keep whitespace before button tags
  117.             '/\>\s+\</'# strip whitespaces between tags
  118.             '/(\"|\')\s+\>/'# strip whitespaces between quotation ("') and end tags
  119.             '/=\s+(\"|\')/'# strip whitespaces between = "'
  120.             '/' $this->spacePlaceholder '/'# replace the spacePlaceholder at the end
  121.         ];
  122.         $replace = [
  123.             "\n",
  124.             "\n",
  125.             ' ',
  126.             '',
  127.             ' ',
  128.             $this->spacePlaceholder '<label',
  129.             'span>' $this->spacePlaceholder,
  130.             $this->spacePlaceholder '<span',
  131.             'button>' $this->spacePlaceholder,
  132.             $this->spacePlaceholder '<button',
  133.             '><',
  134.             '$1>',
  135.             '=$1',
  136.             ' ',
  137.         ];
  138.         $content trim(preg_replace($search$replace$content));
  139.     }
  140.     private function extractCombinedInlineScripts(string &$content): string
  141.     {
  142.         $scriptContents '';
  143.         $index 0;
  144.         $placeholder $this->javascriptPlaceholder;
  145.         if (strpos($content'</script>') !== false) {
  146.             $content preg_replace_callback('#<script>(.*?)<\/script>#s', function ($matches) use (&$scriptContents, &$index$placeholder) {
  147.                 $index++;
  148.                 $content trim($matches[1]);
  149.                 if (!$this->str_ends_with($content';')) {
  150.                     $content .= ';';
  151.                 }
  152.                 $scriptContents .= $content PHP_EOL;
  153.                 return $index === $placeholder '';
  154.             }, $content);
  155.         }
  156.         return $this->minifyJavascript($scriptContents);
  157.     }
  158.     private function minifySourceTypes(&$content): void
  159.     {
  160.         $search = [
  161.             '/ type=["\']text\/javascript["\']/',
  162.             '/ type=["\']text\/css["\']/',
  163.         ];
  164.         $replace '';
  165.         $content preg_replace($search$replace$content);
  166.     }
  167.     private function assignCompressionHeader(Response $responsestring $contentint $lengthInitialContentfloat $startTime): void
  168.     {
  169.         $lengthContent mb_strlen($content'utf8');
  170.         $savedData round(100 100 / ($lengthInitialContent $lengthContent), 2);
  171.         $timeTook = (int)((microtime(true) - $startTime) * 1000);
  172.         $response->headers->add(['X-Html-Compressor' => time() . ': ' $savedData '% ' $timeTook 'ms']);
  173.     }
  174.     private function str_ends_with(string $haystackstring $needle): bool {
  175.         return $needle === '' || substr_compare($haystack$needle, -strlen($needle)) === 0;
  176.     }
  177. }