forked from hyperf/http-message
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHeaderFieldParser.php
More file actions
109 lines (97 loc) · 3.6 KB
/
HeaderFieldParser.php
File metadata and controls
109 lines (97 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\HttpMessage\Util;
use RuntimeException;
use function preg_match_all;
use function strcasecmp;
use function strtok;
use function strtolower;
use function substr;
/**
* Utility class for parsing HTTP header fields.
*
* This class provides functionality to parse structured header fields like
* Content-Type, Content-Disposition, etc., which contain a main value and
* optional parameters (e.g., "text/html; charset=utf-8").
*
* Replaces laminas/laminas-mime dependency with a lightweight implementation.
*/
class HeaderFieldParser
{
/**
* Split a header field into its different parts.
*
* Parses header fields like "text/html; charset=utf-8; boundary=something"
* into structured data.
*
* @param string $field the header field to parse
* @param string|null $wantedPart the wanted part name, or null to return all parts as array
* @param string $firstName key name for the first part (default: '0')
* @return string|array|null wanted part value, all parts as array, or null if not found
*/
public static function splitHeaderField(string $field, ?string $wantedPart = null, string $firstName = '0'): string|array|null
{
$wantedPart = strtolower($wantedPart ?? '');
$firstName = strtolower($firstName);
// Special case - optimized path for getting just the first part
if ($firstName === $wantedPart) {
$field = strtok($field, ';');
return $field[0] === '"' ? substr($field, 1, -1) : $field;
}
// Prepend the first part with firstName as key
$field = $firstName . '=' . $field;
// Parse all key=value pairs
// Pattern: key="quoted value" or key=unquoted-value
if (! preg_match_all('%([^=\s]+)\s*=\s*("[^"]+"|[^;]+)(;\s*|$)%', $field, $matches)) {
throw new RuntimeException('not a valid header field');
}
// If looking for a specific part
if ($wantedPart !== '') {
foreach ($matches[1] as $key => $name) {
if (strcasecmp($name, $wantedPart) !== 0) {
continue;
}
// Remove quotes if present
if ($matches[2][$key][0] !== '"') {
return $matches[2][$key];
}
return substr($matches[2][$key], 1, -1);
}
return null;
}
// Return all parts as associative array
$split = [];
foreach ($matches[1] as $key => $name) {
$name = strtolower($name);
// Remove quotes if present
if ($matches[2][$key][0] === '"') {
$split[$name] = substr($matches[2][$key], 1, -1);
} else {
$split[$name] = $matches[2][$key];
}
}
return $split;
}
/**
* Split a Content-Type header into its different parts.
*
* Convenience method for parsing Content-Type headers.
* Returns type and parameters (charset, boundary, etc.).
*
* @param string $type the content-type header value
* @param string|null $wantedPart the wanted part, or null to return all parts
* @return string|array|null wanted part or all parts as array('type' => content-type, partname => value)
*/
public static function splitContentType(string $type, ?string $wantedPart = null): string|array|null
{
return self::splitHeaderField($type, $wantedPart, 'type');
}
}