Skip to content

Commit

Permalink
feat: add Message::addHeader()
Browse files Browse the repository at this point in the history
  • Loading branch information
kenjis committed Nov 12, 2023
1 parent b5c1f2b commit c60bb05
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 6 deletions.
4 changes: 2 additions & 2 deletions system/HTTP/MessageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public function populateHeaders(): void;
/**
* Returns an array containing all Headers.
*
* @return array<string, Header> An array of the Header objects
* @return array<string, Header|list<Header>> An array of the Header objects
*/
public function headers(): array;

Expand All @@ -83,7 +83,7 @@ public function hasHeader(string $name): bool;
*
* @param string $name
*
* @return array|Header|null
* @return Header|list<Header>|null
*/
public function header($name);

Expand Down
68 changes: 64 additions & 4 deletions system/HTTP/MessageTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace CodeIgniter\HTTP;

use CodeIgniter\HTTP\Exceptions\HTTPException;
use InvalidArgumentException;

/**
* Message Trait
Expand All @@ -25,7 +26,11 @@ trait MessageTrait
/**
* List of all HTTP request headers.
*
* @var array<string, Header>
* [name => Header]
* or
* [name => [Header1, Header2]]
*
* @var array<string, Header|list<Header>>
*/
protected $headers = [];

Expand Down Expand Up @@ -102,7 +107,7 @@ public function populateHeaders(): void
/**
* Returns an array containing all Headers.
*
* @return array<string, Header> An array of the Header objects
* @return array<string, Header|list<Header>> An array of the Header objects
*/
public function headers(): array
{
Expand All @@ -122,7 +127,7 @@ public function headers(): array
*
* @param string $name
*
* @return array|Header|null
* @return Header|list<Header>|null
*/
public function header($name)
{
Expand All @@ -140,9 +145,14 @@ public function header($name)
*/
public function setHeader(string $name, $value): self
{
$this->checkMultipleHeaders($name);

$origName = $this->getHeaderName($name);

if (isset($this->headers[$origName]) && is_array($this->headers[$origName]->getValue())) {
if (
isset($this->headers[$origName])
&& is_array($this->headers[$origName]->getValue())
) {
if (! is_array($value)) {
$value = [$value];
}
Expand All @@ -158,6 +168,23 @@ public function setHeader(string $name, $value): self
return $this;
}

private function hasMultipleHeaders(string $name): bool
{
$origName = $this->getHeaderName($name);

return isset($this->headers[$origName]) && is_array($this->headers[$origName]);
}

private function checkMultipleHeaders(string $name): void
{
if ($this->hasMultipleHeaders($name)) {
throw new InvalidArgumentException(
'The header "' . $name . '" already has multiple headers.'
. ' You cannot change them. If you really need to change, remove the header first.'
);
}
}

/**
* Removes a header from the list of headers we track.
*
Expand All @@ -179,6 +206,8 @@ public function removeHeader(string $name): self
*/
public function appendHeader(string $name, ?string $value): self
{
$this->checkMultipleHeaders($name);

$origName = $this->getHeaderName($name);

array_key_exists($origName, $this->headers)
Expand All @@ -188,6 +217,35 @@ public function appendHeader(string $name, ?string $value): self
return $this;
}

/**
* Adds a header (not a header value) with the same name.
* Use this only when you set multiple headers with the same name,
* typically, for `Set-Cookie`.
*
* @return $this
*/
public function addHeader(string $name, string $value): static
{
$origName = $this->getHeaderName($name);

if (! isset($this->headers[$origName])) {
$this->setHeader($name, $value);

return $this;
}

if (! $this->hasMultipleHeaders($name)) {
if (isset($this->headers[$origName])) {
$this->headers[$origName] = [$this->headers[$origName]];
}
}

// Add the header.
$this->headers[$origName][] = new Header($origName, $value);

return $this;
}

/**
* Adds an additional header value to any headers that accept
* multiple values (i.e. are an array or implement ArrayAccess)
Expand All @@ -196,6 +254,8 @@ public function appendHeader(string $name, ?string $value): self
*/
public function prependHeader(string $name, string $value): self
{
$this->checkMultipleHeaders($name);

$origName = $this->getHeaderName($name);

$this->headers[$origName]->prependValue($value);
Expand Down
51 changes: 51 additions & 0 deletions tests/system/HTTP/MessageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use CodeIgniter\HTTP\Exceptions\HTTPException;
use CodeIgniter\Test\CIUnitTestCase;
use InvalidArgumentException;

/**
* @internal
Expand Down Expand Up @@ -304,4 +305,54 @@ public function testPopulateHeaders(): void

$_SERVER = $original; // restore so code coverage doesn't break
}

public function testAddHeaderAddsFirstHeader(): void
{
$this->message->addHeader(
'Set-Cookie',
'logged_in=no; Path=/'
);

$header = $this->message->header('Set-Cookie');

$this->assertInstanceOf(Header::class, $header);
$this->assertSame('logged_in=no; Path=/', $header->getValue());
}

public function testAddHeaderAddsTwoHeaders(): void
{
$this->message->addHeader(
'Set-Cookie',
'logged_in=no; Path=/'
);
$this->message->addHeader(
'Set-Cookie',
'sessid=123456; Path=/'
);

$headers = $this->message->header('Set-Cookie');

$this->assertCount(2, $headers);
$this->assertSame('logged_in=no; Path=/', $headers[0]->getValue());
$this->assertSame('sessid=123456; Path=/', $headers[1]->getValue());
}

public function testAppendHeaderWithMultipleHeaders(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage(
'The header "Set-Cookie" already has multiple headers. You cannot change them. If you really need to change, remove the header first.'
);

$this->message->addHeader(
'Set-Cookie',
'logged_in=no; Path=/'
);
$this->message->addHeader(
'Set-Cookie',
'sessid=123456; Path=/'
);

$this->message->appendHeader('Set-Cookie', 'HttpOnly');
}
}

0 comments on commit c60bb05

Please sign in to comment.