1212namespace CodeIgniter \HTTP ;
1313
1414use CodeIgniter \HTTP \Exceptions \HTTPException ;
15+ use InvalidArgumentException ;
1516
1617/**
1718 * Message Trait
@@ -25,7 +26,11 @@ trait MessageTrait
2526 /**
2627 * List of all HTTP request headers.
2728 *
28- * @var array<string, Header>
29+ * [name => Header]
30+ * or
31+ * [name => [Header1, Header2]]
32+ *
33+ * @var array<string, Header|list<Header>>
2934 */
3035 protected $ headers = [];
3136
@@ -102,7 +107,7 @@ public function populateHeaders(): void
102107 /**
103108 * Returns an array containing all Headers.
104109 *
105- * @return array<string, Header> An array of the Header objects
110+ * @return array<string, Header|list<Header> > An array of the Header objects
106111 */
107112 public function headers (): array
108113 {
@@ -122,7 +127,7 @@ public function headers(): array
122127 *
123128 * @param string $name
124129 *
125- * @return array| Header|null
130+ * @return Header|list< Header> |null
126131 */
127132 public function header ($ name )
128133 {
@@ -140,9 +145,14 @@ public function header($name)
140145 */
141146 public function setHeader (string $ name , $ value ): self
142147 {
148+ $ this ->checkMultipleHeaders ($ name );
149+
143150 $ origName = $ this ->getHeaderName ($ name );
144151
145- if (isset ($ this ->headers [$ origName ]) && is_array ($ this ->headers [$ origName ]->getValue ())) {
152+ if (
153+ isset ($ this ->headers [$ origName ])
154+ && is_array ($ this ->headers [$ origName ]->getValue ())
155+ ) {
146156 if (! is_array ($ value )) {
147157 $ value = [$ value ];
148158 }
@@ -158,6 +168,23 @@ public function setHeader(string $name, $value): self
158168 return $ this ;
159169 }
160170
171+ private function hasMultipleHeaders (string $ name ): bool
172+ {
173+ $ origName = $ this ->getHeaderName ($ name );
174+
175+ return isset ($ this ->headers [$ origName ]) && is_array ($ this ->headers [$ origName ]);
176+ }
177+
178+ private function checkMultipleHeaders (string $ name ): void
179+ {
180+ if ($ this ->hasMultipleHeaders ($ name )) {
181+ throw new InvalidArgumentException (
182+ 'The header " ' . $ name . '" already has multiple headers. '
183+ . ' You cannot change them. If you really need to change, remove the header first. '
184+ );
185+ }
186+ }
187+
161188 /**
162189 * Removes a header from the list of headers we track.
163190 *
@@ -179,6 +206,8 @@ public function removeHeader(string $name): self
179206 */
180207 public function appendHeader (string $ name , ?string $ value ): self
181208 {
209+ $ this ->checkMultipleHeaders ($ name );
210+
182211 $ origName = $ this ->getHeaderName ($ name );
183212
184213 array_key_exists ($ origName , $ this ->headers )
@@ -188,6 +217,35 @@ public function appendHeader(string $name, ?string $value): self
188217 return $ this ;
189218 }
190219
220+ /**
221+ * Adds a header (not a header value) with the same name.
222+ * Use this only when you set multiple headers with the same name,
223+ * typically, for `Set-Cookie`.
224+ *
225+ * @return $this
226+ */
227+ public function addHeader (string $ name , string $ value ): static
228+ {
229+ $ origName = $ this ->getHeaderName ($ name );
230+
231+ if (! isset ($ this ->headers [$ origName ])) {
232+ $ this ->setHeader ($ name , $ value );
233+
234+ return $ this ;
235+ }
236+
237+ if (! $ this ->hasMultipleHeaders ($ name )) {
238+ if (isset ($ this ->headers [$ origName ])) {
239+ $ this ->headers [$ origName ] = [$ this ->headers [$ origName ]];
240+ }
241+ }
242+
243+ // Add the header.
244+ $ this ->headers [$ origName ][] = new Header ($ origName , $ value );
245+
246+ return $ this ;
247+ }
248+
191249 /**
192250 * Adds an additional header value to any headers that accept
193251 * multiple values (i.e. are an array or implement ArrayAccess)
@@ -196,6 +254,8 @@ public function appendHeader(string $name, ?string $value): self
196254 */
197255 public function prependHeader (string $ name , string $ value ): self
198256 {
257+ $ this ->checkMultipleHeaders ($ name );
258+
199259 $ origName = $ this ->getHeaderName ($ name );
200260
201261 $ this ->headers [$ origName ]->prependValue ($ value );
0 commit comments