Exception
2 years ago
File
1 year ago
RateLimiter
2 years ago
Session
1 year ago
Test
1 year ago
AcceptHeader.php
1 year ago
AcceptHeaderItem.php
2 years ago
BinaryFileResponse.php
1 year ago
Cookie.php
1 year ago
ExpressionRequestMatcher.php
2 years ago
FileBag.php
1 year ago
HeaderBag.php
1 year ago
HeaderUtils.php
1 year ago
InputBag.php
2 years ago
IpUtils.php
1 year ago
JsonResponse.php
1 year ago
LICENSE
2 years ago
ParameterBag.php
1 year ago
README.md
2 years ago
RedirectResponse.php
2 years ago
Request.php
1 year ago
RequestMatcher.php
1 year ago
RequestMatcherInterface.php
2 years ago
RequestStack.php
2 years ago
Response.php
1 year ago
ResponseHeaderBag.php
1 year ago
ServerBag.php
1 year ago
StreamedResponse.php
1 year ago
UrlHelper.php
2 years ago
HeaderUtils.php
258 lines
| 1 | <?php |
| 2 | |
| 3 | /* |
| 4 | * This file is part of the Symfony package. |
| 5 | * |
| 6 | * (c) Fabien Potencier <fabien@symfony.com> |
| 7 | * |
| 8 | * For the full copyright and license information, please view the LICENSE |
| 9 | * file that was distributed with this source code. |
| 10 | */ |
| 11 | namespace Matomo\Dependencies\Symfony\Component\HttpFoundation; |
| 12 | |
| 13 | /** |
| 14 | * HTTP header utility functions. |
| 15 | * |
| 16 | * @author Christian Schmidt <github@chsc.dk> |
| 17 | */ |
| 18 | class HeaderUtils |
| 19 | { |
| 20 | public const DISPOSITION_ATTACHMENT = 'attachment'; |
| 21 | public const DISPOSITION_INLINE = 'inline'; |
| 22 | /** |
| 23 | * This class should not be instantiated. |
| 24 | */ |
| 25 | private function __construct() |
| 26 | { |
| 27 | } |
| 28 | /** |
| 29 | * Splits an HTTP header by one or more separators. |
| 30 | * |
| 31 | * Example: |
| 32 | * |
| 33 | * HeaderUtils::split('da, en-gb;q=0.8', ',;') |
| 34 | * // => ['da'], ['en-gb', 'q=0.8']] |
| 35 | * |
| 36 | * @param string $separators List of characters to split on, ordered by |
| 37 | * precedence, e.g. ',', ';=', or ',;=' |
| 38 | * |
| 39 | * @return array Nested array with as many levels as there are characters in |
| 40 | * $separators |
| 41 | */ |
| 42 | public static function split(string $header, string $separators) : array |
| 43 | { |
| 44 | if ('' === $separators) { |
| 45 | throw new \InvalidArgumentException('At least one separator must be specified.'); |
| 46 | } |
| 47 | $quotedSeparators = preg_quote($separators, '/'); |
| 48 | preg_match_all(' |
| 49 | / |
| 50 | (?!\\s) |
| 51 | (?: |
| 52 | # quoted-string |
| 53 | "(?:[^"\\\\]|\\\\.)*(?:"|\\\\|$) |
| 54 | | |
| 55 | # token |
| 56 | [^"' . $quotedSeparators . ']+ |
| 57 | )+ |
| 58 | (?<!\\s) |
| 59 | | |
| 60 | # separator |
| 61 | \\s* |
| 62 | (?<separator>[' . $quotedSeparators . ']) |
| 63 | \\s* |
| 64 | /x', trim($header), $matches, \PREG_SET_ORDER); |
| 65 | return self::groupParts($matches, $separators); |
| 66 | } |
| 67 | /** |
| 68 | * Combines an array of arrays into one associative array. |
| 69 | * |
| 70 | * Each of the nested arrays should have one or two elements. The first |
| 71 | * value will be used as the keys in the associative array, and the second |
| 72 | * will be used as the values, or true if the nested array only contains one |
| 73 | * element. Array keys are lowercased. |
| 74 | * |
| 75 | * Example: |
| 76 | * |
| 77 | * HeaderUtils::combine([['foo', 'abc'], ['bar']]) |
| 78 | * // => ['foo' => 'abc', 'bar' => true] |
| 79 | */ |
| 80 | public static function combine(array $parts) : array |
| 81 | { |
| 82 | $assoc = []; |
| 83 | foreach ($parts as $part) { |
| 84 | $name = strtolower($part[0]); |
| 85 | $value = $part[1] ?? \true; |
| 86 | $assoc[$name] = $value; |
| 87 | } |
| 88 | return $assoc; |
| 89 | } |
| 90 | /** |
| 91 | * Joins an associative array into a string for use in an HTTP header. |
| 92 | * |
| 93 | * The key and value of each entry are joined with '=', and all entries |
| 94 | * are joined with the specified separator and an additional space (for |
| 95 | * readability). Values are quoted if necessary. |
| 96 | * |
| 97 | * Example: |
| 98 | * |
| 99 | * HeaderUtils::toString(['foo' => 'abc', 'bar' => true, 'baz' => 'a b c'], ',') |
| 100 | * // => 'foo=abc, bar, baz="a b c"' |
| 101 | */ |
| 102 | public static function toString(array $assoc, string $separator) : string |
| 103 | { |
| 104 | $parts = []; |
| 105 | foreach ($assoc as $name => $value) { |
| 106 | if (\true === $value) { |
| 107 | $parts[] = $name; |
| 108 | } else { |
| 109 | $parts[] = $name . '=' . self::quote($value); |
| 110 | } |
| 111 | } |
| 112 | return implode($separator . ' ', $parts); |
| 113 | } |
| 114 | /** |
| 115 | * Encodes a string as a quoted string, if necessary. |
| 116 | * |
| 117 | * If a string contains characters not allowed by the "token" construct in |
| 118 | * the HTTP specification, it is backslash-escaped and enclosed in quotes |
| 119 | * to match the "quoted-string" construct. |
| 120 | */ |
| 121 | public static function quote(string $s) : string |
| 122 | { |
| 123 | if (preg_match('/^[a-z0-9!#$%&\'*.^_`|~-]+$/i', $s)) { |
| 124 | return $s; |
| 125 | } |
| 126 | return '"' . addcslashes($s, '"\\"') . '"'; |
| 127 | } |
| 128 | /** |
| 129 | * Decodes a quoted string. |
| 130 | * |
| 131 | * If passed an unquoted string that matches the "token" construct (as |
| 132 | * defined in the HTTP specification), it is passed through verbatim. |
| 133 | */ |
| 134 | public static function unquote(string $s) : string |
| 135 | { |
| 136 | return preg_replace('/\\\\(.)|"/', '$1', $s); |
| 137 | } |
| 138 | /** |
| 139 | * Generates an HTTP Content-Disposition field-value. |
| 140 | * |
| 141 | * @param string $disposition One of "inline" or "attachment" |
| 142 | * @param string $filename A unicode string |
| 143 | * @param string $filenameFallback A string containing only ASCII characters that |
| 144 | * is semantically equivalent to $filename. If the filename is already ASCII, |
| 145 | * it can be omitted, or just copied from $filename |
| 146 | * |
| 147 | * @throws \InvalidArgumentException |
| 148 | * |
| 149 | * @see RFC 6266 |
| 150 | */ |
| 151 | public static function makeDisposition(string $disposition, string $filename, string $filenameFallback = '') : string |
| 152 | { |
| 153 | if (!\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE])) { |
| 154 | throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE)); |
| 155 | } |
| 156 | if ('' === $filenameFallback) { |
| 157 | $filenameFallback = $filename; |
| 158 | } |
| 159 | // filenameFallback is not ASCII. |
| 160 | if (!preg_match('/^[\\x20-\\x7e]*$/', $filenameFallback)) { |
| 161 | throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.'); |
| 162 | } |
| 163 | // percent characters aren't safe in fallback. |
| 164 | if (str_contains($filenameFallback, '%')) { |
| 165 | throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.'); |
| 166 | } |
| 167 | // path separators aren't allowed in either. |
| 168 | if (str_contains($filename, '/') || str_contains($filename, '\\') || str_contains($filenameFallback, '/') || str_contains($filenameFallback, '\\')) { |
| 169 | throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.'); |
| 170 | } |
| 171 | $params = ['filename' => $filenameFallback]; |
| 172 | if ($filename !== $filenameFallback) { |
| 173 | $params['filename*'] = "utf-8''" . rawurlencode($filename); |
| 174 | } |
| 175 | return $disposition . '; ' . self::toString($params, ';'); |
| 176 | } |
| 177 | /** |
| 178 | * Like parse_str(), but preserves dots in variable names. |
| 179 | */ |
| 180 | public static function parseQuery(string $query, bool $ignoreBrackets = \false, string $separator = '&') : array |
| 181 | { |
| 182 | $q = []; |
| 183 | foreach (explode($separator, $query) as $v) { |
| 184 | if (\false !== ($i = strpos($v, "\x00"))) { |
| 185 | $v = substr($v, 0, $i); |
| 186 | } |
| 187 | if (\false === ($i = strpos($v, '='))) { |
| 188 | $k = urldecode($v); |
| 189 | $v = ''; |
| 190 | } else { |
| 191 | $k = urldecode(substr($v, 0, $i)); |
| 192 | $v = substr($v, $i); |
| 193 | } |
| 194 | if (\false !== ($i = strpos($k, "\x00"))) { |
| 195 | $k = substr($k, 0, $i); |
| 196 | } |
| 197 | $k = ltrim($k, ' '); |
| 198 | if ($ignoreBrackets) { |
| 199 | $q[$k][] = urldecode(substr($v, 1)); |
| 200 | continue; |
| 201 | } |
| 202 | if (\false === ($i = strpos($k, '['))) { |
| 203 | $q[] = bin2hex($k) . $v; |
| 204 | } else { |
| 205 | $q[] = bin2hex(substr($k, 0, $i)) . rawurlencode(substr($k, $i)) . $v; |
| 206 | } |
| 207 | } |
| 208 | if ($ignoreBrackets) { |
| 209 | return $q; |
| 210 | } |
| 211 | parse_str(implode('&', $q), $q); |
| 212 | $query = []; |
| 213 | foreach ($q as $k => $v) { |
| 214 | if (\false !== ($i = strpos($k, '_'))) { |
| 215 | $query[substr_replace($k, hex2bin(substr($k, 0, $i)) . '[', 0, 1 + $i)] = $v; |
| 216 | } else { |
| 217 | $query[hex2bin($k)] = $v; |
| 218 | } |
| 219 | } |
| 220 | return $query; |
| 221 | } |
| 222 | private static function groupParts(array $matches, string $separators, bool $first = \true) : array |
| 223 | { |
| 224 | $separator = $separators[0]; |
| 225 | $separators = substr($separators, 1) ?: ''; |
| 226 | $i = 0; |
| 227 | if ('' === $separators && !$first) { |
| 228 | $parts = ['']; |
| 229 | foreach ($matches as $match) { |
| 230 | if (!$i && isset($match['separator'])) { |
| 231 | $i = 1; |
| 232 | $parts[1] = ''; |
| 233 | } else { |
| 234 | $parts[$i] .= self::unquote($match[0]); |
| 235 | } |
| 236 | } |
| 237 | return $parts; |
| 238 | } |
| 239 | $parts = []; |
| 240 | $partMatches = []; |
| 241 | foreach ($matches as $match) { |
| 242 | if (($match['separator'] ?? null) === $separator) { |
| 243 | ++$i; |
| 244 | } else { |
| 245 | $partMatches[$i][] = $match; |
| 246 | } |
| 247 | } |
| 248 | foreach ($partMatches as $matches) { |
| 249 | if ('' === $separators && '' !== ($unquoted = self::unquote($matches[0][0]))) { |
| 250 | $parts[] = $unquoted; |
| 251 | } elseif ($groupedParts = self::groupParts($matches, $separators, \false)) { |
| 252 | $parts[] = $groupedParts; |
| 253 | } |
| 254 | } |
| 255 | return $parts; |
| 256 | } |
| 257 | } |
| 258 |