繁体   English   中英

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

[英]how to redirect STDOUT to a file in PHP?

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

ob_start();
echo 'xxx';
$contents = ob_get_contents();
ob_end_clean();
file_put_contents($file,$contents);

有更自然的方法吗?

可以在 PHP 中直接将 STDOUT 写入文件,这比使用输出缓冲更容易和直接。

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

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

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

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

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

echo "Hello world\n";

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

fwrite($STDERR, "Something went wrong\n"); 

请注意,当您更改输入、输出和错误描述符时,内置的 PHP 常量 STDIN、STDOUT 和 STDERR 将无法使用。 PHP 不会将这些常量更新为新的描述符,并且不允许重新定义这些常量(毕竟它们被称为常量是有原因的)。

这是一种转移 OUTPUT 的方法,这似乎是原始问题

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

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

ob_start('ob_file_callback');

更多信息在这里:

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

不,输出缓冲和它一样好。 虽然只是做稍微好一点

ob_start();
echo 'xxx';
$contents = ob_get_flush();
file_put_contents($file,$contents);

使用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");

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

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

没有一个答案适用于我的特定情况,我需要一种跨平台的方式在输出后立即重定向输出,以便我可以使用 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();

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

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

tail -f log.txt

这是一个丑陋的解决方案,对我遇到的问题(需要调试)很有用。

if(file_get_contents("out.txt") != "in progress")
{
    file_put_contents("out.txt","in progress");
    $content = file_get_contents('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
    file_put_contents("out.txt",$content);
}

这样做的主要缺点是您最好不要使用 $_POST 变量。 但你不必把它放在最开始的地方。

您可以安装 Eio 扩展

pecl 安装 eio

并复制文件描述符

$temp=fopen('/tmp/my_stdout','a');
$my_data='my something';
$foo=eio_dup2($temp,STDOUT,EIO_PRI_MAX,function($data,$esult,$request){
    var_dump($data,$esult,$request);
    var_dump(eio_get_last_error($request));
},$my_data);
eio_event_loop();
echo "something to stdout\n";
fclose($temp);

这将创建新的文件描述符并重写 STDOUT 的目标流

这也可以用 STDERR 来完成

和常量 STD[OUT|ERR] 仍然可用

我知道这个问题很古老,但是尝试做这个问题的人可能会在这里结束......你们两个。

如果您在特定环境下运行...

  • 在 Linux 下运行(可能大多数其他 Unix 操作系统,未经测试)
  • 通过 CLI 运行(未在 web 服务器上测试)

您实际上可以关闭所有文件描述符(是的,这意味着最好在执行开始时执行此操作......例如,在pcntl_fork()调用后台进程之后(看起来像最常见的需求是这样的)

fclose( STDIN );  // fd 3
fclose( STDERR);  // fd 2
fclose( STDOUT ); // fd 1

然后重新打开文件描述符,将它们分配给一个不会超出 scope 的变量,从而被垃圾回收 因为 Linux 可以预见地以正确的顺序打开它们。

$kept_in_scope_variable_fd1 = fopen(...); // fd 1
$kept_in_scope_variable_fd2 = fopen(...); // fd 2
$kept_in_scope_variable_fd3 = fopen( '/dev/null', ... ); // fd 3

您可以为此使用任何您想要的文件或设备。 我以 /dev/null 作为 STDIN (fd3) 的示例,因为这可能是此类代码的最常见情况。

完成此操作后,您应该能够执行正常操作,例如echoprint_rvar_dump等,而无需专门写入带有 function 的文件。当您尝试使用您不想使用的后台代码时,这很有用,或者不能,重写为文件指针输出友好。

YMMV 适用于其他环境以及打开其他 FD 等。我的建议是从一个小的测试脚本开始,以证明它在您的环境中是否有效,然后从那里继续进行集成。

祝你好运。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM