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
|
* 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
|
* @return array<int|string, array<string, string>|string>|string
|
||||||
*/
|
*/
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace CodeIgniter\HTTP;
|
namespace CodeIgniter\HTTP;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An HTTP message
|
* An HTTP message
|
||||||
*
|
*
|
||||||
@ -112,6 +114,13 @@ class Message implements MessageInterface
|
|||||||
*/
|
*/
|
||||||
public function getHeaderLine(string $name): string
|
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);
|
$origName = $this->getHeaderName($name);
|
||||||
|
|
||||||
if (! array_key_exists($origName, $this->headers)) {
|
if (! array_key_exists($origName, $this->headers)) {
|
||||||
|
@ -62,7 +62,7 @@ interface MessageInterface
|
|||||||
/**
|
/**
|
||||||
* Returns an array containing all Headers.
|
* 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;
|
public function headers(): array;
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ interface MessageInterface
|
|||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
*
|
*
|
||||||
* @return array|Header|null
|
* @return Header|list<Header>|null
|
||||||
*/
|
*/
|
||||||
public function header($name);
|
public function header($name);
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace CodeIgniter\HTTP;
|
namespace CodeIgniter\HTTP;
|
||||||
|
|
||||||
use CodeIgniter\HTTP\Exceptions\HTTPException;
|
use CodeIgniter\HTTP\Exceptions\HTTPException;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message Trait
|
* Message Trait
|
||||||
@ -25,7 +26,11 @@ trait MessageTrait
|
|||||||
/**
|
/**
|
||||||
* List of all HTTP request headers.
|
* List of all HTTP request headers.
|
||||||
*
|
*
|
||||||
* @var array<string, Header>
|
* [name => Header]
|
||||||
|
* or
|
||||||
|
* [name => [Header1, Header2]]
|
||||||
|
*
|
||||||
|
* @var array<string, Header|list<Header>>
|
||||||
*/
|
*/
|
||||||
protected $headers = [];
|
protected $headers = [];
|
||||||
|
|
||||||
@ -93,7 +98,7 @@ trait MessageTrait
|
|||||||
|
|
||||||
$this->setHeader($header, $_SERVER[$key]);
|
$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;
|
$this->headerMap[strtolower($header)] = $header;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +107,7 @@ trait MessageTrait
|
|||||||
/**
|
/**
|
||||||
* Returns an array containing all Headers.
|
* 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
|
public function headers(): array
|
||||||
{
|
{
|
||||||
@ -122,7 +127,7 @@ trait MessageTrait
|
|||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
*
|
*
|
||||||
* @return array|Header|null
|
* @return Header|list<Header>|null
|
||||||
*/
|
*/
|
||||||
public function header($name)
|
public function header($name)
|
||||||
{
|
{
|
||||||
@ -140,9 +145,14 @@ trait MessageTrait
|
|||||||
*/
|
*/
|
||||||
public function setHeader(string $name, $value): self
|
public function setHeader(string $name, $value): self
|
||||||
{
|
{
|
||||||
|
$this->checkMultipleHeaders($name);
|
||||||
|
|
||||||
$origName = $this->getHeaderName($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)) {
|
if (! is_array($value)) {
|
||||||
$value = [$value];
|
$value = [$value];
|
||||||
}
|
}
|
||||||
@ -158,6 +168,23 @@ trait MessageTrait
|
|||||||
return $this;
|
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.
|
* Removes a header from the list of headers we track.
|
||||||
*
|
*
|
||||||
@ -179,6 +206,8 @@ trait MessageTrait
|
|||||||
*/
|
*/
|
||||||
public function appendHeader(string $name, ?string $value): self
|
public function appendHeader(string $name, ?string $value): self
|
||||||
{
|
{
|
||||||
|
$this->checkMultipleHeaders($name);
|
||||||
|
|
||||||
$origName = $this->getHeaderName($name);
|
$origName = $this->getHeaderName($name);
|
||||||
|
|
||||||
array_key_exists($origName, $this->headers)
|
array_key_exists($origName, $this->headers)
|
||||||
@ -188,6 +217,33 @@ trait MessageTrait
|
|||||||
return $this;
|
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
|
* Adds an additional header value to any headers that accept
|
||||||
* multiple values (i.e. are an array or implement ArrayAccess)
|
* multiple values (i.e. are an array or implement ArrayAccess)
|
||||||
@ -196,6 +252,8 @@ trait MessageTrait
|
|||||||
*/
|
*/
|
||||||
public function prependHeader(string $name, string $value): self
|
public function prependHeader(string $name, string $value): self
|
||||||
{
|
{
|
||||||
|
$this->checkMultipleHeaders($name);
|
||||||
|
|
||||||
$origName = $this->getHeaderName($name);
|
$origName = $this->getHeaderName($name);
|
||||||
|
|
||||||
$this->headers[$origName]->prependValue($value);
|
$this->headers[$origName]->prependValue($value);
|
||||||
|
@ -13,6 +13,7 @@ namespace CodeIgniter\HTTP;
|
|||||||
|
|
||||||
use CodeIgniter\HTTP\Exceptions\HTTPException;
|
use CodeIgniter\HTTP\Exceptions\HTTPException;
|
||||||
use CodeIgniter\Test\CIUnitTestCase;
|
use CodeIgniter\Test\CIUnitTestCase;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -207,7 +208,7 @@ final class MessageTest extends CIUnitTestCase
|
|||||||
/**
|
/**
|
||||||
* @dataProvider provideArrayHeaderValue
|
* @dataProvider provideArrayHeaderValue
|
||||||
*
|
*
|
||||||
* @param mixed $arrayHeaderValue
|
* @param array $arrayHeaderValue
|
||||||
*/
|
*/
|
||||||
public function testSetHeaderWithExistingArrayValuesAppendStringValue($arrayHeaderValue): void
|
public function testSetHeaderWithExistingArrayValuesAppendStringValue($arrayHeaderValue): void
|
||||||
{
|
{
|
||||||
@ -220,7 +221,7 @@ final class MessageTest extends CIUnitTestCase
|
|||||||
/**
|
/**
|
||||||
* @dataProvider provideArrayHeaderValue
|
* @dataProvider provideArrayHeaderValue
|
||||||
*
|
*
|
||||||
* @param mixed $arrayHeaderValue
|
* @param array $arrayHeaderValue
|
||||||
*/
|
*/
|
||||||
public function testSetHeaderWithExistingArrayValuesAppendArrayValue($arrayHeaderValue): void
|
public function testSetHeaderWithExistingArrayValuesAppendArrayValue($arrayHeaderValue): void
|
||||||
{
|
{
|
||||||
@ -304,4 +305,73 @@ final class MessageTest extends CIUnitTestCase
|
|||||||
|
|
||||||
$_SERVER = $original; // restore so code coverage doesn't break
|
$_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.
|
usage in your view files, which was supported by CodeIgniter 3.
|
||||||
- **CSP:** Added ``ContentSecurityPolicy::clearDirective()`` method to clear
|
- **CSP:** Added ``ContentSecurityPolicy::clearDirective()`` method to clear
|
||||||
existing CSP directives. See :ref:`csp-clear-directives`.
|
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
|
Message Changes
|
||||||
***************
|
***************
|
||||||
|
@ -7,7 +7,7 @@ requests and responses, including the message body, protocol version, utilities
|
|||||||
the headers, and methods for handling content negotiation.
|
the headers, and methods for handling content negotiation.
|
||||||
|
|
||||||
This class is the parent class that both the :doc:`Request Class <../incoming/request>` and the
|
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
|
Class Reference
|
||||||
@ -146,6 +146,20 @@ Class Reference
|
|||||||
|
|
||||||
.. literalinclude:: message/009.php
|
.. 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()
|
.. php:method:: getProtocolVersion()
|
||||||
|
|
||||||
:returns: The current HTTP protocol version
|
: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