2323use PHPUnit \Framework \Attributes \BackupGlobals ;
2424use PHPUnit \Framework \Attributes \Group ;
2525
26+ require_once SUPPORTPATH . 'Debug/MockNativeHeaders.php ' ;
27+
2628/**
2729 * @internal
2830 */
@@ -37,6 +39,9 @@ final class ToolbarTest extends CIUnitTestCase
3739 protected function setUp (): void
3840 {
3941 parent ::setUp ();
42+
43+ MockNativeHeaders::reset ();
44+
4045 Services::reset ();
4146
4247 is_cli (false );
@@ -99,4 +104,76 @@ public function testPrepareInjectsNormallyWithoutIgnoredHeader(): void
99104 // Assertions
100105 $ this ->assertStringContainsString ('id="debugbar_loader" ' , (string ) $ this ->response ->getBody ());
101106 }
107+
108+ // -------------------------------------------------------------------------
109+ // Native Header Conflicts
110+ // -------------------------------------------------------------------------
111+
112+ public function testPrepareAbortsIfHeadersAlreadySent (): void
113+ {
114+ // Headers explicitly sent (e.g., echo before execution)
115+ MockNativeHeaders::$ headersSent = true ;
116+
117+ $ this ->request = service ('incomingrequest ' , null , false );
118+ $ this ->response = service ('response ' , null , false );
119+ $ this ->response ->setBody ('<html><body>Content</body></html> ' );
120+
121+ $ toolbar = new Toolbar ($ this ->config );
122+ $ toolbar ->prepare ($ this ->request , $ this ->response );
123+
124+ // Must NOT inject because we can't modify the body safely
125+ $ this ->assertStringNotContainsString ('id="debugbar_loader" ' , (string ) $ this ->response ->getBody ());
126+ }
127+
128+ public function testPrepareAbortsIfNativeContentTypeIsNotHtml (): void
129+ {
130+ // A library (like Dompdf) set a PDF header directly
131+ MockNativeHeaders::$ headers = ['Content-Type: application/pdf ' ];
132+
133+ $ this ->request = service ('incomingrequest ' , null , false );
134+ $ this ->response = service ('response ' , null , false );
135+ // Even if the body looks like HTML (before rendering), the header says PDF
136+ $ this ->response ->setBody ('<html><body>Raw PDF Data</body></html> ' );
137+
138+ $ toolbar = new Toolbar ($ this ->config );
139+ $ toolbar ->prepare ($ this ->request , $ this ->response );
140+
141+ // Must NOT inject into non-HTML content
142+ $ this ->assertStringNotContainsString ('id="debugbar_loader" ' , (string ) $ this ->response ->getBody ());
143+ }
144+
145+ public function testPrepareAbortsIfNativeContentDispositionIsAttachment (): void
146+ {
147+ // A file download (even if it is HTML)
148+ MockNativeHeaders::$ headers = [
149+ 'Content-Type: text/html ' ,
150+ 'Content-Disposition: attachment; filename="report.html" ' ,
151+ ];
152+
153+ $ this ->request = service ('incomingrequest ' , null , false );
154+ $ this ->response = service ('response ' , null , false );
155+ $ this ->response ->setBody ('<html><body>Downloadable Report</body></html> ' );
156+
157+ $ toolbar = new Toolbar ($ this ->config );
158+ $ toolbar ->prepare ($ this ->request , $ this ->response );
159+
160+ // Must NOT inject into downloads
161+ $ this ->assertStringNotContainsString ('id="debugbar_loader" ' , (string ) $ this ->response ->getBody ());
162+ }
163+
164+ public function testPrepareWorksWithNativeHtmlHeader (): void
165+ {
166+ // Standard scenario where PHP header is text/html
167+ MockNativeHeaders::$ headers = ['Content-Type: text/html; charset=UTF-8 ' ];
168+
169+ $ this ->request = service ('incomingrequest ' , null , false );
170+ $ this ->response = service ('response ' , null , false );
171+ $ this ->response ->setBody ('<html><body>Valid Page</body></html> ' );
172+
173+ $ toolbar = new Toolbar ($ this ->config );
174+ $ toolbar ->prepare ($ this ->request , $ this ->response );
175+
176+ // Should inject normally
177+ $ this ->assertStringContainsString ('id="debugbar_loader" ' , (string ) $ this ->response ->getBody ());
178+ }
102179}
0 commit comments