3 namespace MediaWiki\Rest
;
6 * This is a container for storing headers. The header names are case-insensitive,
7 * but the case is preserved for methods that return headers in bulk. The
8 * header values are a comma-separated list, or equivalently, an array of strings.
10 * Unlike PSR-7, the container is mutable.
12 class HeaderContainer
{
14 private $headerLists = [];
16 private $headerLines = [];
18 private $headerNames = [];
21 * Erase any existing headers and replace them with the specified
22 * header arrays or values.
24 * @param array $headers
26 public function resetHeaders( $headers = [] ) {
27 $this->headerLines
= [];
28 $this->headerLists
= [];
29 $this->headerNames
= [];
30 foreach ( $headers as $name => $value ) {
31 $this->headerNames
[ strtolower( $name ) ] = $name;
32 [ $valueParts, $valueLine ] = $this->convertToListAndString( $value );
33 $this->headerLines
[$name] = $valueLine;
34 $this->headerLists
[$name] = $valueParts;
39 * Take an input header value, which may either be a string or an array,
40 * and convert it to an array of header values and a header line.
42 * The return value is an array where element 0 has the array of header
43 * values, and element 1 has the header line.
45 * Theoretically, if the input is a string, this could parse the string
46 * and split it on commas. Doing this is complicated, because some headers
47 * can contain double-quoted strings containing commas. The User-Agent
48 * header allows commas in comments delimited by parentheses. So it is not
49 * just explode(",", $value), we would need to parse a grammar defined by
50 * RFC 7231 appendix D which depends on header name.
52 * It's unclear how much it would help handlers to have fully spec-aware
53 * HTTP header handling just to split on commas. They would probably be
54 * better served by an HTTP header parsing library which provides the full
57 * @param string|string[] $value The input header value
60 private function convertToListAndString( $value ) {
61 if ( is_array( $value ) ) {
62 return [ array_values( $value ), implode( ', ', $value ) ];
64 return [ [ $value ], $value ];
69 * Set or replace a header
72 * @param string|string[] $value
74 public function setHeader( $name, $value ) {
75 [ $valueParts, $valueLine ] = $this->convertToListAndString( $value );
76 $lowerName = strtolower( $name );
77 $origName = $this->headerNames
[$lowerName] ??
null;
78 if ( $origName !== null ) {
79 unset( $this->headerLines
[$origName] );
80 unset( $this->headerLists
[$origName] );
82 $this->headerNames
[$lowerName] = $name;
83 $this->headerLines
[$name] = $valueLine;
84 $this->headerLists
[$name] = $valueParts;
88 * Set a header or append to an existing header
91 * @param string|string[] $value
93 public function addHeader( $name, $value ) {
94 [ $valueParts, $valueLine ] = $this->convertToListAndString( $value );
95 $lowerName = strtolower( $name );
96 $origName = $this->headerNames
[$lowerName] ??
null;
97 if ( $origName === null ) {
99 $this->headerNames
[$lowerName] = $origName;
100 $this->headerLines
[$origName] = $valueLine;
101 $this->headerLists
[$origName] = $valueParts;
103 $this->headerLines
[$origName] .= ', ' . $valueLine;
104 $this->headerLists
[$origName] = array_merge( $this->headerLists
[$origName],
112 * @param string $name
114 public function removeHeader( $name ) {
115 $lowerName = strtolower( $name );
116 $origName = $this->headerNames
[$lowerName] ??
null;
117 if ( $origName !== null ) {
118 unset( $this->headerNames
[$lowerName] );
119 unset( $this->headerLines
[$origName] );
120 unset( $this->headerLists
[$origName] );
125 * Get header arrays indexed by original name
129 public function getHeaders() {
130 return $this->headerLists
;
134 * Get the header with a particular name, or an empty array if there is no
137 * @param string $name
140 public function getHeader( $name ) {
141 $headerName = $this->headerNames
[ strtolower( $name ) ] ??
null;
142 if ( $headerName === null ) {
145 return $this->headerLists
[$headerName];
149 * Return true if the header exists, false otherwise
150 * @param string $name
153 public function hasHeader( $name ) {
154 return isset( $this->headerNames
[ strtolower( $name ) ] );
158 * Get the specified header concatenated into a comma-separated string.
159 * If the header does not exist, an empty string is returned.
161 * @param string $name
164 public function getHeaderLine( $name ) {
165 $headerName = $this->headerNames
[ strtolower( $name ) ] ??
null;
166 if ( $headerName === null ) {
169 return $this->headerLines
[$headerName];
173 * Get all header lines
177 public function getHeaderLines() {
178 return $this->headerLines
;
182 * Get an array of strings of the form "Name: Value", suitable for passing
183 * directly to header() to set response headers. The PHP manual describes
184 * these strings as "raw HTTP headers", so we adopt that terminology.
186 * @return string[] Header list (integer indexed)
188 public function getRawHeaderLines() {
190 foreach ( $this->headerNames
as $lowerName => $name ) {
191 if ( $lowerName === 'set-cookie' ) {
192 // As noted by RFC 7230 section 3.2.2, Set-Cookie is the only
193 // header for which multiple values cannot be concatenated into
194 // a single comma-separated line.
195 foreach ( $this->headerLists
[$name] as $value ) {
196 $lines[] = "$name: $value";
199 $lines[] = "$name: " . $this->headerLines
[$name];