如何将STDOUT重定向到PHP中的文件?

omg*_*omg 53 php stdin stdout

下面的代码几乎可以工作,但这不是我的意思:

ob_start();
echo 'xxx';
$contents = ob_get_contents();
ob_end_clean();
file_put_contents($file,$contents);
Run Code Online (Sandbox Code Playgroud)

有更自然的方式吗?

Bas*_*ers 123

可以将STDOUT直接写入PHP中的文件,这比使用输出bufferering更容易,更简单.

在脚本的最开头执行此操作:

fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
$STDIN = fopen('/dev/null', 'r');
$STDOUT = fopen('application.log', 'wb');
$STDERR = fopen('error.log', 'wb');
Run Code Online (Sandbox Code Playgroud)

为什么一开始你可能会问?不应该打开文件描述符,因为当您关闭标准输入,输出和错误文件描述符时,前三个新描述符将成为新的标准输入,输出和错误文件描述符.

在我的示例中,我将标准输入重定向到/ dev/null,将输出和错误文件描述符重定向到日志文件.在PHP中创建守护程序脚本时,这是常见的做法.

要写入application.log文件,这就足够了:

echo "Hello world\n";
Run Code Online (Sandbox Code Playgroud)

要写入error.log,必须执行以下操作:

fwrite($STDERR, "Something went wrong\n"); 
Run Code Online (Sandbox Code Playgroud)

请注意,当你改变输入,输出和错误的描述,集结在PHP常量STDIN,STDOUT和STDERR将变得毫无用处.PHP将不会更新这些常量到新的描述,它是不允许重新定义这些常量(它们被称为常量是有原因的毕竟).

  • 我很惊讶,有点害怕这是有效的. (24认同)
  • 好悲伤.这完全是出于好运.这不是解决方案. (11认同)
  • 它是基本的Unix行为(这也是它不能移植到Windows的原因).Unix提供文件可用的最小文件描述符.鉴于fcloses接近0,1和2,它们可用于接下来的3次开放.Unix的简单性也很棒. (6认同)
  • 这不适用于Windows(无论如何都不适用于5.3.8). (4认同)
  • 此功能在记录[php.net](http://php.net/manual/en/features.commandline.io-streams.php) (2认同)
  • 这不适用于Apache +`mod_php`. (2认同)
  • 如果这与cron脚本一起使用,你可能希望使用`fopen`和模式`a`,这样你之前的日志就不会被覆盖. (2认同)
  • @Pacerier,确切地说.如何命名变量并不重要.为了便于阅读,我只是将它们与它们的等价物保持一致.唯一非常重要的是关闭默认描述符并打开新描述符的顺序. (2认同)

use*_*140 16

这是一种转移OUTPUT的方法,这似乎是最初的问题

$ob_file = fopen('test.txt','w');

function ob_file_callback($buffer)
{
  global $ob_file;
  fwrite($ob_file,$buffer);
}

ob_start('ob_file_callback');
Run Code Online (Sandbox Code Playgroud)

更多信息:

http://my.opera.com/zomg/blog/2007/10/03/how-to-easily-redirect-php-output-to-a-file

  • 如果您使用的是PHP 5.3或更高版本,我建议使用闭包函数而不需要将`$ ob_file`设置为全局:`$ ob_file_callback = function($ buffer)use($ ob_file){fwrite($ ob_file, $缓存); }; ob_start($ ob_file_callback);` (7认同)

cha*_*aos 9

不,输出缓冲是最好的.虽然它只是稍微好一些

ob_start();
echo 'xxx';
$contents = ob_get_flush();
file_put_contents($file,$contents);
Run Code Online (Sandbox Code Playgroud)

  • ob_get_flush()还没有将缓冲区内容发送到浏览器?也许ob_get_clean()会更好. (3认同)
  • 有没有办法也可以得到STDERR? (2认同)

小智 6

使用eio pecl 模块eio 非常简单,还可以捕获PHP 内部错误、var_dump、echo 等。在这段代码中,您可以找到一些不同情况的示例。

$fdout = fopen('/tmp/stdout.log', 'wb');
$fderr = fopen('/tmp/stderr.log', 'wb');

eio_dup2($fdout, STDOUT);
eio_dup2($fderr, STDERR);
eio_event_loop();

fclose($fdout);
fclose($fderr);

// output examples
echo "message to stdout\n";

$v2dump = array(10, "graphinux");
var_dump($v2dump);

// php internal error/warning
$div0 = 10/0;

// user errors messages
fwrite(STDERR, "user controlled error\n");
Run Code Online (Sandbox Code Playgroud)

调用 eio_event_loop 用于确保先前的 eio 请求已被处理。如果您需要在日志上附加,在 fopen 调用时,请使用模式 'ab' 而不是 'wb'。

安装 eio 模块非常简单(http://php.net/manual/es/eio.installation.php)。我用 1.2.6 版的 eio 模块测试了这个例子。


Dan*_*ães 6

没有一个答案适用于我的特定情况,我需要一种跨平台的方式在输出后立即重定向输出,以便我可以使用 tail -f log.txt 或其他日志查看应用程序跟踪日志。我想出了以下解决方案:

$logFp = fopen('log.txt', 'w');

ob_start(function($buffer) use($logFp){
    fwrite($logFp, $buffer);
}, 1); //notice the use of chunk_size == 1

echo "first output\n";
sleep(10)
echo "second output\n";

ob_end_clean();
Run Code Online (Sandbox Code Playgroud)

我没有注意到任何性能问题,但如果你注意到了,你可以将 chunk_size 更改为更大的值。

现在只需 tail -f 日志文件:

tail -f log.txt
Run Code Online (Sandbox Code Playgroud)