mirror of
https://github.com/codeigniter4/CodeIgniter4.git
synced 2025-02-20 11:44:28 +08:00
Merge pull request #8194 from kenjis/feat-Message-addHeader
feat: add Message::addHeader() to add header with the same name
This commit is contained in:
commit
9954ccf67a
@ -64,7 +64,7 @@ class Header
|
||||
|
||||
/**
|
||||
* Gets the raw value of the header. This may return either a string
|
||||
* of an array, depending on whether the header has multiple values or not.
|
||||
* or an array, depending on whether the header has multiple values or not.
|
||||
*
|
||||
* @return array<int|string, array<string, string>|string>|string
|
||||
*/
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
namespace CodeIgniter\HTTP;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* An HTTP message
|
||||
*
|
||||
@ -112,6 +114,13 @@ class Message implements MessageInterface
|
||||
*/
|
||||
public function getHeaderLine(string $name): string
|
||||
{
|
||||
if ($this->hasMultipleHeaders($name)) {
|
||||
throw new InvalidArgumentException(
|
||||
'The header "' . $name . '" already has multiple headers.'
|
||||
. ' You cannot use getHeaderLine().'
|
||||
);
|
||||
}
|
||||
|
||||
$origName = $this->getHeaderName($name);
|
||||
|
||||
if (! array_key_exists($origName, $this->headers)) {
|
||||
|
@ -62,7 +62,7 @@ interface MessageInterface
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@ -83,7 +83,7 @@ interface MessageInterface
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return array|Header|null
|
||||
* @return Header|list<Header>|null
|
||||
*/
|
||||
public function header($name);
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace CodeIgniter\HTTP;
|
||||
|
||||
use CodeIgniter\HTTP\Exceptions\HTTPException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Message Trait
|
||||
@ -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 = [];
|
||||
|
||||
@ -93,7 +98,7 @@ trait MessageTrait
|
||||
|
||||
$this->setHeader($header, $_SERVER[$key]);
|
||||
|
||||
// Add us to the header map so we can find them case-insensitively
|
||||
// Add us to the header map, so we can find them case-insensitively
|
||||
$this->headerMap[strtolower($header)] = $header;
|
||||
}
|
||||
}
|
||||
@ -102,7 +107,7 @@ trait MessageTrait
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -122,7 +127,7 @@ trait MessageTrait
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return array|Header|null
|
||||
* @return Header|list<Header>|null
|
||||
*/
|
||||
public function header($name)
|
||||
{
|
||||
@ -140,9 +145,14 @@ trait MessageTrait
|
||||
*/
|
||||
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];
|
||||
}
|
||||
@ -158,6 +168,23 @@ trait MessageTrait
|
||||
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.
|
||||
*
|
||||
@ -179,6 +206,8 @@ trait MessageTrait
|
||||
*/
|
||||
public function appendHeader(string $name, ?string $value): self
|
||||
{
|
||||
$this->checkMultipleHeaders($name);
|
||||
|
||||
$origName = $this->getHeaderName($name);
|
||||
|
||||
array_key_exists($origName, $this->headers)
|
||||
@ -188,6 +217,33 @@ trait MessageTrait
|
||||
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) && 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)
|
||||
@ -196,6 +252,8 @@ trait MessageTrait
|
||||
*/
|
||||
public function prependHeader(string $name, string $value): self
|
||||
{
|
||||
$this->checkMultipleHeaders($name);
|
||||
|
||||
$origName = $this->getHeaderName($name);
|
||||
|
||||
$this->headers[$origName]->prependValue($value);
|
||||
|
@ -13,6 +13,7 @@ namespace CodeIgniter\HTTP;
|
||||
|
||||
use CodeIgniter\HTTP\Exceptions\HTTPException;
|
||||
use CodeIgniter\Test\CIUnitTestCase;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -207,7 +208,7 @@ final class MessageTest extends CIUnitTestCase
|
||||
/**
|
||||
* @dataProvider provideArrayHeaderValue
|
||||
*
|
||||
* @param mixed $arrayHeaderValue
|
||||
* @param array $arrayHeaderValue
|
||||
*/
|
||||
public function testSetHeaderWithExistingArrayValuesAppendStringValue($arrayHeaderValue): void
|
||||
{
|
||||
@ -220,7 +221,7 @@ final class MessageTest extends CIUnitTestCase
|
||||
/**
|
||||
* @dataProvider provideArrayHeaderValue
|
||||
*
|
||||
* @param mixed $arrayHeaderValue
|
||||
* @param array $arrayHeaderValue
|
||||
*/
|
||||
public function testSetHeaderWithExistingArrayValuesAppendArrayValue($arrayHeaderValue): void
|
||||
{
|
||||
@ -304,4 +305,73 @@ final class MessageTest extends CIUnitTestCase
|
||||
|
||||
$_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');
|
||||
}
|
||||
|
||||
public function testGetHeaderLineWithMultipleHeaders(): void
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'The header "Set-Cookie" already has multiple headers. You cannot use getHeaderLine().'
|
||||
);
|
||||
|
||||
$this->message->addHeader(
|
||||
'Set-Cookie',
|
||||
'logged_in=no; Path=/'
|
||||
);
|
||||
$this->message->addHeader(
|
||||
'Set-Cookie',
|
||||
'sessid=123456; Path=/'
|
||||
);
|
||||
|
||||
$this->message->getHeaderLine('Set-Cookie');
|
||||
}
|
||||
}
|
||||
|
@ -299,6 +299,8 @@ Others
|
||||
usage in your view files, which was supported by CodeIgniter 3.
|
||||
- **CSP:** Added ``ContentSecurityPolicy::clearDirective()`` method to clear
|
||||
existing CSP directives. See :ref:`csp-clear-directives`.
|
||||
- **HTTP:** Added ``Message::addHeader()`` method to add another header with
|
||||
the same name. See :php:meth:`CodeIgniter\\HTTP\\Message::addHeader()`.
|
||||
|
||||
Message Changes
|
||||
***************
|
||||
|
@ -7,7 +7,7 @@ requests and responses, including the message body, protocol version, utilities
|
||||
the headers, and methods for handling content negotiation.
|
||||
|
||||
This class is the parent class that both the :doc:`Request Class <../incoming/request>` and the
|
||||
:doc:`Response Class <../outgoing/response>` extend from.
|
||||
:doc:`Response Class <../outgoing/response>` extend from, and it is not used directly.
|
||||
|
||||
***************
|
||||
Class Reference
|
||||
@ -146,6 +146,20 @@ Class Reference
|
||||
|
||||
.. literalinclude:: message/009.php
|
||||
|
||||
.. php:method:: addHeader($name, $value)
|
||||
|
||||
.. versionadded:: 4.5.0
|
||||
|
||||
:param string $name: The name of the header to add.
|
||||
:param string $value: The value of the header.
|
||||
:returns: The current message instance
|
||||
:rtype: CodeIgniter\\HTTP\\Message
|
||||
|
||||
Adds a header (not a header value) with the same name.
|
||||
Use this only when you set multiple headers with the same name,
|
||||
|
||||
.. literalinclude:: message/011.php
|
||||
|
||||
.. php:method:: getProtocolVersion()
|
||||
|
||||
:returns: The current HTTP protocol version
|
||||
|
4
user_guide_src/source/incoming/message/011.php
Normal file
4
user_guide_src/source/incoming/message/011.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
$message->addHeader('Set-Cookie', 'logged_in=no; Path=/');
|
||||
$message->addHeader('Set-Cookie', 'sessid=123456; Path=/');
|
Loading…
x
Reference in New Issue
Block a user