vendor/contao/core-bundle/src/Resources/contao/library/Contao/Template.php line 278

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Contao.
  4.  *
  5.  * (c) Leo Feyer
  6.  *
  7.  * @license LGPL-3.0-or-later
  8.  */
  9. namespace Contao;
  10. use Contao\CoreBundle\Routing\ResponseContext\JsonLd\JsonLdManager;
  11. use Contao\Image\ImageInterface;
  12. use Contao\Image\PictureConfiguration;
  13. use MatthiasMullie\Minify\CSS;
  14. use MatthiasMullie\Minify\JS;
  15. use Spatie\SchemaOrg\Graph;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\VarDumper\VarDumper;
  18. /**
  19.  * Parses and outputs template files
  20.  *
  21.  * The class supports loading template files, adding variables to them and then
  22.  * printing them to the screen. It functions as abstract parent class for the
  23.  * two core classes "BackendTemplate" and "FrontendTemplate".
  24.  *
  25.  * Usage:
  26.  *
  27.  *     $template = new BackendTemplate();
  28.  *     $template->name = 'Leo Feyer';
  29.  *     $template->output();
  30.  *
  31.  * @property string       $style
  32.  * @property array|string $cssID
  33.  * @property string       $class
  34.  * @property string       $inColumn
  35.  * @property string       $headline
  36.  * @property array        $hl
  37.  * @property string       $content
  38.  * @property string       $action
  39.  * @property string       $enforceTwoFactor
  40.  * @property string       $targetPath
  41.  * @property string       $message
  42.  * @property string       $href
  43.  * @property string       $twoFactor
  44.  * @property string       $explain
  45.  * @property string       $active
  46.  * @property string       $enableButton
  47.  * @property string       $disableButton
  48.  * @property boolean      $enable
  49.  * @property boolean      $isEnabled
  50.  * @property string       $secret
  51.  * @property string       $textCode
  52.  * @property string       $qrCode
  53.  * @property string       $scan
  54.  * @property string       $verify
  55.  * @property string       $verifyHelp
  56.  * @property boolean      $showBackupCodes
  57.  * @property array        $backupCodes
  58.  * @property boolean      $trustedDevicesEnabled
  59.  * @property array        $trustedDevices
  60.  * @property string       $currentDevice
  61.  */
  62. abstract class Template extends Controller
  63. {
  64.     use TemplateInheritance;
  65.     /**
  66.      * Output buffer
  67.      * @var string
  68.      */
  69.     protected $strBuffer;
  70.     /**
  71.      * Content type
  72.      * @var string
  73.      */
  74.     protected $strContentType;
  75.     /**
  76.      * Template data
  77.      * @var array
  78.      */
  79.     protected $arrData = array();
  80.     /**
  81.      * Valid JavaScipt types
  82.      * @var array
  83.      * @see http://www.w3.org/TR/html5/scripting-1.html#scriptingLanguages
  84.      */
  85.     protected static $validJavaScriptTypes = array
  86.     (
  87.         'application/ecmascript',
  88.         'application/javascript',
  89.         'application/x-ecmascript',
  90.         'application/x-javascript',
  91.         'text/ecmascript',
  92.         'text/javascript',
  93.         'text/javascript1.0',
  94.         'text/javascript1.1',
  95.         'text/javascript1.2',
  96.         'text/javascript1.3',
  97.         'text/javascript1.4',
  98.         'text/javascript1.5',
  99.         'text/jscript',
  100.         'text/livescript',
  101.         'text/x-ecmascript',
  102.         'text/x-javascript',
  103.     );
  104.     /**
  105.      * Create a new template object
  106.      *
  107.      * @param string $strTemplate    The template name
  108.      * @param string $strContentType The content type (defaults to "text/html")
  109.      */
  110.     public function __construct($strTemplate=''$strContentType='text/html')
  111.     {
  112.         parent::__construct();
  113.         $this->strTemplate $strTemplate;
  114.         $this->strContentType $strContentType;
  115.     }
  116.     /**
  117.      * Set an object property
  118.      *
  119.      * @param string $strKey   The property name
  120.      * @param mixed  $varValue The property value
  121.      */
  122.     public function __set($strKey$varValue)
  123.     {
  124.         $this->arrData[$strKey] = $varValue;
  125.     }
  126.     /**
  127.      * Return an object property
  128.      *
  129.      * @param string $strKey The property name
  130.      *
  131.      * @return mixed The property value
  132.      */
  133.     public function __get($strKey)
  134.     {
  135.         if (isset($this->arrData[$strKey]))
  136.         {
  137.             if (\is_object($this->arrData[$strKey]) && \is_callable($this->arrData[$strKey]))
  138.             {
  139.                 return $this->arrData[$strKey]();
  140.             }
  141.             return $this->arrData[$strKey];
  142.         }
  143.         return parent::__get($strKey);
  144.     }
  145.     /**
  146.      * Execute a callable and return the result
  147.      *
  148.      * @param string $strKey    The name of the key
  149.      * @param array  $arrParams The parameters array
  150.      *
  151.      * @return mixed The callable return value
  152.      *
  153.      * @throws \InvalidArgumentException If the callable does not exist
  154.      */
  155.     public function __call($strKey$arrParams)
  156.     {
  157.         if (!isset($this->arrData[$strKey]) || !\is_callable($this->arrData[$strKey]))
  158.         {
  159.             throw new \InvalidArgumentException("$strKey is not set or not a callable");
  160.         }
  161.         return ($this->arrData[$strKey])(...$arrParams);
  162.     }
  163.     /**
  164.      * Check whether a property is set
  165.      *
  166.      * @param string $strKey The property name
  167.      *
  168.      * @return boolean True if the property is set
  169.      */
  170.     public function __isset($strKey)
  171.     {
  172.         return isset($this->arrData[$strKey]);
  173.     }
  174.     /**
  175.      * Set the template data from an array
  176.      *
  177.      * @param array $arrData The data array
  178.      */
  179.     public function setData($arrData)
  180.     {
  181.         $this->arrData $arrData;
  182.     }
  183.     /**
  184.      * Return the template data as array
  185.      *
  186.      * @return array The data array
  187.      */
  188.     public function getData()
  189.     {
  190.         return $this->arrData;
  191.     }
  192.     /**
  193.      * Set the template name
  194.      *
  195.      * @param string $strTemplate The template name
  196.      */
  197.     public function setName($strTemplate)
  198.     {
  199.         $this->strTemplate $strTemplate;
  200.     }
  201.     /**
  202.      * Return the template name
  203.      *
  204.      * @return string The template name
  205.      */
  206.     public function getName()
  207.     {
  208.         return $this->strTemplate;
  209.     }
  210.     /**
  211.      * Set the output format
  212.      *
  213.      * @param string $strFormat The output format
  214.      */
  215.     public function setFormat($strFormat)
  216.     {
  217.         $this->strFormat $strFormat;
  218.     }
  219.     /**
  220.      * Return the output format
  221.      *
  222.      * @return string The output format
  223.      */
  224.     public function getFormat()
  225.     {
  226.         return $this->strFormat;
  227.     }
  228.     /**
  229.      * Print all template variables to the screen using print_r
  230.      *
  231.      * @deprecated Deprecated since Contao 4.3, to be removed in Contao 5.
  232.      *             Use Template::dumpTemplateVars() instead.
  233.      */
  234.     public function showTemplateVars()
  235.     {
  236.         trigger_deprecation('contao/core-bundle''4.0''Using "Contao\Template::showTemplateVars()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Template::dumpTemplateVars()" instead.');
  237.         $this->dumpTemplateVars();
  238.     }
  239.     /**
  240.      * Print all template variables to the screen using the Symfony VarDumper component
  241.      */
  242.     public function dumpTemplateVars()
  243.     {
  244.         VarDumper::dump($this->arrData);
  245.     }
  246.     /**
  247.      * Parse the template file and return it as string
  248.      *
  249.      * @return string The template markup
  250.      */
  251.     public function parse()
  252.     {
  253.         if (!$this->strTemplate)
  254.         {
  255.             return '';
  256.         }
  257.         // HOOK: add custom parse filters
  258.         if (isset($GLOBALS['TL_HOOKS']['parseTemplate']) && \is_array($GLOBALS['TL_HOOKS']['parseTemplate']))
  259.         {
  260.             foreach ($GLOBALS['TL_HOOKS']['parseTemplate'] as $callback)
  261.             {
  262.                 $this->import($callback[0]);
  263.                 $this->{$callback[0]}->{$callback[1]}($this);
  264.             }
  265.         }
  266.         return $this->inherit();
  267.     }
  268.     /**
  269.      * Parse the template file and print it to the screen
  270.      *
  271.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  272.      *             Use Template::getResponse() instead.
  273.      */
  274.     public function output()
  275.     {
  276.         trigger_deprecation('contao/core-bundle''4.0''Using "Contao\Template::output()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Template::getResponse()" instead.');
  277.         $this->compile();
  278.         header('Content-Type: ' $this->strContentType '; charset=' System::getContainer()->getParameter('kernel.charset'));
  279.         echo $this->strBuffer;
  280.     }
  281.     /**
  282.      * Return a response object
  283.      *
  284.      * @return Response The response object
  285.      */
  286.     public function getResponse()
  287.     {
  288.         $this->compile();
  289.         $response = new Response($this->strBuffer);
  290.         $response->headers->set('Content-Type'$this->strContentType);
  291.         $response->setCharset(System::getContainer()->getParameter('kernel.charset'));
  292.         return $response;
  293.     }
  294.     /**
  295.      * Return a route relative to the base URL
  296.      *
  297.      * @param string $strName   The route name
  298.      * @param array  $arrParams The route parameters
  299.      *
  300.      * @return string The route
  301.      */
  302.     public function route($strName$arrParams=array())
  303.     {
  304.         $strUrl System::getContainer()->get('router')->generate($strName$arrParams);
  305.         $strUrl substr($strUrl, \strlen(Environment::get('path')) + 1);
  306.         return StringUtil::ampersand($strUrl);
  307.     }
  308.     /**
  309.      * Return the preview route
  310.      *
  311.      * @param string $strName   The route name
  312.      * @param array  $arrParams The route parameters
  313.      *
  314.      * @return string The route
  315.      */
  316.     public function previewRoute($strName$arrParams=array())
  317.     {
  318.         $container System::getContainer();
  319.         if (!$previewScript $container->getParameter('contao.preview_script'))
  320.         {
  321.             return $this->route($strName$arrParams);
  322.         }
  323.         $router $container->get('router');
  324.         $context $router->getContext();
  325.         $context->setBaseUrl($previewScript);
  326.         $strUrl $router->generate($strName$arrParams);
  327.         $strUrl substr($strUrl, \strlen(Environment::get('path')) + 1);
  328.         $context->setBaseUrl('');
  329.         return StringUtil::ampersand($strUrl);
  330.     }
  331.     /**
  332.      * Returns a translated message
  333.      *
  334.      * @param string $strId
  335.      * @param array  $arrParams
  336.      * @param string $strDomain
  337.      *
  338.      * @return string
  339.      */
  340.     public function trans($strId, array $arrParams=array(), $strDomain='contao_default')
  341.     {
  342.         return System::getContainer()->get('translator')->trans($strId$arrParams$strDomain);
  343.     }
  344.     /**
  345.      * Helper method to allow quick access in the Contao templates for safe raw (unencoded) output.
  346.      * It replaces (or optionally removes) Contao insert tags and removes all HTML.
  347.      *
  348.      * Be careful when using this. It must NOT be used within regular HTML when $value
  349.      * is uncontrolled user input. It's useful to ensure raw values within e.g. <code> examples
  350.      * or JSON-LD arrays.
  351.      */
  352.     public function rawPlainText(string $valuebool $removeInsertTags false): string
  353.     {
  354.         return System::getContainer()->get('contao.string.html_decoder')->inputEncodedToPlainText($value$removeInsertTags);
  355.     }
  356.     /**
  357.      * Helper method to allow quick access in the Contao templates for safe raw (unencoded) output.
  358.      *
  359.      * Compared to $this->rawPlainText() it adds new lines before and after block level HTML elements
  360.      * and only then removes the rest of the HTML tags.
  361.      *
  362.      * Be careful when using this. It must NOT be used within regular HTML when $value
  363.      * is uncontrolled user input. It's useful to ensure raw values within e.g. <code> examples
  364.      * or JSON-LD arrays.
  365.      */
  366.     public function rawHtmlToPlainText(string $valuebool $removeInsertTags false): string
  367.     {
  368.         return System::getContainer()->get('contao.string.html_decoder')->htmlToPlainText($value$removeInsertTags);
  369.     }
  370.     /**
  371.      * Adds schema.org JSON-LD data to the current response context
  372.      */
  373.     public function addSchemaOrg(array $jsonLd): void
  374.     {
  375.         $responseContext System::getContainer()->get('contao.routing.response_context_accessor')->getResponseContext();
  376.         if (!$responseContext || !$responseContext->has(JsonLdManager::class))
  377.         {
  378.             return;
  379.         }
  380.         /** @var JsonLdManager $jsonLdManager */
  381.         $jsonLdManager $responseContext->get(JsonLdManager::class);
  382.         $type $jsonLdManager->createSchemaOrgTypeFromArray($jsonLd);
  383.         $jsonLdManager
  384.             ->getGraphForSchema(JsonLdManager::SCHEMA_ORG)
  385.             ->set($type$jsonLd['identifier'] ?? Graph::IDENTIFIER_DEFAULT)
  386.         ;
  387.     }
  388.     /**
  389.      * Render a figure
  390.      *
  391.      * The provided configuration array is used to configure a FigureBuilder.
  392.      * If not explicitly set, the default template "image.html5" will be used
  393.      * to render the results. To use the core's default Twig template, pass
  394.      * "@ContaoCore/Image/Studio/figure.html.twig" as $template argument.
  395.      *
  396.      * @param int|string|FilesModel|ImageInterface       $from          Can be a FilesModel, an ImageInterface, a tl_files UUID/ID/path or a file system path
  397.      * @param int|string|array|PictureConfiguration|null $size          A picture size configuration or reference
  398.      * @param array<string, mixed>                       $configuration Configuration for the FigureBuilder
  399.      * @param string                                     $template      A Contao or Twig template
  400.      *
  401.      * @return string|null Returns null if the resource is invalid
  402.      */
  403.     public function figure($from$size$configuration = array(), $template 'image')
  404.     {
  405.         return System::getContainer()->get('contao.image.studio.figure_renderer')->render($from$size$configuration$template);
  406.     }
  407.     /**
  408.      * Returns an asset path
  409.      *
  410.      * @param string      $path
  411.      * @param string|null $packageName
  412.      *
  413.      * @return string
  414.      */
  415.     public function asset($path$packageName null)
  416.     {
  417.         $url System::getContainer()->get('assets.packages')->getUrl($path$packageName);
  418.         $basePath '/';
  419.         $request System::getContainer()->get('request_stack')->getMainRequest();
  420.         if ($request !== null)
  421.         {
  422.             $basePath $request->getBasePath() . '/';
  423.         }
  424.         if (=== strncmp($url$basePath, \strlen($basePath)))
  425.         {
  426.             return substr($url, \strlen($basePath));
  427.         }
  428.         // Contao paths are relative to the <base> tag, so remove leading slashes
  429.         return $url;
  430.     }
  431.     /**
  432.      * Returns a container parameter
  433.      *
  434.      * @param string $strKey
  435.      *
  436.      * @return mixed
  437.      */
  438.     public function param($strKey)
  439.     {
  440.         return System::getContainer()->getParameter($strKey);
  441.     }
  442.     /**
  443.      * Compile the template
  444.      *
  445.      * @internal Do not call this method in your code. It will be made private in Contao 5.0.
  446.      */
  447.     protected function compile()
  448.     {
  449.         if (!$this->strBuffer)
  450.         {
  451.             $this->strBuffer $this->parse();
  452.         }
  453.     }
  454.     /**
  455.      * Return the debug bar string
  456.      *
  457.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  458.      */
  459.     protected function getDebugBar()
  460.     {
  461.         trigger_deprecation('contao/core-bundle''4.0''Using "Contao\Template::getDebugBar()" has been deprecated and will no longer work in Contao 5.0.');
  462.     }
  463.     /**
  464.      * Minify the HTML markup preserving pre, script, style and textarea tags
  465.      *
  466.      * @param string $strHtml The HTML markup
  467.      *
  468.      * @return string The minified HTML markup
  469.      */
  470.     public function minifyHtml($strHtml)
  471.     {
  472.         if (System::getContainer()->getParameter('kernel.debug'))
  473.         {
  474.             return $strHtml;
  475.         }
  476.         // Split the markup based on the tags that shall be preserved
  477.         $arrChunks preg_split('@(</?pre[^>]*>)|(</?script[^>]*>)|(</?style[^>]*>)|( ?</?textarea[^>]*>)@i'$strHtml, -1PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
  478.         $strHtml '';
  479.         $blnPreserveNext false;
  480.         $blnOptimizeNext false;
  481.         $strType null;
  482.         // Check for valid JavaScript types (see #7927)
  483.         $isJavaScript = static function ($strChunk)
  484.         {
  485.             $typeMatch = array();
  486.             if (preg_match('/\stype\s*=\s*(?:(?J)(["\'])\s*(?<type>.*?)\s*\1|(?<type>[^\s>]+))/i'$strChunk$typeMatch) && !\in_array(strtolower($typeMatch['type']), static::$validJavaScriptTypes))
  487.             {
  488.                 return false;
  489.             }
  490.             if (preg_match('/\slanguage\s*=\s*(?:(?J)(["\'])\s*(?<type>.*?)\s*\1|(?<type>[^\s>]+))/i'$strChunk$typeMatch) && !\in_array('text/' strtolower($typeMatch['type']), static::$validJavaScriptTypes))
  491.             {
  492.                 return false;
  493.             }
  494.             return true;
  495.         };
  496.         // Recombine the markup
  497.         foreach ($arrChunks as $strChunk)
  498.         {
  499.             if (strncasecmp($strChunk'<pre'4) === || strncasecmp(ltrim($strChunk), '<textarea'9) === 0)
  500.             {
  501.                 $blnPreserveNext true;
  502.             }
  503.             elseif (strncasecmp($strChunk'<script'7) === 0)
  504.             {
  505.                 if ($isJavaScript($strChunk))
  506.                 {
  507.                     $blnOptimizeNext true;
  508.                     $strType 'js';
  509.                 }
  510.                 else
  511.                 {
  512.                     $blnPreserveNext true;
  513.                 }
  514.             }
  515.             elseif (strncasecmp($strChunk'<style'6) === 0)
  516.             {
  517.                 $blnOptimizeNext true;
  518.                 $strType 'css';
  519.             }
  520.             elseif ($blnPreserveNext)
  521.             {
  522.                 $blnPreserveNext false;
  523.             }
  524.             elseif ($blnOptimizeNext)
  525.             {
  526.                 $blnOptimizeNext false;
  527.                 // Minify inline scripts
  528.                 if ($strType == 'js')
  529.                 {
  530.                     $objMinify = new JS();
  531.                     $objMinify->add($strChunk);
  532.                     $strChunk $objMinify->minify();
  533.                 }
  534.                 elseif ($strType == 'css')
  535.                 {
  536.                     $objMinify = new CSS();
  537.                     $objMinify->add($strChunk);
  538.                     $strChunk $objMinify->minify();
  539.                 }
  540.             }
  541.             else
  542.             {
  543.                 // Remove line indentations and trailing spaces
  544.                 $strChunk str_replace("\r"''$strChunk);
  545.                 $strChunk preg_replace(array('/^[\t ]+/m''/[\t ]+$/m''/\n\n+/'), array(''''"\n"), $strChunk);
  546.             }
  547.             $strHtml .= $strChunk;
  548.         }
  549.         return trim($strHtml);
  550.     }
  551.     /**
  552.      * Generate the markup for a style sheet tag
  553.      *
  554.      * @param string $href  The script path
  555.      * @param string $media The media type string
  556.      * @param mixed  $mtime The file mtime
  557.      *
  558.      * @return string The markup string
  559.      */
  560.     public static function generateStyleTag($href$media=null$mtime=false)
  561.     {
  562.         // Add the filemtime if not given and not an external file
  563.         if ($mtime === null && !preg_match('@^https?://@'$href))
  564.         {
  565.             $container System::getContainer();
  566.             $projectDir $container->getParameter('kernel.project_dir');
  567.             if (file_exists($projectDir '/' $href))
  568.             {
  569.                 $mtime filemtime($projectDir '/' $href);
  570.             }
  571.             else
  572.             {
  573.                 $webDir StringUtil::stripRootDir($container->getParameter('contao.web_dir'));
  574.                 // Handle public bundle resources in the contao.web_dir folder
  575.                 if (file_exists($projectDir '/' $webDir '/' $href))
  576.                 {
  577.                     $mtime filemtime($projectDir '/' $webDir '/' $href);
  578.                 }
  579.             }
  580.         }
  581.         if ($mtime)
  582.         {
  583.             $href .= '?v=' substr(md5($mtime), 08);
  584.         }
  585.         return '<link rel="stylesheet" href="' $href '"' . (($media && $media != 'all') ? ' media="' $media '"' '') . '>';
  586.     }
  587.     /**
  588.      * Generate the markup for inline CSS code
  589.      *
  590.      * @param string $script The CSS code
  591.      *
  592.      * @return string The markup string
  593.      */
  594.     public static function generateInlineStyle($script)
  595.     {
  596.         return '<style>' $script '</style>';
  597.     }
  598.     /**
  599.      * Generate the markup for a JavaScript tag
  600.      *
  601.      * @param string      $src            The script path
  602.      * @param boolean     $async          True to add the async attribute
  603.      * @param mixed       $mtime          The file mtime
  604.      * @param string|null $hash           An optional integrity hash
  605.      * @param string|null $crossorigin    An optional crossorigin attribute
  606.      * @param string|null $referrerpolicy An optional referrerpolicy attribute
  607.      *
  608.      * @return string The markup string
  609.      */
  610.     public static function generateScriptTag($src$async=false$mtime=false$hash=null$crossorigin=null$referrerpolicy=null)
  611.     {
  612.         // Add the filemtime if not given and not an external file
  613.         if ($mtime === null && !preg_match('@^https?://@'$src))
  614.         {
  615.             $container System::getContainer();
  616.             $projectDir $container->getParameter('kernel.project_dir');
  617.             if (file_exists($projectDir '/' $src))
  618.             {
  619.                 $mtime filemtime($projectDir '/' $src);
  620.             }
  621.             else
  622.             {
  623.                 $webDir StringUtil::stripRootDir($container->getParameter('contao.web_dir'));
  624.                 // Handle public bundle resources in the contao.web_dir folder
  625.                 if (file_exists($projectDir '/' $webDir '/' $src))
  626.                 {
  627.                     $mtime filemtime($projectDir '/' $webDir '/' $src);
  628.                 }
  629.             }
  630.         }
  631.         if ($mtime)
  632.         {
  633.             $src .= '?v=' substr(md5($mtime), 08);
  634.         }
  635.         return '<script src="' $src '"' . ($async ' async' '') . ($hash ' integrity="' $hash '"' '') . ($crossorigin ' crossorigin="' $crossorigin '"' '') . ($referrerpolicy ' referrerpolicy="' $referrerpolicy '"' '') . '></script>';
  636.     }
  637.     /**
  638.      * Generate the markup for an inline JavaScript
  639.      *
  640.      * @param string $script The JavaScript code
  641.      *
  642.      * @return string The markup string
  643.      */
  644.     public static function generateInlineScript($script)
  645.     {
  646.         return '<script>' $script '</script>';
  647.     }
  648.     /**
  649.      * Generate the markup for an RSS feed tag
  650.      *
  651.      * @param string $href   The script path
  652.      * @param string $format The feed format
  653.      * @param string $title  The feed title
  654.      *
  655.      * @return string The markup string
  656.      */
  657.     public static function generateFeedTag($href$format$title)
  658.     {
  659.         return '<link type="application/' $format '+xml" rel="alternate" href="' $href '" title="' StringUtil::specialchars($title) . '">';
  660.     }
  661.     /**
  662.      * Flush the output buffers
  663.      *
  664.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  665.      */
  666.     public function flushAllData()
  667.     {
  668.         trigger_deprecation('contao/core-bundle''4.0''Using "Contao\Template::flushAllData()" has been deprecated and will no longer work in Contao 5.0.');
  669.         if (\function_exists('fastcgi_finish_request'))
  670.         {
  671.             fastcgi_finish_request();
  672.         }
  673.         elseif (\PHP_SAPI !== 'cli')
  674.         {
  675.             $status ob_get_status(true);
  676.             $level = \count($status);
  677.             while ($level-- > && (!empty($status[$level]['del']) || (isset($status[$level]['flags']) && ($status[$level]['flags'] & PHP_OUTPUT_HANDLER_REMOVABLE) && ($status[$level]['flags'] & PHP_OUTPUT_HANDLER_FLUSHABLE))))
  678.             {
  679.                 ob_end_flush();
  680.             }
  681.             flush();
  682.         }
  683.     }
  684. }
  685. class_alias(Template::class, 'Template');