|
| 1 | +<?php |
| 2 | +/** |
| 3 | + * Class Header |
| 4 | + * |
| 5 | + * @created 28.03.2021 |
| 6 | + * @author smiley <smiley@chillerlan.net> |
| 7 | + * @copyright 2021 smiley |
| 8 | + * @license MIT |
| 9 | + */ |
| 10 | + |
| 11 | +namespace chillerlan\HTTP\Utils; |
| 12 | + |
| 13 | +use function array_keys, array_map, array_values, count, explode, implode, |
| 14 | + is_array, is_numeric, is_string, strtolower, trim, ucfirst; |
| 15 | + |
| 16 | +/** |
| 17 | + * |
| 18 | + */ |
| 19 | +class Header{ |
| 20 | + |
| 21 | + /** |
| 22 | + * Normalizes an array of header lines to format ["Name" => "Value (, Value2, Value3, ...)", ...] |
| 23 | + * An exception is being made for Set-Cookie, which holds an array of values for each cookie. |
| 24 | + * For multiple cookies with the same name, only the last value will be kept. |
| 25 | + * |
| 26 | + * @param array $headers |
| 27 | + * |
| 28 | + * @return array |
| 29 | + */ |
| 30 | + public static function normalize(array $headers):array{ |
| 31 | + $normalized = []; |
| 32 | + |
| 33 | + foreach($headers as $key => $val){ |
| 34 | + |
| 35 | + // the key is numeric, so $val is either a string or an array |
| 36 | + if(is_numeric($key)){ |
| 37 | + |
| 38 | + // "key: val" |
| 39 | + if(is_string($val)){ |
| 40 | + $header = explode(':', $val, 2); |
| 41 | + |
| 42 | + if(count($header) !== 2){ |
| 43 | + continue; |
| 44 | + } |
| 45 | + |
| 46 | + $key = $header[0]; |
| 47 | + $val = $header[1]; |
| 48 | + } |
| 49 | + // [$key, $val], ["key" => $key, "val" => $val] |
| 50 | + elseif(is_array($val)){ |
| 51 | + $key = array_keys($val)[0]; |
| 52 | + $val = array_values($val)[0]; |
| 53 | + } |
| 54 | + else{ |
| 55 | + continue; |
| 56 | + } |
| 57 | + } |
| 58 | + // the key is named, so we assume $val holds the header values only, either as string or array |
| 59 | + else{ |
| 60 | + if(is_array($val)){ |
| 61 | + $val = implode(', ', array_values($val)); |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + $key = implode('-', array_map(fn(string $v):string => ucfirst(strtolower(trim($v))), explode('-', $key))); |
| 66 | + $val = trim($val); |
| 67 | + |
| 68 | + // skip if the header already exists but the current value is empty |
| 69 | + if(isset($normalized[$key]) && empty($val)){ |
| 70 | + continue; |
| 71 | + } |
| 72 | + |
| 73 | + // cookie headers may appear multiple times |
| 74 | + // https://tools.ietf.org/html/rfc6265#section-4.1.2 |
| 75 | + if($key === 'Set-Cookie'){ |
| 76 | + // i'll just collect the last value here and leave parsing up to you :P |
| 77 | + $normalized[$key][strtolower(explode('=', $val, 2)[0])] = $val; |
| 78 | + } |
| 79 | + // combine header fields with the same name |
| 80 | + // https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 |
| 81 | + else{ |
| 82 | + isset($normalized[$key]) && !empty($normalized[$key]) |
| 83 | + ? $normalized[$key] .= ', '.$val |
| 84 | + : $normalized[$key] = $val; |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + return $normalized; |
| 89 | + } |
| 90 | + |
| 91 | +} |
0 commit comments