vendor/contao/core-bundle/src/Resources/contao/library/Contao/Input.php line 1024

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. /**
  11.  * Safely read the user input
  12.  *
  13.  * The class functions as an adapter for the global input arrays ($_GET, $_POST,
  14.  * $_COOKIE) and safely returns their values. To prevent XSS vulnerabilities,
  15.  * you should always use the class when reading user input.
  16.  *
  17.  * Usage:
  18.  *
  19.  *     if (Input::get('action') == 'register')
  20.  *     {
  21.  *         $username = Input::post('username');
  22.  *         $password = Input::post('password');
  23.  *     }
  24.  */
  25. class Input
  26. {
  27.     /**
  28.      * Object instance (Singleton)
  29.      * @var Input
  30.      */
  31.     protected static $objInstance;
  32.     /**
  33.      * Cache
  34.      * @var array
  35.      */
  36.     protected static $arrCache = array();
  37.     /**
  38.      * Unused $_GET parameters
  39.      * @var array
  40.      */
  41.     protected static $arrUnusedGet = array();
  42.     /**
  43.      * Magic quotes setting
  44.      * @var boolean
  45.      */
  46.     protected static $blnMagicQuotes false;
  47.     /**
  48.      * Clean the global GPC arrays
  49.      */
  50.     public static function initialize()
  51.     {
  52.         $_GET    = static::cleanKey($_GET);
  53.         $_POST   = static::cleanKey($_POST);
  54.         $_COOKIE = static::cleanKey($_COOKIE);
  55.     }
  56.     /**
  57.      * Return a $_GET variable
  58.      *
  59.      * @param string  $strKey            The variable name
  60.      * @param boolean $blnDecodeEntities If true, all entities will be decoded
  61.      * @param boolean $blnKeepUnused     If true, the parameter will not be marked as used (see #4277)
  62.      *
  63.      * @return mixed The cleaned variable value
  64.      */
  65.     public static function get($strKey$blnDecodeEntities=false$blnKeepUnused=false)
  66.     {
  67.         if (!isset($_GET[$strKey]))
  68.         {
  69.             return null;
  70.         }
  71.         $strCacheKey $blnDecodeEntities 'getDecoded' 'getEncoded';
  72.         if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  73.         {
  74.             $varValue $_GET[$strKey];
  75.             $varValue = static::decodeEntities($varValue);
  76.             $varValue = static::xssClean($varValuetrue);
  77.             $varValue = static::stripTags($varValue);
  78.             if (!$blnDecodeEntities)
  79.             {
  80.                 $varValue = static::encodeSpecialChars($varValue);
  81.             }
  82.             $varValue = static::encodeInsertTags($varValue);
  83.             static::$arrCache[$strCacheKey][$strKey] = $varValue;
  84.         }
  85.         // Mark the parameter as used (see #4277)
  86.         if (!$blnKeepUnused)
  87.         {
  88.             unset(static::$arrUnusedGet[$strKey]);
  89.         }
  90.         return static::$arrCache[$strCacheKey][$strKey];
  91.     }
  92.     /**
  93.      * Return a $_POST variable
  94.      *
  95.      * @param string  $strKey            The variable name
  96.      * @param boolean $blnDecodeEntities If true, all entities will be decoded
  97.      *
  98.      * @return mixed The cleaned variable value
  99.      */
  100.     public static function post($strKey$blnDecodeEntities=false)
  101.     {
  102.         $strCacheKey $blnDecodeEntities 'postDecoded' 'postEncoded';
  103.         if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  104.         {
  105.             $varValue = static::findPost($strKey);
  106.             if ($varValue === null)
  107.             {
  108.                 return null;
  109.             }
  110.             $varValue = static::decodeEntities($varValue);
  111.             $varValue = static::xssClean($varValuetrue);
  112.             $varValue = static::stripTags($varValue);
  113.             if (!$blnDecodeEntities)
  114.             {
  115.                 $varValue = static::encodeSpecialChars($varValue);
  116.             }
  117.             if (!\defined('TL_MODE') || TL_MODE != 'BE')
  118.             {
  119.                 $varValue = static::encodeInsertTags($varValue);
  120.             }
  121.             static::$arrCache[$strCacheKey][$strKey] = $varValue;
  122.         }
  123.         return static::$arrCache[$strCacheKey][$strKey];
  124.     }
  125.     /**
  126.      * Return a $_POST variable preserving allowed HTML tags
  127.      *
  128.      * @param string  $strKey            The variable name
  129.      * @param boolean $blnDecodeEntities If true, all entities will be decoded
  130.      *
  131.      * @return mixed The cleaned variable value
  132.      */
  133.     public static function postHtml($strKey$blnDecodeEntities=false)
  134.     {
  135.         $strCacheKey $blnDecodeEntities 'postHtmlDecoded' 'postHtmlEncoded';
  136.         if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  137.         {
  138.             $varValue = static::findPost($strKey);
  139.             if ($varValue === null)
  140.             {
  141.                 return null;
  142.             }
  143.             $varValue = static::decodeEntities($varValue);
  144.             $varValue = static::xssClean($varValue);
  145.             $varValue = static::stripTags($varValueConfig::get('allowedTags'), Config::get('allowedAttributes'));
  146.             if (!$blnDecodeEntities)
  147.             {
  148.                 $varValue = static::encodeSpecialChars($varValue);
  149.             }
  150.             if (!\defined('TL_MODE') || TL_MODE != 'BE')
  151.             {
  152.                 $varValue = static::encodeInsertTags($varValue);
  153.             }
  154.             static::$arrCache[$strCacheKey][$strKey] = $varValue;
  155.         }
  156.         return static::$arrCache[$strCacheKey][$strKey];
  157.     }
  158.     /**
  159.      * Return a raw, unsafe $_POST variable
  160.      *
  161.      * @param string $strKey The variable name
  162.      *
  163.      * @return mixed The raw variable value
  164.      */
  165.     public static function postRaw($strKey)
  166.     {
  167.         $strCacheKey 'postRaw';
  168.         if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  169.         {
  170.             $varValue = static::findPost($strKey);
  171.             if ($varValue === null)
  172.             {
  173.                 return null;
  174.             }
  175.             $varValue = static::preserveBasicEntities($varValue);
  176.             $varValue = static::xssClean($varValue);
  177.             if (!\defined('TL_MODE') || TL_MODE != 'BE')
  178.             {
  179.                 $varValue = static::encodeInsertTags($varValue);
  180.             }
  181.             static::$arrCache[$strCacheKey][$strKey] = $varValue;
  182.         }
  183.         return static::$arrCache[$strCacheKey][$strKey];
  184.     }
  185.     /**
  186.      * Return a raw, unsafe and unfiltered $_POST variable
  187.      *
  188.      * @param string $strKey The variable name
  189.      *
  190.      * @return mixed The raw variable value
  191.      */
  192.     public static function postUnsafeRaw($strKey)
  193.     {
  194.         $strCacheKey 'postUnsafeRaw';
  195.         if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  196.         {
  197.             $varValue = static::findPost($strKey);
  198.             if ($varValue === null)
  199.             {
  200.                 return null;
  201.             }
  202.             static::$arrCache[$strCacheKey][$strKey] = $varValue;
  203.         }
  204.         return static::$arrCache[$strCacheKey][$strKey];
  205.     }
  206.     /**
  207.      * Return a $_COOKIE variable
  208.      *
  209.      * @param string  $strKey            The variable name
  210.      * @param boolean $blnDecodeEntities If true, all entities will be decoded
  211.      *
  212.      * @return mixed The cleaned variable value
  213.      */
  214.     public static function cookie($strKey$blnDecodeEntities=false)
  215.     {
  216.         if (!isset($_COOKIE[$strKey]))
  217.         {
  218.             return null;
  219.         }
  220.         $strCacheKey $blnDecodeEntities 'cookieDecoded' 'cookieEncoded';
  221.         if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  222.         {
  223.             $varValue $_COOKIE[$strKey];
  224.             $varValue = static::decodeEntities($varValue);
  225.             $varValue = static::xssClean($varValuetrue);
  226.             $varValue = static::stripTags($varValue);
  227.             if (!$blnDecodeEntities)
  228.             {
  229.                 $varValue = static::encodeSpecialChars($varValue);
  230.             }
  231.             $varValue = static::encodeInsertTags($varValue);
  232.             static::$arrCache[$strCacheKey][$strKey] = $varValue;
  233.         }
  234.         return static::$arrCache[$strCacheKey][$strKey];
  235.     }
  236.     /**
  237.      * Set a $_GET variable
  238.      *
  239.      * @param string  $strKey       The variable name
  240.      * @param mixed   $varValue     The variable value
  241.      * @param boolean $blnAddUnused If true, the value usage will be checked
  242.      */
  243.     public static function setGet($strKey$varValue$blnAddUnused=false)
  244.     {
  245.         // Convert special characters (see #7829)
  246.         $strKey str_replace(array(' ''.''['), '_'$strKey);
  247.         $strKey = static::cleanKey($strKey);
  248.         unset(static::$arrCache['getEncoded'][$strKey], static::$arrCache['getDecoded'][$strKey]);
  249.         if ($varValue === null)
  250.         {
  251.             unset($_GET[$strKey]);
  252.         }
  253.         else
  254.         {
  255.             $_GET[$strKey] = $varValue;
  256.             if ($blnAddUnused)
  257.             {
  258.                 static::setUnusedGet($strKey$varValue); // see #4277
  259.             }
  260.         }
  261.     }
  262.     /**
  263.      * Set a $_POST variable
  264.      *
  265.      * @param string $strKey   The variable name
  266.      * @param mixed  $varValue The variable value
  267.      */
  268.     public static function setPost($strKey$varValue)
  269.     {
  270.         $strKey = static::cleanKey($strKey);
  271.         unset(
  272.             static::$arrCache['postEncoded'][$strKey],
  273.             static::$arrCache['postDecoded'][$strKey],
  274.             static::$arrCache['postHtmlEncoded'][$strKey],
  275.             static::$arrCache['postHtmlDecoded'][$strKey],
  276.             static::$arrCache['postRaw'][$strKey],
  277.             static::$arrCache['postUnsafeRaw'][$strKey]
  278.         );
  279.         if ($varValue === null)
  280.         {
  281.             unset($_POST[$strKey]);
  282.         }
  283.         else
  284.         {
  285.             $_POST[$strKey] = $varValue;
  286.         }
  287.     }
  288.     /**
  289.      * Set a $_COOKIE variable
  290.      *
  291.      * @param string $strKey   The variable name
  292.      * @param mixed  $varValue The variable value
  293.      */
  294.     public static function setCookie($strKey$varValue)
  295.     {
  296.         $strKey = static::cleanKey($strKey);
  297.         unset(static::$arrCache['cookieEncoded'][$strKey], static::$arrCache['cookieDecoded'][$strKey]);
  298.         if ($varValue === null)
  299.         {
  300.             unset($_COOKIE[$strKey]);
  301.         }
  302.         else
  303.         {
  304.             $_COOKIE[$strKey] = $varValue;
  305.         }
  306.     }
  307.     /**
  308.      * Reset the internal cache
  309.      */
  310.     public static function resetCache()
  311.     {
  312.         static::$arrCache = array();
  313.     }
  314.     /**
  315.      * Return whether there are unused GET parameters
  316.      *
  317.      * @return boolean True if there are unused GET parameters
  318.      */
  319.     public static function hasUnusedGet()
  320.     {
  321.         return \count(static::$arrUnusedGet) > 0;
  322.     }
  323.     /**
  324.      * Return the unused GET parameters as array
  325.      *
  326.      * @return array The unused GET parameter array
  327.      */
  328.     public static function getUnusedGet()
  329.     {
  330.         return array_keys(static::$arrUnusedGet);
  331.     }
  332.     /**
  333.      * Set an unused GET parameter
  334.      *
  335.      * @param string $strKey   The array key
  336.      * @param mixed  $varValue The array value
  337.      */
  338.     public static function setUnusedGet($strKey$varValue)
  339.     {
  340.         static::$arrUnusedGet[$strKey] = $varValue;
  341.     }
  342.     /**
  343.      * Reset the unused GET parameters
  344.      */
  345.     public static function resetUnusedGet()
  346.     {
  347.         static::$arrUnusedGet = array();
  348.     }
  349.     /**
  350.      * Sanitize the variable names (thanks to Andreas Schempp)
  351.      *
  352.      * @param mixed $varValue A variable name or an array of variable names
  353.      *
  354.      * @return mixed The clean name or array of names
  355.      */
  356.     public static function cleanKey($varValue)
  357.     {
  358.         // Recursively clean arrays
  359.         if (\is_array($varValue))
  360.         {
  361.             $return = array();
  362.             foreach ($varValue as $k=>$v)
  363.             {
  364.                 $k = static::cleanKey($k);
  365.                 if (\is_array($v))
  366.                 {
  367.                     $v = static::cleanKey($v);
  368.                 }
  369.                 $return[$k] = $v;
  370.             }
  371.             return $return;
  372.         }
  373.         $varValue = static::decodeEntities($varValue);
  374.         $varValue = static::xssClean($varValuetrue);
  375.         $varValue = static::stripTags($varValue);
  376.         return $varValue;
  377.     }
  378.     /**
  379.      * Strip slashes
  380.      *
  381.      * @param mixed $varValue A string or array
  382.      *
  383.      * @return mixed The string or array without slashes
  384.      *
  385.      * @deprecated Deprecated since Contao 3.5, to be removed in Contao 5.
  386.      *             Since get_magic_quotes_gpc() always returns false in PHP 5.4+, the method was never actually executed.
  387.      */
  388.     public static function stripSlashes($varValue)
  389.     {
  390.         return $varValue;
  391.     }
  392.     /**
  393.      * Strip HTML and PHP tags preserving HTML comments
  394.      *
  395.      * @param mixed  $varValue             A string or array
  396.      * @param string $strAllowedTags       A string of tags to preserve
  397.      * @param string $strAllowedAttributes A serialized string of attributes to preserve
  398.      *
  399.      * @return mixed The cleaned string or array
  400.      */
  401.     public static function stripTags($varValue$strAllowedTags=''$strAllowedAttributes='')
  402.     {
  403.         if ($strAllowedTags !== '' && \func_num_args() < 3)
  404.         {
  405.             trigger_deprecation('contao/core-bundle''4.4''Using %s() with $strAllowedTags but without $strAllowedAttributes has been deprecated and will no longer work in Contao 5.0.'__METHOD__);
  406.             $strAllowedAttributes Config::get('allowedAttributes');
  407.         }
  408.         if (!$varValue)
  409.         {
  410.             return $varValue;
  411.         }
  412.         // Recursively clean arrays
  413.         if (\is_array($varValue))
  414.         {
  415.             foreach ($varValue as $k=>$v)
  416.             {
  417.                 $varValue[$k] = static::stripTags($v$strAllowedTags$strAllowedAttributes);
  418.             }
  419.             return $varValue;
  420.         }
  421.         $arrAllowedAttributes = array();
  422.         foreach (StringUtil::deserialize($strAllowedAttributestrue) as $arrRow)
  423.         {
  424.             if (!empty($arrRow['key']) && !empty($arrRow['value']))
  425.             {
  426.                 $arrAllowedAttributes[trim($arrRow['key'])] = StringUtil::trimsplit(','$arrRow['value']);
  427.             }
  428.         }
  429.         // Encode opening arrow brackets (see #3998)
  430.         $varValue preg_replace_callback(
  431.             '@</?([^\s<>/]*)@',
  432.             static function ($matches) use ($strAllowedTags)
  433.             {
  434.                 if (!$matches[1] || stripos($strAllowedTags'<' strtolower($matches[1]) . '>') === false)
  435.                 {
  436.                     $matches[0] = str_replace('<''&lt;'$matches[0]);
  437.                 }
  438.                 return $matches[0];
  439.             },
  440.             $varValue
  441.         );
  442.         // Strip the tags
  443.         $varValue strip_tags($varValue$strAllowedTags);
  444.         // Restore HTML comments and recheck for encoded null bytes
  445.         $varValue str_replace(array('&lt;!--''&lt;![''\\0'), array('<!--''<![''&#92;0'), $varValue);
  446.         // Strip attributes
  447.         if ($strAllowedTags)
  448.         {
  449.             $varValue self::stripAttributes($varValue$strAllowedTags$arrAllowedAttributes);
  450.         }
  451.         return $varValue;
  452.     }
  453.     /**
  454.      * Strip HTML attributes and normalize them to lowercase and double quotes
  455.      *
  456.      * @param string $strHtml
  457.      * @param string $strAllowedTags
  458.      * @param array  $arrAllowedAttributes
  459.      *
  460.      * @return string
  461.      */
  462.     private static function stripAttributes($strHtml$strAllowedTags$arrAllowedAttributes)
  463.     {
  464.         // Skip if all attributes are allowed on all tags
  465.         if (\in_array('*'$arrAllowedAttributes['*'] ?? array(), true))
  466.         {
  467.             return $strHtml;
  468.         }
  469.         $blnCommentOpen false;
  470.         $strOpenRawtext null;
  471.         // Match every single starting and closing tag or special characters outside of tags
  472.         return preg_replace_callback(
  473.             '@</?([^\s<>/]*)([^<>]*)>?|-->|[>"\'=]+@',
  474.             static function ($matches) use ($strAllowedTags$arrAllowedAttributes, &$blnCommentOpen, &$strOpenRawtext)
  475.             {
  476.                 $strTagName strtolower($matches[1] ?? '');
  477.                 if ($strOpenRawtext === $strTagName && '/' === $matches[0][1])
  478.                 {
  479.                     $strOpenRawtext null;
  480.                     return '</' $strTagName '>';
  481.                 }
  482.                 if (null !== $strOpenRawtext)
  483.                 {
  484.                     return $matches[0];
  485.                 }
  486.                 if ($blnCommentOpen && substr($matches[0], -3) === '-->')
  487.                 {
  488.                     $blnCommentOpen false;
  489.                     return static::encodeSpecialChars(substr($matches[0], 0, -3)) . '-->';
  490.                 }
  491.                 if (!$blnCommentOpen && === strncmp($matches[0], '<!--'4))
  492.                 {
  493.                     if (substr($matches[0], -3) === '-->')
  494.                     {
  495.                         return '<!--' . static::encodeSpecialChars(substr($matches[0], 4, -3)) . '-->';
  496.                     }
  497.                     $blnCommentOpen true;
  498.                     return '<!--' . static::encodeSpecialChars(substr($matches[0], 4));
  499.                 }
  500.                 // Matched special characters or tag is invalid or not allowed, return everything encoded
  501.                 if ($strTagName == '' || stripos($strAllowedTags'<' $strTagName '>') === false)
  502.                 {
  503.                     return static::encodeSpecialChars($matches[0]);
  504.                 }
  505.                 // Closing tags have no attributes
  506.                 if ('/' === substr($matches[0], 11))
  507.                 {
  508.                     return '</' $strTagName '>';
  509.                 }
  510.                 // Special parsing for RCDATA and RAWTEXT elements https://html.spec.whatwg.org/#rcdata-state
  511.                 if (!$blnCommentOpen && \in_array($strTagName, array('script''title''textarea''style''xmp''iframe''noembed''noframes''noscript')))
  512.                 {
  513.                     $strOpenRawtext $strTagName;
  514.                 }
  515.                 $arrAttributes self::getAttributesFromTag($matches[2]);
  516.                 // Only keep allowed attributes
  517.                 $arrAttributes array_filter(
  518.                     $arrAttributes,
  519.                     static function ($strAttribute) use ($strTagName$arrAllowedAttributes)
  520.                     {
  521.                         // Skip if all attributes are allowed
  522.                         if (\in_array('*'$arrAllowedAttributes[$strTagName] ?? array(), true))
  523.                         {
  524.                             return true;
  525.                         }
  526.                         $arrCandidates = array($strAttribute);
  527.                         // Check for wildcard attributes like data-*
  528.                         if (false !== $intDashPosition strpos($strAttribute'-'))
  529.                         {
  530.                             $arrCandidates[] = substr($strAttribute0$intDashPosition 1) . '*';
  531.                         }
  532.                         foreach ($arrCandidates as $strCandidate)
  533.                         {
  534.                             if (
  535.                                 \in_array($strCandidate$arrAllowedAttributes['*'] ?? array(), true)
  536.                                 || \in_array($strCandidate$arrAllowedAttributes[$strTagName] ?? array(), true)
  537.                             ) {
  538.                                 return true;
  539.                             }
  540.                         }
  541.                         return false;
  542.                     },
  543.                     ARRAY_FILTER_USE_KEY
  544.                 );
  545.                 // Build the tag in its normalized form
  546.                 $strReturn '<' $strTagName;
  547.                 foreach ($arrAttributes as $strAttributeName => $strAttributeValue)
  548.                 {
  549.                     // The value was already encoded by the getAttributesFromTag() method
  550.                     $strReturn .= ' ' $strAttributeName '="' $strAttributeValue '"';
  551.                 }
  552.                 $strReturn .= '>';
  553.                 return $strReturn;
  554.             },
  555.             $strHtml
  556.         );
  557.     }
  558.     /**
  559.      * Get the attributes as key/value pairs with the values already encoded for HTML
  560.      *
  561.      * @param string $strAttributes
  562.      *
  563.      * @return array
  564.      */
  565.     private static function getAttributesFromTag($strAttributes)
  566.     {
  567.         // Match every attribute name value pair
  568.         if (!preg_match_all('@\s+([a-z][a-z0-9_:-]*)(?:\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]*))?@i'$strAttributes$matchesPREG_SET_ORDER))
  569.         {
  570.             return array();
  571.         }
  572.         $arrAttributes = array();
  573.         foreach ($matches as $arrMatch)
  574.         {
  575.             $strAttribute strtolower($arrMatch[1]);
  576.             // Skip attributes that end with dashes or use a double dash
  577.             if (substr($strAttribute, -1) === '-' || false !== strpos($strAttribute'--'))
  578.             {
  579.                 continue;
  580.             }
  581.             // Default to empty string for the value
  582.             $strValue $arrMatch[2] ?? '';
  583.             // Remove the quotes if matched by the regular expression
  584.             if (
  585.                 (strpos($strValue'"') === && substr($strValue, -1) === '"')
  586.                 || (strpos($strValue"'") === && substr($strValue, -1) === "'")
  587.             ) {
  588.                 $strValue substr($strValue1, -1);
  589.             }
  590.             // Encode all special characters and insert tags that are not encoded yet
  591.             if (=== preg_match('((?:^|:)(?:src|srcset|href|action|formaction|codebase|cite|background|longdesc|profile|usemap|classid|data|icon|manifest|poster|archive)$)'$strAttribute))
  592.             {
  593.                 $strValue StringUtil::specialcharsUrl($strValue);
  594.             }
  595.             else
  596.             {
  597.                 $strValue StringUtil::specialcharsAttribute($strValue);
  598.             }
  599.             $arrAttributes[$strAttribute] = $strValue;
  600.         }
  601.         return $arrAttributes;
  602.     }
  603.     /**
  604.      * Clean a value and try to prevent XSS attacks
  605.      *
  606.      * @param mixed   $varValue      A string or array
  607.      * @param boolean $blnStrictMode If true, the function removes also JavaScript event handlers
  608.      *
  609.      * @return mixed The cleaned string or array
  610.      */
  611.     public static function xssClean($varValue$blnStrictMode=false)
  612.     {
  613.         if (!$varValue)
  614.         {
  615.             return $varValue;
  616.         }
  617.         // Recursively clean arrays
  618.         if (\is_array($varValue))
  619.         {
  620.             foreach ($varValue as $k=>$v)
  621.             {
  622.                 $varValue[$k] = static::xssClean($v);
  623.             }
  624.             return $varValue;
  625.         }
  626.         // Return if the value is not a string
  627.         if (\is_bool($varValue) || is_numeric($varValue))
  628.         {
  629.             return $varValue;
  630.         }
  631.         // Validate standard character entities and UTF16 two byte encoding
  632.         $varValue preg_replace('/(&#*\w+)[\x00-\x20]+;/i''$1;'$varValue);
  633.         // Remove carriage returns
  634.         $varValue preg_replace('/\r+/'''$varValue);
  635.         // Replace unicode entities
  636.         $varValue preg_replace_callback('~&#x([0-9a-f]+);~i', static function ($matches) { return mb_chr(hexdec($matches[1])); }, $varValue);
  637.         $varValue preg_replace_callback('~&#([0-9]+);~', static function ($matches) { return mb_chr($matches[1]); }, $varValue);
  638.         // Remove null bytes
  639.         $varValue str_replace(array(\chr(0), '\\0'), array('''&#92;0'), $varValue);
  640.         // Define a list of keywords
  641.         $arrKeywords = array
  642.         (
  643.             '/\bj\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\b/is'// javascript
  644.             '/\bv\s*b\s*s\s*c\s*r\s*i\s*p\s*t\b/is'// vbscript
  645.             '/\bv\s*b\s*s\s*c\s*r\s*p\s*t\b/is'// vbscrpt
  646.             '/\bs\s*c\s*r\s*i\s*p\s*t\b/is'//script
  647.             '/\ba\s*p\s*p\s*l\s*e\s*t\b/is'// applet
  648.             '/\ba\s*l\s*e\s*r\s*t\b/is'// alert
  649.             '/\bd\s*o\s*c\s*u\s*m\s*e\s*n\s*t\b/is'// document
  650.             '/\bw\s*r\s*i\s*t\s*e\b/is'// write
  651.             '/\bc\s*o\s*o\s*k\s*i\s*e\b/is'// cookie
  652.             '/\bw\s*i\s*n\s*d\s*o\s*w\b/is' // window
  653.         );
  654.         // Compact exploded keywords like "j a v a s c r i p t"
  655.         foreach ($arrKeywords as $strKeyword)
  656.         {
  657.             $arrMatches = array();
  658.             preg_match_all($strKeyword$varValue$arrMatches);
  659.             foreach ($arrMatches[0] as $strMatch)
  660.             {
  661.                 $varValue str_replace($strMatchpreg_replace('/\s*/'''$strMatch), $varValue);
  662.             }
  663.         }
  664.         $arrRegexp[] = '/<(a|img)[^>]*[^a-z](<script|<xss)[^>]*>/is';
  665.         $arrRegexp[] = '/<(a|img)[^>]*[^a-z]document\.cookie[^>]*>/is';
  666.         $arrRegexp[] = '/<(a|img)[^>]*[^a-z]vbscri?pt\s*:[^>]*>/is';
  667.         $arrRegexp[] = '/<(a|img)[^>]*[^a-z]expression\s*\([^>]*>/is';
  668.         // Also remove event handlers and JavaScript in strict mode
  669.         if ($blnStrictMode)
  670.         {
  671.             $arrRegexp[] = '/vbscri?pt\s*:/is';
  672.             $arrRegexp[] = '/javascript\s*:/is';
  673.             $arrRegexp[] = '/<\s*embed.*swf/is';
  674.             $arrRegexp[] = '/<(a|img)[^>]*[^a-z]alert\s*\([^>]*>/is';
  675.             $arrRegexp[] = '/<(a|img)[^>]*[^a-z]javascript\s*:[^>]*>/is';
  676.             $arrRegexp[] = '/<(a|img)[^>]*[^a-z]window\.[^>]*>/is';
  677.             $arrRegexp[] = '/<(a|img)[^>]*[^a-z]document\.[^>]*>/is';
  678.             $arrRegexp[] = '/<[^>]*[^a-z]onabort\s*=[^>]*>/is';
  679.             $arrRegexp[] = '/<[^>]*[^a-z]onblur\s*=[^>]*>/is';
  680.             $arrRegexp[] = '/<[^>]*[^a-z]onchange\s*=[^>]*>/is';
  681.             $arrRegexp[] = '/<[^>]*[^a-z]onclick\s*=[^>]*>/is';
  682.             $arrRegexp[] = '/<[^>]*[^a-z]onerror\s*=[^>]*>/is';
  683.             $arrRegexp[] = '/<[^>]*[^a-z]onfocus\s*=[^>]*>/is';
  684.             $arrRegexp[] = '/<[^>]*[^a-z]onkeypress\s*=[^>]*>/is';
  685.             $arrRegexp[] = '/<[^>]*[^a-z]onkeydown\s*=[^>]*>/is';
  686.             $arrRegexp[] = '/<[^>]*[^a-z]onkeyup\s*=[^>]*>/is';
  687.             $arrRegexp[] = '/<[^>]*[^a-z]onload\s*=[^>]*>/is';
  688.             $arrRegexp[] = '/<[^>]*[^a-z]onmouseover\s*=[^>]*>/is';
  689.             $arrRegexp[] = '/<[^>]*[^a-z]onmouseup\s*=[^>]*>/is';
  690.             $arrRegexp[] = '/<[^>]*[^a-z]onmousedown\s*=[^>]*>/is';
  691.             $arrRegexp[] = '/<[^>]*[^a-z]onmouseout\s*=[^>]*>/is';
  692.             $arrRegexp[] = '/<[^>]*[^a-z]onreset\s*=[^>]*>/is';
  693.             $arrRegexp[] = '/<[^>]*[^a-z]onselect\s*=[^>]*>/is';
  694.             $arrRegexp[] = '/<[^>]*[^a-z]onsubmit\s*=[^>]*>/is';
  695.             $arrRegexp[] = '/<[^>]*[^a-z]onunload\s*=[^>]*>/is';
  696.             $arrRegexp[] = '/<[^>]*[^a-z]onresize\s*=[^>]*>/is';
  697.         }
  698.         $varValue preg_replace($arrRegexp''$varValue);
  699.         // Recheck for encoded null bytes
  700.         $varValue str_replace('\\0''&#92;0'$varValue);
  701.         return $varValue;
  702.     }
  703.     /**
  704.      * Decode HTML entities
  705.      *
  706.      * @param mixed $varValue A string or array
  707.      *
  708.      * @return mixed The decoded string or array
  709.      */
  710.     public static function decodeEntities($varValue)
  711.     {
  712.         if (!$varValue)
  713.         {
  714.             return $varValue;
  715.         }
  716.         // Recursively clean arrays
  717.         if (\is_array($varValue))
  718.         {
  719.             foreach ($varValue as $k=>$v)
  720.             {
  721.                 $varValue[$k] = static::decodeEntities($v);
  722.             }
  723.             return $varValue;
  724.         }
  725.         // Preserve basic entities
  726.         $varValue = static::preserveBasicEntities($varValue);
  727.         $varValue html_entity_decode($varValueENT_QUOTESSystem::getContainer()->getParameter('kernel.charset'));
  728.         return $varValue;
  729.     }
  730.     /**
  731.      * Preserve basic entities by replacing them with square brackets (e.g. &amp; becomes [amp])
  732.      *
  733.      * @param mixed $varValue A string or array
  734.      *
  735.      * @return mixed The string or array with the converted entities
  736.      */
  737.     public static function preserveBasicEntities($varValue)
  738.     {
  739.         if (!$varValue)
  740.         {
  741.             return $varValue;
  742.         }
  743.         // Recursively clean arrays
  744.         if (\is_array($varValue))
  745.         {
  746.             foreach ($varValue as $k=>$v)
  747.             {
  748.                 $varValue[$k] = static::preserveBasicEntities($v);
  749.             }
  750.             return $varValue;
  751.         }
  752.         $varValue str_replace
  753.         (
  754.             array('[&amp;]''&amp;''[&lt;]''&lt;''[&gt;]''&gt;''[&nbsp;]''&nbsp;''[&shy;]''&shy;'),
  755.             array('[&]''[&]''[lt]''[lt]''[gt]''[gt]''[nbsp]''[nbsp]''[-]''[-]'),
  756.             $varValue
  757.         );
  758.         return $varValue;
  759.     }
  760.     /**
  761.      * Encode special characters which are potentially dangerous
  762.      *
  763.      * @param mixed $varValue A string or array
  764.      *
  765.      * @return mixed The encoded string or array
  766.      */
  767.     public static function encodeSpecialChars($varValue)
  768.     {
  769.         if (!$varValue)
  770.         {
  771.             return $varValue;
  772.         }
  773.         // Recursively clean arrays
  774.         if (\is_array($varValue))
  775.         {
  776.             foreach ($varValue as $k=>$v)
  777.             {
  778.                 $varValue[$k] = static::encodeSpecialChars($v);
  779.             }
  780.             return $varValue;
  781.         }
  782.         $arrSearch = array(
  783.             '#''<''>''('')''\\''=''"'"'",
  784.             // Revert double encoded #
  785.             '&&#35;35;''&&#35;60;''&&#35;62;''&&#35;40;''&&#35;41;''&&#35;92;''&&#35;61;''&&#35;34;''&&#35;39;',
  786.         );
  787.         $arrReplace = array(
  788.             '&#35;''&#60;''&#62;''&#40;''&#41;''&#92;''&#61;''&#34;''&#39;',
  789.             '&#35;''&#60;''&#62;''&#40;''&#41;''&#92;''&#61;''&#34;''&#39;',
  790.         );
  791.         return str_replace($arrSearch$arrReplace$varValue);
  792.     }
  793.     /**
  794.      * Encode the opening and closing delimiters of insert tags
  795.      *
  796.      * @param string|array $varValue The input string
  797.      *
  798.      * @return string|array The encoded input string
  799.      */
  800.     public static function encodeInsertTags($varValue)
  801.     {
  802.         // Recursively encode insert tags
  803.         if (\is_array($varValue))
  804.         {
  805.             foreach ($varValue as $k=>$v)
  806.             {
  807.                 $varValue[$k] = static::encodeInsertTags($v);
  808.             }
  809.             return $varValue;
  810.         }
  811.         return str_replace(array('{{''}}'), array('&#123;&#123;''&#125;&#125;'), (string) $varValue);
  812.     }
  813.     /**
  814.      * Fallback to the session form data if there is no post data
  815.      *
  816.      * @param string $strKey The variable name
  817.      *
  818.      * @return mixed The variable value
  819.      */
  820.     public static function findPost($strKey)
  821.     {
  822.         if (isset($_POST[$strKey]))
  823.         {
  824.             return $_POST[$strKey];
  825.         }
  826.         // Do not check for $request->hasPreviousSession() and early return here (see #3971)
  827.         if (isset($_SESSION['FORM_DATA'][$strKey]))
  828.         {
  829.             return ($strKey == 'FORM_SUBMIT') ? preg_replace('/^auto_/i'''$_SESSION['FORM_DATA'][$strKey]) : $_SESSION['FORM_DATA'][$strKey];
  830.         }
  831.         return null;
  832.     }
  833.     /**
  834.      * Clean the keys of the request arrays
  835.      *
  836.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  837.      *             The Input class is now static.
  838.      */
  839.     protected function __construct()
  840.     {
  841.         static::initialize();
  842.     }
  843.     /**
  844.      * Prevent cloning of the object (Singleton)
  845.      *
  846.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  847.      *             The Input class is now static.
  848.      */
  849.     final public function __clone()
  850.     {
  851.     }
  852.     /**
  853.      * Return the object instance (Singleton)
  854.      *
  855.      * @return Input The object instance
  856.      *
  857.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  858.      *             The Input class is now static.
  859.      */
  860.     public static function getInstance()
  861.     {
  862.         trigger_deprecation('contao/core-bundle''4.0''Using "Contao\Input::getInstance()" has been deprecated and will no longer work in Contao 5.0. The "Contao\Input" class is now static.');
  863.         if (static::$objInstance === null)
  864.         {
  865.             static::$objInstance = new static();
  866.         }
  867.         return static::$objInstance;
  868.     }
  869. }
  870. class_alias(Input::class, 'Input');