Skip to content

Commit effc6ad

Browse files
committed
test: refactor NativeHeadersStack to use simplified header simulation
1 parent 130f47a commit effc6ad

File tree

3 files changed

+25
-108
lines changed

3 files changed

+25
-108
lines changed

system/Test/Utilities/NativeHeadersStack.php

Lines changed: 18 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -14,134 +14,51 @@
1414
namespace CodeIgniter\Test\Utilities;
1515

1616
/**
17-
* Class NativeHeadersStack
18-
*
1917
* A utility class for simulating native PHP header handling in unit tests.
20-
* It allows the inspection, manipulation, and mocking of HTTP headers without
21-
* affecting the actual HTTP output.
2218
*
2319
* @internal This class is for testing purposes only.
2420
*/
2521
final class NativeHeadersStack
2622
{
27-
private static bool $headersSent = false;
28-
29-
/**
30-
* @var array<string, list<string>>
31-
*/
32-
private static array $headers = [];
33-
34-
private static ?int $responseCode = null;
35-
36-
/**
37-
* Resets the state of the class to its default values.
38-
*/
39-
public static function reset(): void
40-
{
41-
self::$headersSent = false;
42-
self::$headers = [];
43-
self::$responseCode = null;
44-
}
45-
46-
/**
47-
* Sets the state of whether headers have been sent.
48-
*/
49-
public static function setHeadersSent(bool $sent): void
50-
{
51-
self::$headersSent = $sent;
52-
}
53-
54-
/**
55-
* Simulates PHP's native `headers_sent()` function.
56-
*/
57-
public static function headersSent(): bool
58-
{
59-
return self::$headersSent;
60-
}
61-
62-
/**
63-
* Sets a header by name, replacing or appending it.
64-
* This is the main method for header manipulation.
65-
*
66-
* @param string $header The header string (e.g., 'Content-Type: application/json').
67-
* @param bool $replace Whether to replace a previous similar header.
68-
* @param int|null $responseCode Forces the HTTP response code to the specified value.
69-
*/
70-
public static function set(string $header, bool $replace = true, ?int $responseCode = null): void
71-
{
72-
if (str_contains($header, ':')) {
73-
[$name, $value] = explode(':', $header, 2);
74-
$name = trim($name);
75-
$value = trim($value);
76-
77-
if ($replace || ! isset(self::$headers[strtolower($name)])) {
78-
self::$headers[strtolower($name)] = [];
79-
}
80-
self::$headers[strtolower($name)][] = "{$name}: {$value}";
81-
} else {
82-
// Handle non-key-value headers like "HTTP/1.1 404 Not Found"
83-
self::$headers['status'][] = $header;
84-
}
85-
86-
if ($responseCode !== null) {
87-
self::$responseCode = $responseCode;
88-
}
89-
}
90-
9123
/**
92-
* Pushes a header to the stack without replacing existing ones.
24+
* Simulates whether headers have been sent.
9325
*/
94-
public static function push(string $header): void
95-
{
96-
self::set($header, false);
97-
}
26+
public static bool $headersSent = false;
9827

9928
/**
100-
* A convenience method to push multiple headers at once.
29+
* Stores the list of headers.
10130
*
102-
* @param list<string> $headers An array of headers to push onto the stack.
31+
* @var list<string>
10332
*/
104-
public static function pushMany(array $headers): void
105-
{
106-
foreach ($headers as $header) {
107-
// Default to not replacing for multiple adds
108-
self::set($header, false);
109-
}
110-
}
33+
public static array $headers = [];
11134

11235
/**
113-
* Simulates PHP's `headers_list()` function.
114-
*
115-
* @return list<string> The list of simulated headers.
36+
* Resets the header stack to defaults.
37+
* Call this in setUp() to ensure clean state between tests.
11638
*/
117-
public static function listHeaders(): array
39+
public static function reset(): void
11840
{
119-
$list = [];
120-
121-
foreach (self::$headers as $values) {
122-
$list = array_merge($list, $values);
123-
}
124-
125-
return $list;
41+
self::$headersSent = false;
42+
self::$headers = [];
12643
}
12744

12845
/**
129-
* Checks if a header with the given name exists in the stack (case-insensitive).
46+
* Checks if a specific header exists in the stack.
13047
*
131-
* @param string $name The header name to search for (e.g., 'Content-Type').
48+
* @param string $header The exact header string (e.g., 'Content-Type: text/html')
13249
*/
133-
public static function hasHeader(string $name): bool
50+
public static function has(string $header): bool
13451
{
135-
return isset(self::$headers[strtolower($name)]);
52+
return in_array($header, self::$headers, true);
13653
}
13754

13855
/**
139-
* Simulates PHP's `http_response_code()` function.
56+
* Adds a header to the stack.
14057
*
141-
* @return int|null The stored response code, or null if not set.
58+
* @param string $header The header to add (e.g., 'Content-Type: text/html')
14259
*/
143-
public static function getResponseCode(): ?int
60+
public static function push(string $header): void
14461
{
145-
return self::$responseCode;
62+
self::$headers[] = $header;
14663
}
14764
}

