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

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.      * Adds a function to a template which is evaluated only once. This is helpful for
  176.      * lazy-evaluating data where we can use functions without arguments. Let's say
  177.      * you wanted to lazy-evaluate a variable like this:
  178.      *
  179.      *     $template->hasText = function () use ($article) {
  180.      *         return ContentModel::countPublishedByPidAndTable($article->id, 'tl_news') > 0;
  181.      *     };
  182.      *
  183.      * This would cause a query everytime $template->hasText is accessed in the
  184.      * template. You can improve this by turning it into this:
  185.      *
  186.      *     $template->hasText = Template::once(function () use ($article) {
  187.      *         return ContentModel::countPublishedByPidAndTable($article->id, 'tl_news') > 0;
  188.      *     });
  189.      */
  190.     public static function once(callable $callback)
  191.     {
  192.         $result null;
  193.         return static function () use (&$callback, &$result)
  194.         {
  195.             if ($callback !== null)
  196.             {
  197.                 $result $callback();
  198.                 $callback null;
  199.             }
  200.             return $result;
  201.         };
  202.     }
  203.     /**
  204.      * Set the template data from an array
  205.      *
  206.      * @param array $arrData The data array
  207.      */
  208.     public function setData($arrData)
  209.     {
  210.         $this->arrData $arrData;
  211.     }
  212.     /**
  213.      * Return the template data as array
  214.      *
  215.      * @return array The data array
  216.      */
  217.     public function getData()
  218.     {
  219.         return $this->arrData;
  220.     }
  221.     /**
  222.      * Set the template name
  223.      *
  224.      * @param string $strTemplate The template name
  225.      */
  226.     public function setName($strTemplate)
  227.     {
  228.         $this->strTemplate $strTemplate;
  229.     }
  230.     /**
  231.      * Return the template name
  232.      *
  233.      * @return string The template name
  234.      */
  235.     public function getName()
  236.     {
  237.         return $this->strTemplate;
  238.     }
  239.     /**
  240.      * Set the output format
  241.      *
  242.      * @param string $strFormat The output format
  243.      */
  244.     public function setFormat($strFormat)
  245.     {
  246.         $this->strFormat $strFormat;
  247.     }
  248.     /**
  249.      * Return the output format
  250.      *
  251.      * @return string The output format
  252.      */
  253.     public function getFormat()
  254.     {
  255.         return $this->strFormat;
  256.     }
  257.     /**
  258.      * Print all template variables to the screen using print_r
  259.      *
  260.      * @deprecated Deprecated since Contao 4.3, to be removed in Contao 5.
  261.      *             Use Template::dumpTemplateVars() instead.
  262.      */
  263.     public function showTemplateVars()
  264.     {
  265.         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.');
  266.         $this->dumpTemplateVars();
  267.     }
  268.     /**
  269.      * Print all template variables to the screen using the Symfony VarDumper component
  270.      */
  271.     public function dumpTemplateVars()
  272.     {
  273.         VarDumper::dump($this->arrData);
  274.     }
  275.     /**
  276.      * Parse the template file and return it as string
  277.      *
  278.      * @return string The template markup
  279.      */
  280.     public function parse()
  281.     {
  282.         if (!$this->strTemplate)
  283.         {
  284.             return '';
  285.         }
  286.         // HOOK: add custom parse filters
  287.         if (isset($GLOBALS['TL_HOOKS']['parseTemplate']) && \is_array($GLOBALS['TL_HOOKS']['parseTemplate']))
  288.         {
  289.             foreach ($GLOBALS['TL_HOOKS']['parseTemplate'] as $callback)
  290.             {
  291.                 $this->import($callback[0]);
  292.                 $this->{$callback[0]}->{$callback[1]}($this);
  293.             }
  294.         }
  295.         return $this->inherit();
  296.     }
  297.     /**
  298.      * Parse the template file and print it to the screen
  299.      *
  300.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  301.      *             Use Template::getResponse() instead.
  302.      */
  303.     public function output()
  304.     {
  305.         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.');
  306.         $this->compile();
  307.         header('Content-Type: ' $this->strContentType '; charset=' System::getContainer()->getParameter('kernel.charset'));
  308.         echo $this->strBuffer;
  309.     }
  310.     /**
  311.      * Return a response object
  312.      *
  313.      * @return Response The response object
  314.      */
  315.     public function getResponse()
  316.     {
  317.         $this->compile();
  318.         $response = new Response($this->strBuffer);
  319.         $response->headers->set('Content-Type'$this->strContentType);
  320.         $response->setCharset(System::getContainer()->getParameter('kernel.charset'));
  321.         return $response;
  322.     }
  323.     /**
  324.      * Return a route relative to the base URL
  325.      *
  326.      * @param string $strName   The route name
  327.      * @param array  $arrParams The route parameters
  328.      *
  329.      * @return string The route
  330.      */
  331.     public function route($strName$arrParams=array())
  332.     {
  333.         $strUrl System::getContainer()->get('router')->generate($strName$arrParams);
  334.         $strUrl substr($strUrl\strlen(Environment::get('path')) + 1);
  335.         return StringUtil::ampersand($strUrl);
  336.     }
  337.     /**
  338.      * Return the preview route
  339.      *
  340.      * @param string $strName   The route name
  341.      * @param array  $arrParams The route parameters
  342.      *
  343.      * @return string The route
  344.      */
  345.     public function previewRoute($strName$arrParams=array())
  346.     {
  347.         $container System::getContainer();
  348.         if (!$previewScript $container->getParameter('contao.preview_script'))
  349.         {
  350.             return $this->route($strName$arrParams);
  351.         }
  352.         $router $container->get('router');
  353.         $context $router->getContext();
  354.         $context->setBaseUrl($previewScript);
  355.         $strUrl $router->generate($strName$arrParams);
  356.         $strUrl substr($strUrl\strlen(Environment::get('path')) + 1);
  357.         $context->setBaseUrl('');
  358.         return StringUtil::ampersand($strUrl);
  359.     }
  360.     /**
  361.      * Returns a translated message
  362.      *
  363.      * @param string $strId
  364.      * @param array  $arrParams
  365.      * @param string $strDomain
  366.      *
  367.      * @return string
  368.      */
  369.     public function trans($strId, array $arrParams=array(), $strDomain='contao_default')
  370.     {
  371.         return System::getContainer()->get('translator')->trans($strId$arrParams$strDomain);
  372.     }
  373.     /**
  374.      * Helper method to allow quick access in the Contao templates for safe raw (unencoded) output.
  375.      * It replaces (or optionally removes) Contao insert tags and removes all HTML.
  376.      *
  377.      * Be careful when using this. It must NOT be used within regular HTML when $value
  378.      * is uncontrolled user input. It's useful to ensure raw values within e.g. <code> examples
  379.      * or JSON-LD arrays.
  380.      */
  381.     public function rawPlainText(string $valuebool $removeInsertTags false): string
  382.     {
  383.         return System::getContainer()->get('contao.string.html_decoder')->inputEncodedToPlainText($value$removeInsertTags);
  384.     }
  385.     /**
  386.      * Helper method to allow quick access in the Contao templates for safe raw (unencoded) output.
  387.      *
  388.      * Compared to $this->rawPlainText() it adds new lines before and after block level HTML elements
  389.      * and only then removes the rest of the HTML tags.
  390.      *
  391.      * Be careful when using this. It must NOT be used within regular HTML when $value
  392.      * is uncontrolled user input. It's useful to ensure raw values within e.g. <code> examples
  393.      * or JSON-LD arrays.
  394.      */
  395.     public function rawHtmlToPlainText(string $valuebool $removeInsertTags false): string
  396.     {
  397.         return System::getContainer()->get('contao.string.html_decoder')->htmlToPlainText($value$removeInsertTags);
  398.     }
  399.     /**
  400.      * Adds schema.org JSON-LD data to the current response context
  401.      */
  402.     public function addSchemaOrg(array $jsonLd): void
  403.     {
  404.         $responseContext System::getContainer()->get('contao.routing.response_context_accessor')->getResponseContext();
  405.         if (!$responseContext || !$responseContext->has(JsonLdManager::class))
  406.         {
  407.             return;
  408.         }
  409.         /** @var JsonLdManager $jsonLdManager */
  410.         $jsonLdManager $responseContext->get(JsonLdManager::class);
  411.         $type $jsonLdManager->createSchemaOrgTypeFromArray($jsonLd);
  412.         $jsonLdManager
  413.             ->getGraphForSchema(JsonLdManager::SCHEMA_ORG)
  414.             ->set($type$jsonLd['identifier'] ?? Graph::IDENTIFIER_DEFAULT)
  415.         ;
  416.     }
  417.     /**
  418.      * Render a figure
  419.      *
  420.      * The provided configuration array is used to configure a FigureBuilder.
  421.      * If not explicitly set, the default template "image.html5" will be used
  422.      * to render the results. To use the core's default Twig template, pass
  423.      * "@ContaoCore/Image/Studio/figure.html.twig" as $template argument.
  424.      *
  425.      * @param int|string|FilesModel|ImageInterface       $from          Can be a FilesModel, an ImageInterface, a tl_files UUID/ID/path or a file system path
  426.      * @param int|string|array|PictureConfiguration|null $size          A picture size configuration or reference
  427.      * @param array<string, mixed>                       $configuration Configuration for the FigureBuilder
  428.      * @param string                                     $template      A Contao or Twig template
  429.      *
  430.      * @return string|null Returns null if the resource is invalid
  431.      */
  432.     public function figure($from$size$configuration = array(), $template 'image')
  433.     {
  434.         return System::getContainer()->get('contao.image.studio.figure_renderer')->render($from$size$configuration$template);
  435.     }
  436.     /**
  437.      * Returns an asset path
  438.      *
  439.      * @param string      $path
  440.      * @param string|null $packageName
  441.      *
  442.      * @return string
  443.      */
  444.     public function asset($path$packageName null)
  445.     {
  446.         $url System::getContainer()->get('assets.packages')->getUrl($path$packageName);
  447.         $basePath '/';
  448.         $request System::getContainer()->get('request_stack')->getMainRequest();
  449.         if ($request !== null)
  450.         {
  451.             $basePath $request->getBasePath() . '/';
  452.         }
  453.         if (=== strncmp($url$basePath\strlen($basePath)))
  454.         {
  455.             return substr($url\strlen($basePath));
  456.         }
  457.         // Contao paths are relative to the <base> tag, so remove leading slashes
  458.         return $url;
  459.     }
  460.     /**
  461.      * Returns an asset version
  462.      *
  463.      * @param string      $path
  464.      * @param string|null $packageName
  465.      *
  466.      * @return string
  467.      */
  468.     public function assetVersion($path$packageName null)
  469.     {
  470.         return System::getContainer()->get('assets.packages')->getVersion($path$packageName);
  471.     }
  472.     /**
  473.      * Returns a container parameter
  474.      *
  475.      * @param string $strKey
  476.      *
  477.      * @return mixed
  478.      */
  479.     public function param($strKey)
  480.     {
  481.         return System::getContainer()->getParameter($strKey);
  482.     }
  483.     /**
  484.      * Compile the template
  485.      *
  486.      * @internal Do not call this method in your code. It will be made private in Contao 5.0.
  487.      */
  488.     protected function compile()
  489.     {
  490.         if (!$this->strBuffer)
  491.         {
  492.             $this->strBuffer $this->parse();
  493.         }
  494.     }
  495.     /**
  496.      * Return the debug bar string
  497.      *
  498.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  499.      */
  500.     protected function getDebugBar()
  501.     {
  502.         trigger_deprecation('contao/core-bundle''4.0''Using "Contao\Template::getDebugBar()" has been deprecated and will no longer work in Contao 5.0.');
  503.     }
  504.     /**
  505.      * Minify the HTML markup preserving pre, script, style and textarea tags
  506.      *
  507.      * @param string $strHtml The HTML markup
  508.      *
  509.      * @return string The minified HTML markup
  510.      */
  511.     public function minifyHtml($strHtml)
  512.     {
  513.         if (System::getContainer()->getParameter('kernel.debug'))
  514.         {
  515.             return $strHtml;
  516.         }
  517.         // Split the markup based on the tags that shall be preserved
  518.         $arrChunks preg_split('@(</?pre[^>]*>)|(</?script[^>]*>)|(</?style[^>]*>)|( ?</?textarea[^>]*>)@i'$strHtml, -1PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
  519.         $strHtml '';
  520.         $blnPreserveNext false;
  521.         $blnOptimizeNext false;
  522.         $strType null;
  523.         // Check for valid JavaScript types (see #7927)
  524.         $isJavaScript = static function ($strChunk)
  525.         {
  526.             $typeMatch = array();
  527.             if (preg_match('/\stype\s*=\s*(?:(?J)(["\'])\s*(?<type>.*?)\s*\1|(?<type>[^\s>]+))/i'$strChunk$typeMatch) && !\in_array(strtolower($typeMatch['type']), static::$validJavaScriptTypes))
  528.             {
  529.                 return false;
  530.             }
  531.             if (preg_match('/\slanguage\s*=\s*(?:(?J)(["\'])\s*(?<type>.*?)\s*\1|(?<type>[^\s>]+))/i'$strChunk$typeMatch) && !\in_array('text/' strtolower($typeMatch['type']), static::$validJavaScriptTypes))
  532.             {
  533.                 return false;
  534.             }
  535.             return true;
  536.         };
  537.         // Recombine the markup
  538.         foreach ($arrChunks as $strChunk)
  539.         {
  540.             if (strncasecmp($strChunk'<pre'4) === || strncasecmp(ltrim($strChunk), '<textarea'9) === 0)
  541.             {
  542.                 $blnPreserveNext true;
  543.             }
  544.             elseif (strncasecmp($strChunk'<script'7) === 0)
  545.             {
  546.                 if ($isJavaScript($strChunk))
  547.                 {
  548.                     $blnOptimizeNext true;
  549.                     $strType 'js';
  550.                 }
  551.                 else
  552.                 {
  553.                     $blnPreserveNext true;
  554.                 }
  555.             }
  556.             elseif (strncasecmp($strChunk'<style'6) === 0)
  557.             {
  558.                 $blnOptimizeNext true;
  559.                 $strType 'css';
  560.             }
  561.             elseif ($blnPreserveNext)
  562.             {
  563.                 $blnPreserveNext false;
  564.             }
  565.             elseif ($blnOptimizeNext)
  566.             {
  567.                 $blnOptimizeNext false;
  568.                 // Minify inline scripts
  569.                 if ($strType == 'js')
  570.                 {
  571.                     $objMinify = new JS();
  572.                     $objMinify->add($strChunk);
  573.                     $strChunk $objMinify->minify();
  574.                 }
  575.                 elseif ($strType == 'css')
  576.                 {
  577.                     $objMinify = new CSS();
  578.                     $objMinify->add($strChunk);
  579.                     $strChunk $objMinify->minify();
  580.                 }
  581.             }
  582.             else
  583.             {
  584.                 // Remove line indentations and trailing spaces
  585.                 $strChunk str_replace("\r"''$strChunk);
  586.                 $strChunk preg_replace(array('/^[\t ]+/m''/[\t ]+$/m''/\n\n+/'), array(''''"\n"), $strChunk);
  587.             }
  588.             $strHtml .= $strChunk;
  589.         }
  590.         return trim($strHtml);
  591.     }
  592.     /**
  593.      * Generate the markup for a style sheet tag
  594.      *
  595.      * @param string $href  The script path
  596.      * @param string $media The media type string
  597.      * @param mixed  $mtime The file mtime
  598.      *
  599.      * @return string The markup string
  600.      */
  601.     public static function generateStyleTag($href$media=null$mtime=false)
  602.     {
  603.         // Add the filemtime if not given and not an external file
  604.         if ($mtime === null && !preg_match('@^https?://@'$href))
  605.         {
  606.             $container System::getContainer();
  607.             $projectDir $container->getParameter('kernel.project_dir');
  608.             if (file_exists($projectDir '/' $href))
  609.             {
  610.                 $mtime filemtime($projectDir '/' $href);
  611.             }
  612.             else
  613.             {
  614.                 $webDir StringUtil::stripRootDir($container->getParameter('contao.web_dir'));
  615.                 // Handle public bundle resources in the contao.web_dir folder
  616.                 if (file_exists($projectDir '/' $webDir '/' $href))
  617.                 {
  618.                     $mtime filemtime($projectDir '/' $webDir '/' $href);
  619.                 }
  620.             }
  621.         }
  622.         if ($mtime)
  623.         {
  624.             $href .= '?v=' substr(md5($mtime), 08);
  625.         }
  626.         return '<link rel="stylesheet" href="' $href '"' . (($media && $media != 'all') ? ' media="' $media '"' '') . '>';
  627.     }
  628.     /**
  629.      * Generate the markup for inline CSS code
  630.      *
  631.      * @param string $script The CSS code
  632.      *
  633.      * @return string The markup string
  634.      */
  635.     public static function generateInlineStyle($script)
  636.     {
  637.         return '<style>' $script '</style>';
  638.     }
  639.     /**
  640.      * Generate the markup for a JavaScript tag
  641.      *
  642.      * @param string      $src            The script path
  643.      * @param boolean     $async          True to add the async attribute
  644.      * @param mixed       $mtime          The file mtime
  645.      * @param string|null $hash           An optional integrity hash
  646.      * @param string|null $crossorigin    An optional crossorigin attribute
  647.      * @param string|null $referrerpolicy An optional referrerpolicy attribute
  648.      *
  649.      * @return string The markup string
  650.      */
  651.     public static function generateScriptTag($src$async=false$mtime=false$hash=null$crossorigin=null$referrerpolicy=null)
  652.     {
  653.         // Add the filemtime if not given and not an external file
  654.         if ($mtime === null && !preg_match('@^https?://@'$src))
  655.         {
  656.             $container System::getContainer();
  657.             $projectDir $container->getParameter('kernel.project_dir');
  658.             if (file_exists($projectDir '/' $src))
  659.             {
  660.                 $mtime filemtime($projectDir '/' $src);
  661.             }
  662.             else
  663.             {
  664.                 $webDir StringUtil::stripRootDir($container->getParameter('contao.web_dir'));
  665.                 // Handle public bundle resources in the contao.web_dir folder
  666.                 if (file_exists($projectDir '/' $webDir '/' $src))
  667.                 {
  668.                     $mtime filemtime($projectDir '/' $webDir '/' $src);
  669.                 }
  670.             }
  671.         }
  672.         if ($mtime)
  673.         {
  674.             $src .= '?v=' substr(md5($mtime), 08);
  675.         }
  676.         return '<script src="' $src '"' . ($async ' async' '') . ($hash ' integrity="' $hash '"' '') . ($crossorigin ' crossorigin="' $crossorigin '"' '') . ($referrerpolicy ' referrerpolicy="' $referrerpolicy '"' '') . '></script>';
  677.     }
  678.     /**
  679.      * Generate the markup for an inline JavaScript
  680.      *
  681.      * @param string $script The JavaScript code
  682.      *
  683.      * @return string The markup string
  684.      */
  685.     public static function generateInlineScript($script)
  686.     {
  687.         return '<script>' $script '</script>';
  688.     }
  689.     /**
  690.      * Generate the markup for an RSS feed tag
  691.      *
  692.      * @param string $href   The script path
  693.      * @param string $format The feed format
  694.      * @param string $title  The feed title
  695.      *
  696.      * @return string The markup string
  697.      */
  698.     public static function generateFeedTag($href$format$title)
  699.     {
  700.         return '<link type="application/' $format '+xml" rel="alternate" href="' $href '" title="' StringUtil::specialchars($title) . '">';
  701.     }
  702.     /**
  703.      * Flush the output buffers
  704.      *
  705.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  706.      */
  707.     public function flushAllData()
  708.     {
  709.         trigger_deprecation('contao/core-bundle''4.0''Using "Contao\Template::flushAllData()" has been deprecated and will no longer work in Contao 5.0.');
  710.         if (\function_exists('fastcgi_finish_request'))
  711.         {
  712.             fastcgi_finish_request();
  713.         }
  714.         elseif (\PHP_SAPI !== 'cli')
  715.         {
  716.             $status ob_get_status(true);
  717.             $level \count($status);
  718.             while ($level-- > && (!empty($status[$level]['del']) || (isset($status[$level]['flags']) && ($status[$level]['flags'] & PHP_OUTPUT_HANDLER_REMOVABLE) && ($status[$level]['flags'] & PHP_OUTPUT_HANDLER_FLUSHABLE))))
  719.             {
  720.                 ob_end_flush();
  721.             }
  722.             flush();
  723.         }
  724.     }
  725. }
  726. class_alias(Template::class, 'Template');