You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel调用fwrite输出特定字符串时应用崩溃问题

问题:Laravel自定义日志函数在特定场景下崩溃

问题重现

在Laravel应用中使用自定义助手函数向服务器端输出日志,正常场景下运行正常,但在中间件中终止请求时,输出字符串" URI:"时应用直接崩溃,且该字符串未被打印。

自定义日志函数代码

if(!defined('STDIN'))  define('STDIN',  fopen('php://stdin',  'rb'));
if(!defined('STDOUT')) define('STDOUT', fopen('php://stdout', 'wb'));
if(!defined('STDERR')) define('STDERR', fopen('php://stderr', 'wb'));

class Funcoes{
 public static function consolelog(...$args): void{
    foreach ($args as $arg) {
        if (is_object($arg) || is_array($arg) || is_resource($arg)) {
            $output = print_r($arg, true);
        } else {
            $output = (string) $arg;
        }

        fwrite(STDOUT, $output . "\n");
     }
 }
}

中间件中触发崩溃的代码

Funcoes::consolelog("Requisição inválida, headers:\n");
Funcoes::consolelog(getallheaders());
Funcoes::consolelog(" Remote addr:");
Funcoes::consolelog($_SERVER['REMOTE_ADDR']);
Funcoes::consolelog(" URI:"); // 此处崩溃,未打印字符串
Funcoes::consolelog($_SERVER['REQUEST_URI']);
abort(400);

错误信息

在ServeCommand.php第381行:
Undefined array key 1

PHP版本信息

$ php -v
PHP 8.3.11 (cli) (built: Aug 28 2024 22:31:38) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.11, Copyright (c) Zend Technologies
    with Xdebug v3.3.2, Copyright (c) 2002-2024, by Derick Rethans

完整栈追踪

[2024-09-21 20:52:46] local.ERROR: Undefined array key 1 {"exception":"[object] (ErrorException(code: 0): Undefined array key 1 at /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Foundation/Console/ServeCommand.php:381)
[stacktrace]
#0 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php(256): Illuminate\\Foundation\\Bootstrap\\HandleExceptions->handleError()
#1 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Foundation/Console/ServeCommand.php(381): Illuminate\\Foundation\\Bootstrap\\HandleExceptions->Illuminate\\Foundation\\Bootstrap\\{closure}()
#2 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Foundation/Console/ServeCommand.php(306): Illuminate\\Foundation\\Console\\ServeCommand->getRequestPortFromLine()
#3 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php(257): Illuminate\\Foundation\\Console\\ServeCommand->Illuminate\\Foundation\\Console\\{closure}()
#4 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Foundation/Console/ServeCommand.php(279): Illuminate\\Support\\Collection->each()
#5 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Foundation/Console/ServeCommand.php(261): Illuminate\\Foundation\\Console\\ServeCommand->flushOutputBuffer()
#6 /home/user/src/LaravelProj/vendor/symfony/process/Process.php(1306): Illuminate\\Foundation\\Console\\ServeCommand->Illuminate\\Foundation\\Console\\{closure}()
#7 /home/user/src/LaravelProj/vendor/symfony/process/Process.php(1417): Symfony\\Component\\Process\\Process->Symfony\\Component\\Process\\{closure}()
#8 /home/user/src/LaravelProj/vendor/symfony/process/Process.php(1337): Symfony\\Component\\Process\\Process->readPipes()
#9 /home/user/src/LaravelProj/vendor/symfony/process/Process.php(860): Symfony\\Component\\Process\\Process->updateStatus()
#10 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Foundation/Console/ServeCommand.php(105): Symfony\\Component\\Process\\Process->isRunning()
#11 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): Illuminate\\Foundation\\Console\\ServeCommand->handle()
#12 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Container/Util.php(43): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#13 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(95): Illuminate\\Container\\Util::unwrapIfClosure()
#14 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(35): Illuminate\\Container\\BoundMethod::callBoundMethod()
#15 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Container/Container.php(690): Illuminate\\Container\\BoundMethod::call()
#16 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Console/Command.php(213): Illuminate\\Container\\Container->call()
#17 /home/user/src/LaravelProj/vendor/symfony/console/Command/Command.php(279): Illuminate\\Console\\Command->execute()
#18 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Console/Command.php(182): Symfony\\Component\\Console\\Command\\Command->run()
#19 /home/user/src/LaravelProj/vendor/symfony/console/Application.php(1045): Illuminate\\Console\\Command->run()
#20 /home/user/src/LaravelProj/vendor/symfony/console/Application.php(316): Symfony\\Component\\Console\\Application->doRunCommand()
#21 /home/user/src/LaravelProj/vendor/symfony/console/Application.php(167): Symfony\\Component\\Console\\Application->doRun()
#22 /home/user/src/LaravelProj/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(197): Symfony\\Component\\Console\\Application->run()
#23 /home/user/src/LaravelProj/artisan(33): Illuminate\\Foundation\\Console\\Kernel->handle()
#24 {main}

原因分析

Laravel的serve命令(即ServeCommand类)会监听PHP内置服务器的输出,尝试解析日志行中的请求端口(比如[Thu Sep 21 20:52:46 2024] 127.0.0.1:54321 Accepted这种格式)。而自定义函数直接往STDOUT写入内容,当输出的行不符合内置服务器的日志格式时,getRequestPortFromLine方法会尝试分割字符串并取数组索引1,导致数组越界错误。

解决方案

方案1:改用Laravel内置日志系统

放弃直接写STDOUT,使用Laravel官方提供的Log门面,日志会写入项目的日志文件,不会干扰serve命令的输出解析:

use Illuminate\Support\Facades\Log;

Log::info("Requisição inválida, headers:", getallheaders());
Log::info("Remote addr: {$_SERVER['REMOTE_ADDR']}");
Log::info("URI: {$_SERVER['REQUEST_URI']}");

方案2:修改自定义函数写入STDERR

将输出目标从STDOUT改为STDERR,因为serve命令不会解析STDERR的输出,这样既可以在控制台看到日志,又不会触发错误:

class Funcoes{
 public static function consolelog(...$args): void{
    foreach ($args as $arg) {
        if (is_object($arg) || is_array($arg) || is_resource($arg)) {
            $output = print_r($arg, true);
        } else {
            $output = (string) $arg;
        }

        fwrite(STDERR, $output . "\n"); // 替换为STDERR
     }
 }
}

方案3:生产环境避免使用serve命令

生产环境通常使用Nginx或Apache作为Web服务器,此时直接往STDOUT写内容不会有这个问题,因为没有serve命令的日志解析逻辑。

内容的提问来源于stack exchange,提问作者Fnr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 06:07:03