diff --git a/rector.php b/rector.php index 2b3c36597dae..2698aec9f098 100644 --- a/rector.php +++ b/rector.php @@ -100,6 +100,7 @@ // Exclude test file because `is_cli()` is mocked and Rector might remove needed parameters. RemoveExtraParametersRector::class => [ + __DIR__ . '/tests/system/CLI/CLITest.php', __DIR__ . '/tests/system/Debug/ToolbarTest.php', ], diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 08d5c214582e..314b6a934ee1 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -481,7 +481,7 @@ public static function error(string $text, string $foreground = 'light_red', ?st // Check color support for STDERR $stdout = static::$isColored; - static::$isColored = static::hasColorSupport(STDERR); + static::$isColored = is_cli() && static::hasColorSupport(STDERR); if ($foreground !== '' || (string) $background !== '') { $text = static::color($text, $foreground, $background); @@ -493,7 +493,7 @@ public static function error(string $text, string $foreground = 'light_red', ?st static::$lastWrite = 'write'; } - static::fwrite(STDERR, $text . PHP_EOL); + static::fwrite(is_cli() ? STDERR : STDOUT, $text . PHP_EOL); // return STDOUT color support static::$isColored = $stdout; @@ -572,7 +572,7 @@ public static function clearScreen() { // Unix systems, and Windows with VT100 Terminal support (i.e. Win10) // can handle CSI sequences. For lower than Win10 we just shove in 40 new lines. - is_windows() && ! static::streamSupports('sapi_windows_vt100_support', STDOUT) + is_windows() && is_cli() && ! static::streamSupports('sapi_windows_vt100_support', STDOUT) ? static::newLine(40) : static::fwrite(STDOUT, "\033[H\033[2J"); } diff --git a/system/CLI/SignalTrait.php b/system/CLI/SignalTrait.php index 792f7334f865..2bc69d51e93c 100644 --- a/system/CLI/SignalTrait.php +++ b/system/CLI/SignalTrait.php @@ -394,7 +394,7 @@ protected function getProcessState(): array if ($this->isPosixAvailable()) { $state['session_id'] = posix_getsid($pid); $state['process_group'] = posix_getpgid($pid); - $state['has_controlling_terminal'] = posix_isatty(STDIN); + $state['has_controlling_terminal'] = is_cli() && posix_isatty(STDIN); } return $state; diff --git a/tests/system/CLI/CLITest.php b/tests/system/CLI/CLITest.php index 62903f80ac64..9c697b25afb9 100644 --- a/tests/system/CLI/CLITest.php +++ b/tests/system/CLI/CLITest.php @@ -563,6 +563,32 @@ public function testError(): void $this->assertSame($expected, $this->getStreamFilterBuffer()); } + public function testErrorOutsideCliWritesToStdoutWithoutColor(): void + { + $io = new class () extends InputOutput { + /** + * @var list + */ + public array $writes = []; + + public function fwrite($handle, string $string): void + { + $this->writes[] = [$handle === STDERR ? 'STDERR' : 'STDOUT', $string]; + } + }; + CLI::setInputOutput($io); + + is_cli(false); + + try { + CLI::error('test'); + } finally { + is_cli(true); + } + + $this->assertSame([['STDOUT', "\ntest\n"]], $io->writes); + } + public function testMixedWriteError(): void { CLI::write('test 1'); diff --git a/user_guide_src/source/changelogs/v4.7.5.rst b/user_guide_src/source/changelogs/v4.7.5.rst index 34dc4dcf41eb..10e0d4e054a4 100644 --- a/user_guide_src/source/changelogs/v4.7.5.rst +++ b/user_guide_src/source/changelogs/v4.7.5.rst @@ -46,6 +46,8 @@ Bugs Fixed ANSI color codes in the prompt (e.g., option defaults) are wrapped in readline's non-printing markers under GNU readline so cursor positioning stays accurate. On Windows, where the ``readline`` extension is built on WinEditLine, the prompt is written to STDOUT first because WinEditLine reports no library version and prints ANSI sequences literally. - **CLI:** Fixed a bug where ``CLI::input()`` and ``CLI::prompt()`` threw a ``TypeError`` when STDIN reached end-of-file (e.g., Ctrl+D) with the ``readline`` extension enabled. An empty string is now returned, matching the behavior without ``readline``. +- **CLI:** Fixed a bug where ``CLI::error()`` threw an ``Error`` for the undefined ``STDERR`` constant when a command ran outside the CLI (e.g., through ``command()`` in a controller). The message is now written like other output. + ``CLI::clearScreen()`` on Windows and ``SignalTrait::getProcessState()`` no longer probe the standard streams outside the CLI either. - **CLIRequest:** Fixed a bug where ``parseCommand()`` could throw a TypeError when ``argv`` is missing. - **CodeIgniter:** Fixed a bug where ``gatherOutput()`` could be called twice when ``startController()`` returned a ``ResponseInterface`` (e.g., from filter attributes or closure routes). - **Commands:** Fixed a bug where ``make:migration --session`` silently generated a broken migration when the database group's driver is neither ``MySQLi`` nor ``Postgre``. The command now reports an error and returns ``EXIT_ERROR``. diff --git a/utils/phpstan-baseline/arguments.count.neon b/utils/phpstan-baseline/arguments.count.neon index 1df396472752..999a1d99310f 100644 --- a/utils/phpstan-baseline/arguments.count.neon +++ b/utils/phpstan-baseline/arguments.count.neon @@ -1,7 +1,12 @@ -# total 2 errors +# total 4 errors parameters: ignoreErrors: + - + message: '#^Function is_cli invoked with 1 parameter, 0 required\.$#' + count: 2 + path: ../../tests/system/CLI/CLITest.php + - message: '#^Function is_cli invoked with 1 parameter, 0 required\.$#' count: 2 diff --git a/utils/phpstan-baseline/loader.neon b/utils/phpstan-baseline/loader.neon index c00a0da66add..5223437222e3 100644 --- a/utils/phpstan-baseline/loader.neon +++ b/utils/phpstan-baseline/loader.neon @@ -1,4 +1,4 @@ -# total 12 errors +# total 14 errors includes: - arguments.count.neon