tests/_support/Mock/MockNativeHeaders.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
*/
2626
function headers_sent(): bool
2727
{
28-
return NativeHeadersStack::headersSent();
28+
return NativeHeadersStack::$headersSent;
2929
}
3030

3131
/**
@@ -38,5 +38,5 @@ function headers_sent(): bool
3838
*/
3939
function headers_list(): array
4040
{
41-
return NativeHeadersStack::listHeaders();
41+
return NativeHeadersStack::$headers;
4242
}

tests/system/Debug/ToolbarTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public function testPrepareInjectsNormallyWithoutIgnoredHeader(): void
113113
public function testPrepareAbortsIfHeadersAlreadySent(): void
114114
{
115115
// Headers explicitly sent (e.g., echo before execution)
116-
NativeHeadersStack::setHeadersSent(true);
116+
NativeHeadersStack::$headersSent = true;
117117

118118
$this->request = service('incomingrequest', null, false);
119119
$this->response = service('response', null, false);
@@ -129,7 +129,7 @@ public function testPrepareAbortsIfHeadersAlreadySent(): void
129129
public function testPrepareAbortsIfNativeContentTypeIsNotHtml(): void
130130
{
131131
// A library (like Dompdf) set a PDF header directly
132-
NativeHeadersStack::set('Content-Type: application/pdf');
132+
NativeHeadersStack::push('Content-Type: application/pdf');
133133

134134
$this->request = service('incomingrequest', null, false);
135135
$this->response = service('response', null, false);
@@ -146,10 +146,10 @@ public function testPrepareAbortsIfNativeContentTypeIsNotHtml(): void
146146
public function testPrepareAbortsIfNativeContentDispositionIsAttachment(): void
147147
{
148148
// A file download (even if it is HTML)
149-
NativeHeadersStack::pushMany([
149+
NativeHeadersStack::$headers = [
150150
'Content-Type: text/html',
151151
'Content-Disposition: attachment; filename="report.html"',
152-
]);
152+
];
153153

154154
$this->request = service('incomingrequest', null, false);
155155
$this->response = service('response', null, false);
@@ -165,7 +165,7 @@ public function testPrepareAbortsIfNativeContentDispositionIsAttachment(): void
165165
public function testPrepareWorksWithNativeHtmlHeader(): void
166166
{
167167
// Standard scenario where PHP header is text/html
168-
NativeHeadersStack::set('Content-Type: text/html; charset=UTF-8');
168+
NativeHeadersStack::push('Content-Type: text/html; charset=UTF-8');
169169

170170
$this->request = service('incomingrequest', null, false);
171171
$this->response = service('response', null, false);

0 commit comments

Comments
 (0)