Skip to content
Merged
1 change: 0 additions & 1 deletion .styleci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ enabled:
- declare_strict_types
- dir_constant
- empty_loop_body_braces
- function_to_constant
- hash_to_slash_comment
- integer_literal_case
- is_null
Expand Down
2 changes: 1 addition & 1 deletion src/http/ErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@
{
$currentLevel = ob_get_level();

$minLevel = YII_ENV_TEST ? 1 : 0;

Check warning on line 88 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "IncrementInteger": @@ @@ public function clearOutput(): void { $currentLevel = ob_get_level(); - $minLevel = YII_ENV_TEST ? 1 : 0; + $minLevel = YII_ENV_TEST ? 1 : 1; while ($currentLevel > $minLevel) { if (@ob_end_clean() === false) { // @codeCoverageIgnoreStart

Check warning on line 88 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "DecrementInteger": @@ @@ public function clearOutput(): void { $currentLevel = ob_get_level(); - $minLevel = YII_ENV_TEST ? 1 : 0; + $minLevel = YII_ENV_TEST ? 1 : -1; while ($currentLevel > $minLevel) { if (@ob_end_clean() === false) { // @codeCoverageIgnoreStart

Check warning on line 88 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "DecrementInteger": @@ @@ public function clearOutput(): void { $currentLevel = ob_get_level(); - $minLevel = YII_ENV_TEST ? 1 : 0; + $minLevel = YII_ENV_TEST ? 1 : -1; while ($currentLevel > $minLevel) { if (@ob_end_clean() === false) { // @codeCoverageIgnoreStart

Check warning on line 88 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "IncrementInteger": @@ @@ public function clearOutput(): void { $currentLevel = ob_get_level(); - $minLevel = YII_ENV_TEST ? 1 : 0; + $minLevel = YII_ENV_TEST ? 1 : 1; while ($currentLevel > $minLevel) { if (@ob_end_clean() === false) { // @codeCoverageIgnoreStart

while ($currentLevel > $minLevel) {
if (@ob_end_clean() === false) {

Check warning on line 91 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "Identical": @@ @@ $currentLevel = ob_get_level(); $minLevel = YII_ENV_TEST ? 1 : 0; while ($currentLevel > $minLevel) { - if (@ob_end_clean() === false) { + if (@ob_end_clean() !== false) { // @codeCoverageIgnoreStart ob_clean(); // @codeCoverageIgnoreEnd

Check warning on line 91 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "Identical": @@ @@ $currentLevel = ob_get_level(); $minLevel = YII_ENV_TEST ? 1 : 0; while ($currentLevel > $minLevel) { - if (@ob_end_clean() === false) { + if (@ob_end_clean() !== false) { // @codeCoverageIgnoreStart ob_clean(); // @codeCoverageIgnoreEnd
// @codeCoverageIgnoreStart
ob_clean();
// @codeCoverageIgnoreEnd
Expand Down Expand Up @@ -117,23 +117,23 @@
{
$this->exception = $exception;

$this->unregister();

Check warning on line 120 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ public function handleException($exception): Response { $this->exception = $exception; - $this->unregister(); + if (php_sapi_name() !== 'cli') { $statusCode = 500; if ($exception instanceof HttpException) {

Check warning on line 120 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ public function handleException($exception): Response { $this->exception = $exception; - $this->unregister(); + if (php_sapi_name() !== 'cli') { $statusCode = 500; if ($exception instanceof HttpException) {

if (PHP_SAPI !== 'cli') {
if (php_sapi_name() !== 'cli') {

Check warning on line 122 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "NotIdentical": @@ @@ { $this->exception = $exception; $this->unregister(); - if (php_sapi_name() !== 'cli') { + if (php_sapi_name() === 'cli') { $statusCode = 500; if ($exception instanceof HttpException) { $statusCode = $exception->statusCode;

Check warning on line 122 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "NotIdentical": @@ @@ { $this->exception = $exception; $this->unregister(); - if (php_sapi_name() !== 'cli') { + if (php_sapi_name() === 'cli') { $statusCode = 500; if ($exception instanceof HttpException) { $statusCode = $exception->statusCode;
$statusCode = 500;

Check warning on line 123 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "IncrementInteger": @@ @@ $this->exception = $exception; $this->unregister(); if (php_sapi_name() !== 'cli') { - $statusCode = 500; + $statusCode = 501; if ($exception instanceof HttpException) { $statusCode = $exception->statusCode; }

Check warning on line 123 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "DecrementInteger": @@ @@ $this->exception = $exception; $this->unregister(); if (php_sapi_name() !== 'cli') { - $statusCode = 500; + $statusCode = 499; if ($exception instanceof HttpException) { $statusCode = $exception->statusCode; }

Check warning on line 123 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "IncrementInteger": @@ @@ $this->exception = $exception; $this->unregister(); if (php_sapi_name() !== 'cli') { - $statusCode = 500; + $statusCode = 501; if ($exception instanceof HttpException) { $statusCode = $exception->statusCode; }

Check warning on line 123 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "DecrementInteger": @@ @@ $this->exception = $exception; $this->unregister(); if (php_sapi_name() !== 'cli') { - $statusCode = 500; + $statusCode = 499; if ($exception instanceof HttpException) { $statusCode = $exception->statusCode; }

if ($exception instanceof HttpException) {
$statusCode = $exception->statusCode;
}

http_response_code($statusCode);

Check warning on line 129 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "FunctionCallRemoval": @@ @@ if ($exception instanceof HttpException) { $statusCode = $exception->statusCode; } - http_response_code($statusCode); + } try { $this->logException($exception);

Check warning on line 129 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "FunctionCallRemoval": @@ @@ if ($exception instanceof HttpException) { $statusCode = $exception->statusCode; } - http_response_code($statusCode); + } try { $this->logException($exception);
}

try {
$this->logException($exception);

Check warning on line 133 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ http_response_code($statusCode); } try { - $this->logException($exception); + if ($this->discardExistingOutput) { $this->clearOutput(); }

Check warning on line 133 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ http_response_code($statusCode); } try { - $this->logException($exception); + if ($this->discardExistingOutput) { $this->clearOutput(); }

if ($this->discardExistingOutput) {
$this->clearOutput();

Check warning on line 136 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ try { $this->logException($exception); if ($this->discardExistingOutput) { - $this->clearOutput(); + } $response = $this->renderException($exception); } catch (Throwable $e) {

Check warning on line 136 in src/http/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ try { $this->logException($exception); if ($this->discardExistingOutput) { - $this->clearOutput(); + } $response = $this->renderException($exception); } catch (Throwable $e) {
}

$response = $this->renderException($exception);
Expand Down
20 changes: 19 additions & 1 deletion tests/http/ErrorHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,27 @@
namespace yii2\extensions\psrbridge\tests\http;

use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\TestWith;
use RuntimeException;
use Throwable;
use yii\base\{Exception, UserException};
use yii\web\HttpException;
use yii2\extensions\psrbridge\http\{ErrorHandler, Response};
use yii2\extensions\psrbridge\tests\support\stub\HTTPFunctions;
use yii2\extensions\psrbridge\tests\TestCase;

use function str_repeat;

#[Group('http')]
final class ErrorHandlerTest extends TestCase
{
protected function tearDown(): void
{
HTTPFunctions::reset();

parent::tearDown();
}

public function testHandleExceptionResetsState(): void
{
$errorHandler = new ErrorHandler();
Expand Down Expand Up @@ -97,8 +106,12 @@ public function testHandleExceptionWithGenericException(): void
);
}

public function testHandleExceptionWithHttpException(): void
#[TestWith(['apache'])]
#[TestWith(['cli'])]
public function testHandleExceptionWithHttpException(string $sapi): void
{
HTTPFunctions::set_sapi($sapi);

$errorHandler = new ErrorHandler();

$errorHandler->discardExistingOutput = false;
Expand All @@ -116,6 +129,11 @@ public function testHandleExceptionWithHttpException(): void
$response->data,
'Should set response data for HTTP exception.',
);
self::assertSame(
$sapi,
HTTPFunctions::php_sapi_name(),
"Should return correct SAPI name '{$sapi}' for 'HttpException'.",
);
}

public function testHandleExceptionWithLongMessage(): void
Expand Down
5 changes: 5 additions & 0 deletions tests/support/MockerExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public static function load(): void
$line,
),
],
[
'namespace' => 'yii2\extensions\psrbridge\http',
'name' => 'php_sapi_name',
'function' => static fn(): string => HTTPFunctions::php_sapi_name(),
],
[
'namespace' => 'yii2\extensions\psrbridge\adapter',
'name' => 'stream_get_contents',
Expand Down
27 changes: 22 additions & 5 deletions tests/support/stub/HTTPFunctions.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@
* Provides controlled replacements for core PHP HTTP header and response functions to facilitate testing of HTTP
* emitter and response-related code without actual header output or side effects.
*
* This class allows tests to simulate and inspect HTTP header operations, response codes, and output flushing by
* maintaining internal state and exposing methods to manipulate and query that state.
* This class allows tests to simulate and inspect HTTP header operations, response codes, output flushing,
* and SAPI name retrieval by maintaining internal state and exposing methods to manipulate and query that state.
*
* It enables validation of emitter logic, header management, and response code handling in isolation from PHP's global
* state.
* It enables validation of emitter logic, header management, response code handling, and SAPI simulation in isolation
* from PHP global state.
*
* Key features.
* - Complete simulation of {@see \header()}, {@see \headers_sent()}, {@see \header_remove()}, {@see \headers_list()},
* and {@see \http_response_code()}
* {@see \http_response_code()}, and {@see php_sapi_name()} for test reliability.
* - Consistent behavior matching PHP's native functions for test reliability.
* - File and line tracking for headers_sent simulation.
* - Header inspection and manipulation for assertions.
* - Simulated output flushing and flush count tracking.
* - SAPI name simulation for emitter and environment testing.
* - State reset capability for test isolation and repeatability.
*
* @copyright Copyright (C) 2025 Terabytesoftw.
Expand Down Expand Up @@ -67,6 +68,11 @@ final class HTTPFunctions
*/
private static int $responseCode = 200;

/**
* Tracks the current SAPI name for simulation.
*/
private static string $sapi = 'cli';

/**
* Controls whether stream_get_contents should fail.
*/
Expand Down Expand Up @@ -154,6 +160,11 @@ public static function http_response_code(int|null $response_code = 0): int
return self::$responseCode;
}

public static function php_sapi_name(): string
{
return self::$sapi;
}

public static function reset(): void
{
self::$flushedTimes = 0;
Expand All @@ -162,6 +173,7 @@ public static function reset(): void
self::$headersSentFile = '';
self::$headersSentLine = 0;
self::$responseCode = 200;
self::$sapi = 'cli';
self::$streamGetContentsShouldFail = false;
}

Expand All @@ -172,6 +184,11 @@ public static function set_headers_sent(bool $value = false, string $file = '',
self::$headersSentLine = $line;
}

public static function set_sapi(string $sapi): void
{
self::$sapi = $sapi;
}

public static function set_stream_get_contents_should_fail(bool $shouldFail = true): void
{
self::$streamGetContentsShouldFail = $shouldFail;
Expand Down
Loading