Completed
Push — master ( 8414ee...13c677 )
by
unknown
32s queued 20s
created

src/Qiniu/Http/Header.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace Qiniu\Http;
4
5
/**
6
 * field name case-insensitive Header
7
 */
8
class Header implements \ArrayAccess, \IteratorAggregate, \Countable
9
{
10
    /** @var array normalized key name map */
11
    private $data;
12
13
    /**
14
     * @param array $obj non-normalized header object
15
     */
16
    public function __construct($obj = array())
17
    {
18
        foreach ($obj as $key => $values) {
19
            $normalizedKey = self::normalizeKey($key);
20
            $normalizedValues = array();
21
            foreach ($values as $value) {
22
                array_push($normalizedValues, self::normalizeValue($value));
23
            }
24
            $this->data[$normalizedKey] = $normalizedValues;
25
        }
26
        return $this;
27
    }
28
29
    /**
30
     * return origin headers, which is field name case-sensitive
31
     *
32
     * @param string $raw
33
     *
34
     * @return array
35
     */
36
    public static function parseRawText($raw)
37
    {
38
        $headers = array();
39
        $headerLines = explode("\r\n", $raw);
40
        foreach ($headerLines as $line) {
41
            $headerLine = trim($line);
42
            $kv = explode(':', $headerLine);
43
            if (count($kv) <= 1) {
44
                continue;
45
            }
46
            // for http2 [Pseudo-Header Fields](https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.1)
47
            if ($kv[0] == "") {
48
                $fieldName = ":" . $kv[1];
49
            } else {
50
                $fieldName = $kv[0];
51
            }
52
            $fieldValue = trim(substr($headerLine, strlen($fieldName . ":")));
53
            if (isset($headers[$fieldName])) {
54
                array_push($headers[$fieldName], $fieldValue);
55
            } else {
56
                $headers[$fieldName] = array($fieldValue);
57
            }
58
        }
59
        return $headers;
60
    }
61
62
    /**
63
     * @param string $raw
64
     *
65
     * @return Header
66
     */
67
    public static function fromRawText($raw)
68
    {
69
        return new Header(self::parseRawText($raw));
70
    }
71
72
    /**
73
     * @param string $key
74
     *
75
     * @return string
76
     */
77
    public static function normalizeKey($key)
78
    {
79
        $key = trim($key);
80
81
        if (!self::isValidKeyName($key)) {
82
            return $key;
83
        }
84
85
        return ucwords(strtolower($key), '-');
86
    }
87
88
    /**
89
     * @param string|numeric $value
90
     *
91
     * @return string|numeric
92
     */
93
    public static function normalizeValue($value)
94
    {
95
        if (is_numeric($value)) {
96
            return $value + 0;
97
        }
98
        return trim($value);
99
    }
100
101
    /**
102
     * @return array
103
     */
104
    public function getRawData()
105
    {
106
        return $this->data;
107
    }
108
109
    /**
110
     * @param $offset string
111
     *
112
     * @return boolean
113
     */
114
    public function offsetExists($offset)
115
    {
116
        $key = self::normalizeKey($offset);
117
        return isset($this->data[$key]);
118
    }
119
120
    /**
121
     * @param $offset string
122
     *
123
     * @return string|null
124
     */
125
    public function offsetGet($offset)
126
    {
127
        $key = self::normalizeKey($offset);
128
        if (isset($this->data[$key]) && count($this->data[$key])) {
129
            return $this->data[$key][0];
130
        } else {
131
            return null;
132
        }
133
    }
134
135
    /**
136
     * @param $offset string
137
     * @param $value string
138
     *
139
     * @return void
140
     */
141
    public function offsetSet($offset, $value)
142
    {
143
        $key = self::normalizeKey($offset);
144
        if (isset($this->data[$key]) && count($this->data[$key] > 0)) {
0 ignored issues
show
$this->data[$key] > 0 of type boolean is incompatible with the type Countable|array expected by parameter $value of count(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

144
        if (isset($this->data[$key]) && count(/** @scrutinizer ignore-type */ $this->data[$key] > 0)) {
Loading history...
145
            $this->data[$key][0] = self::normalizeValue($value);
146
        } else {
147
            $this->data[$key] = array(self::normalizeValue($value));
148
        }
149
    }
150
151
    /**
152
     * @return void
153
     */
154
    public function offsetUnset($offset)
155
    {
156
        $key = self::normalizeKey($offset);
157
        unset($this->data[$key]);
158
    }
159
160
    /**
161
     * @return \ArrayIterator
162
     */
163
    public function getIterator()
164
    {
165
        $arr = array();
166
        foreach ($this->data as $k => $v) {
167
            $arr[$k] = $v[0];
168
        }
169
        return new \ArrayIterator($arr);
170
    }
171
172
    /**
173
     * @return int
174
     */
175
    public function count()
176
    {
177
        return count($this->data);
178
    }
179
180
    private static $isTokenTable = array(
181
        '!' => true,
182
        '#' => true,
183
        '$' => true,
184
        '%' => true,
185
        '&' => true,
186
        '\'' => true,
187
        '*' => true,
188
        '+' => true,
189
        '-' => true,
190
        '.' => true,
191
        '0' => true,
192
        '1' => true,
193
        '2' => true,
194
        '3' => true,
195
        '4' => true,
196
        '5' => true,
197
        '6' => true,
198
        '7' => true,
199
        '8' => true,
200
        '9' => true,
201
        'A' => true,
202
        'B' => true,
203
        'C' => true,
204
        'D' => true,
205
        'E' => true,
206
        'F' => true,
207
        'G' => true,
208
        'H' => true,
209
        'I' => true,
210
        'J' => true,
211
        'K' => true,
212
        'L' => true,
213
        'M' => true,
214
        'N' => true,
215
        'O' => true,
216
        'P' => true,
217
        'Q' => true,
218
        'R' => true,
219
        'S' => true,
220
        'T' => true,
221
        'U' => true,
222
        'W' => true,
223
        'V' => true,
224
        'X' => true,
225
        'Y' => true,
226
        'Z' => true,
227
        '^' => true,
228
        '_' => true,
229
        '`' => true,
230
        'a' => true,
231
        'b' => true,
232
        'c' => true,
233
        'd' => true,
234
        'e' => true,
235
        'f' => true,
236
        'g' => true,
237
        'h' => true,
238
        'i' => true,
239
        'j' => true,
240
        'k' => true,
241
        'l' => true,
242
        'm' => true,
243
        'n' => true,
244
        'o' => true,
245
        'p' => true,
246
        'q' => true,
247
        'r' => true,
248
        's' => true,
249
        't' => true,
250
        'u' => true,
251
        'v' => true,
252
        'w' => true,
253
        'x' => true,
254
        'y' => true,
255
        'z' => true,
256
        '|' => true,
257
        '~' => true,
258
    );
259
260
    /**
261
     * @param string $str
262
     *
263
     * @return boolean
264
     */
265
    private static function isValidKeyName($str)
266
    {
267
        for ($i = 0; $i < strlen($str); $i += 1) {
268
            if (!isset(self::$isTokenTable[$str[$i]])) {
269
                return false;
270
            }
271
        }
272
        return true;
273
    }
274
}
275