【问题标题】:how to redirect STDOUT to a file in PHP?如何将 STDOUT 重定向到 PHP 中的文件?
【发布时间】:2010-10-30 13:45:06
【问题描述】:

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

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

还有更自然的方法吗?

【问题讨论】:

  • 你真的应该在这里接受一个答案。 Bas Peters 为您提供了完美的解决方案。
  • 示例代码将 OUTPUT 重定向到文件,而不是 STDOUT,Bas 的解决方案似乎只能工作,因为 CLI(以及较小范围的 CGI)环境可互换地使用这些流。 apache-module PHP 没有。

标签: php stdout stdin


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    使用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 模块测试了这个例子。

    【讨论】:

      【解决方案3】:

      你可以安装 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] 仍然可用

      【讨论】:

        【解决方案4】:

        可以在 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 不会将这些常量更新为新的描述符,也不允许重新定义这些常量(毕竟它们被称为常量是有原因的)。

        【讨论】:

        • 此功能记录在php.net
        • 这是基本的 Unix 行为(这也是它不能移植到 Windows 的原因)。 Unix 提供文件可用的最小文件描述符。鉴于 fclose 关闭 0、1 和 2,它们可用于下一个 3 打开。 Unix 的简单性也很棒。
        • 如果这与 cron 脚本一起使用,您可能希望将 fopen 与模式 a 一起使用,这样您之前的日志就不会被覆盖。
        • 好悲痛。这完全是运气。这不是解决方案。
        • @Pacerier,没错。如何命名变量并不重要。为了可读性,我只是将它们与常量等价物保持一致。唯一真正重要的是关闭默认描述符和打开新描述符的顺序。
        【解决方案5】:

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

        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 变量。 但你不必把它放在最开始。

        【讨论】:

          【解决方案6】:

          这是一种转移 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

          【讨论】:

          • 如果您使用的是 PHP 5.3 或更新版本,我建议使用闭包函数,而无需将 $ob_file 设为全局:$ob_file_callback = function($buffer) use ($ob_file) { fwrite($ob_file, $buffer); }; ob_start($ob_file_callback);
          • 这对我有用,在我看来应该是公认的答案
          • @user340140 如果你知道如何重定向标准输入,请分享。
          【解决方案7】:

          不,输出缓冲是最好的。虽然这样做会更好

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

          【讨论】:

          • ob_get_flush() 不也将缓冲区内容发送到浏览器吗?也许 ob_get_clean() 会更好。
          • 有没有办法同时获得 STDERR?
          猜你喜欢
          • 2014-07-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-08
          • 1970-01-01
          • 2011-11-23
          • 2017-08-05
          • 2016-02-19
          相关资源
          最近更新 更多