class HeaderUtils
HTTP header utility functions.
@author Christian Schmidt <github@chsc.dk>
Hierarchy
- class \Symfony\Component\HttpFoundation\HeaderUtils
Expanded class hierarchy of HeaderUtils
File
-
vendor/
symfony/ http-foundation/ HeaderUtils.php, line 19
Namespace
Symfony\Component\HttpFoundationView source
class HeaderUtils {
public const DISPOSITION_ATTACHMENT = 'attachment';
public const DISPOSITION_INLINE = 'inline';
/**
* This class should not be instantiated.
*/
private function __construct() {
}
/**
* Splits an HTTP header by one or more separators.
*
* Example:
*
* HeaderUtils::split('da, en-gb;q=0.8', ',;')
* # returns [['da'], ['en-gb', 'q=0.8']]
*
* @param string $separators List of characters to split on, ordered by
* precedence, e.g. ',', ';=', or ',;='
*
* @return array Nested array with as many levels as there are characters in
* $separators
*/
public static function split(string $header, string $separators) : array {
if ('' === $separators) {
throw new \InvalidArgumentException('At least one separator must be specified.');
}
$quotedSeparators = preg_quote($separators, '/');
preg_match_all('
/
(?!\\s)
(?:
# quoted-string
"(?:[^"\\\\]|\\\\.)*(?:"|\\\\|$)
|
# token
[^"' . $quotedSeparators . ']+
)+
(?<!\\s)
|
# separator
\\s*
(?<separator>[' . $quotedSeparators . '])
\\s*
/x', trim($header), $matches, \PREG_SET_ORDER);
return self::groupParts($matches, $separators);
}
/**
* Combines an array of arrays into one associative array.
*
* Each of the nested arrays should have one or two elements. The first
* value will be used as the keys in the associative array, and the second
* will be used as the values, or true if the nested array only contains one
* element. Array keys are lowercased.
*
* Example:
*
* HeaderUtils::combine([['foo', 'abc'], ['bar']])
* // => ['foo' => 'abc', 'bar' => true]
*/
public static function combine(array $parts) : array {
$assoc = [];
foreach ($parts as $part) {
$name = strtolower($part[0]);
$value = $part[1] ?? true;
$assoc[$name] = $value;
}
return $assoc;
}
/**
* Joins an associative array into a string for use in an HTTP header.
*
* The key and value of each entry are joined with '=', and all entries
* are joined with the specified separator and an additional space (for
* readability). Values are quoted if necessary.
*
* Example:
*
* HeaderUtils::toString(['foo' => 'abc', 'bar' => true, 'baz' => 'a b c'], ',')
* // => 'foo=abc, bar, baz="a b c"'
*/
public static function toString(array $assoc, string $separator) : string {
$parts = [];
foreach ($assoc as $name => $value) {
if (true === $value) {
$parts[] = $name;
}
else {
$parts[] = $name . '=' . self::quote($value);
}
}
return implode($separator . ' ', $parts);
}
/**
* Encodes a string as a quoted string, if necessary.
*
* If a string contains characters not allowed by the "token" construct in
* the HTTP specification, it is backslash-escaped and enclosed in quotes
* to match the "quoted-string" construct.
*/
public static function quote(string $s) : string {
if (preg_match('/^[a-z0-9!#$%&\'*.^_`|~-]+$/i', $s)) {
return $s;
}
return '"' . addcslashes($s, '"\\"') . '"';
}
/**
* Decodes a quoted string.
*
* If passed an unquoted string that matches the "token" construct (as
* defined in the HTTP specification), it is passed through verbatim.
*/
public static function unquote(string $s) : string {
return preg_replace('/\\\\(.)|"/', '$1', $s);
}
/**
* Generates an HTTP Content-Disposition field-value.
*
* @param string $disposition One of "inline" or "attachment"
* @param string $filename A unicode string
* @param string $filenameFallback A string containing only ASCII characters that
* is semantically equivalent to $filename. If the filename is already ASCII,
* it can be omitted, or just copied from $filename
*
* @throws \InvalidArgumentException
*
* @see RFC 6266
*/
public static function makeDisposition(string $disposition, string $filename, string $filenameFallback = '') : string {
if (!\in_array($disposition, [
self::DISPOSITION_ATTACHMENT,
self::DISPOSITION_INLINE,
])) {
throw new \InvalidArgumentException(\sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
}
if ('' === $filenameFallback) {
$filenameFallback = $filename;
}
// filenameFallback is not ASCII.
if (!preg_match('/^[\\x20-\\x7e]*$/', $filenameFallback)) {
throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
}
// percent characters aren't safe in fallback.
if (str_contains($filenameFallback, '%')) {
throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
}
// path separators aren't allowed in either.
if (str_contains($filename, '/') || str_contains($filename, '\\') || str_contains($filenameFallback, '/') || str_contains($filenameFallback, '\\')) {
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
}
$params = [
'filename' => $filenameFallback,
];
if ($filename !== $filenameFallback) {
$params['filename*'] = "utf-8''" . rawurlencode($filename);
}
return $disposition . '; ' . self::toString($params, ';');
}
/**
* Like parse_str(), but preserves dots in variable names.
*/
public static function parseQuery(string $query, bool $ignoreBrackets = false, string $separator = '&') : array {
$q = [];
foreach (explode($separator, $query) as $v) {
if (false !== ($i = strpos($v, "\x00"))) {
$v = substr($v, 0, $i);
}
if (false === ($i = strpos($v, '='))) {
$k = urldecode($v);
$v = '';
}
else {
$k = urldecode(substr($v, 0, $i));
$v = substr($v, $i);
}
if (false !== ($i = strpos($k, "\x00"))) {
$k = substr($k, 0, $i);
}
$k = ltrim($k, ' ');
if ($ignoreBrackets) {
$q[$k][] = urldecode(substr($v, 1));
continue;
}
if (false === ($i = strpos($k, '['))) {
$q[] = bin2hex($k) . $v;
}
else {
$q[] = bin2hex(substr($k, 0, $i)) . rawurlencode(substr($k, $i)) . $v;
}
}
if ($ignoreBrackets) {
return $q;
}
parse_str(implode('&', $q), $q);
$query = [];
foreach ($q as $k => $v) {
if (false !== ($i = strpos($k, '_'))) {
$query[substr_replace($k, hex2bin(substr($k, 0, $i)) . '[', 0, 1 + $i)] = $v;
}
else {
$query[hex2bin($k)] = $v;
}
}
return $query;
}
private static function groupParts(array $matches, string $separators, bool $first = true) : array {
$separator = $separators[0];
$separators = substr($separators, 1) ?: '';
$i = 0;
if ('' === $separators && !$first) {
$parts = [
'',
];
foreach ($matches as $match) {
if (!$i && isset($match['separator'])) {
$i = 1;
$parts[1] = '';
}
else {
$parts[$i] .= self::unquote($match[0]);
}
}
return $parts;
}
$parts = [];
$partMatches = [];
foreach ($matches as $match) {
if (($match['separator'] ?? null) === $separator) {
++$i;
}
else {
$partMatches[$i][] = $match;
}
}
foreach ($partMatches as $matches) {
if ('' === $separators && '' !== ($unquoted = self::unquote($matches[0][0]))) {
$parts[] = $unquoted;
}
elseif ($groupedParts = self::groupParts($matches, $separators, false)) {
$parts[] = $groupedParts;
}
}
return $parts;
}
}
Members
Title Sort descending | Modifiers | Object type | Summary |
---|---|---|---|
HeaderUtils::combine | public static | function | Combines an array of arrays into one associative array. |
HeaderUtils::DISPOSITION_ATTACHMENT | public | constant | |
HeaderUtils::DISPOSITION_INLINE | public | constant | |
HeaderUtils::groupParts | private static | function | |
HeaderUtils::makeDisposition | public static | function | Generates an HTTP Content-Disposition field-value. |
HeaderUtils::parseQuery | public static | function | Like parse_str(), but preserves dots in variable names. |
HeaderUtils::quote | public static | function | Encodes a string as a quoted string, if necessary. |
HeaderUtils::split | public static | function | Splits an HTTP header by one or more separators. |
HeaderUtils::toString | public static | function | Joins an associative array into a string for use in an HTTP header. |
HeaderUtils::unquote | public static | function | Decodes a quoted string. |
HeaderUtils::__construct | private | function | This class should not be instantiated. |