下面的代码几乎可以工作,但不是我真正想要的:
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');
为什么要在最开始这样做呢?因为此时不应该打开任何文件描述符,因为当关闭标准输入、输出和错误文件描述符时,前三个新的描述符将成为新的标准输入、输出和错误文件描述符。echo "Hello world\n";
要写入error.log,必须执行以下操作:
fwrite($STDERR, "Something went wrong\n");
请注意,当您更改输入、输出和错误描述符时,内置PHP常量STDIN、STDOUT和STDERR将变得无法使用。PHP不会将这些常量更新为新的描述符,并且不允许重新定义这些常量(毕竟它们被称为常量)。a
的fopen
函数,以便不会覆盖之前的日志。 - nullability这里有一种方法可以将输出转移,这似乎是最初的问题。
$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_file
设置为全局变量:$ob_file_callback = function($buffer) use ($ob_file) { fwrite($ob_file, $buffer); }; ob_start($ob_file_callback);
。 - Pascal Rosin在我的特定情况下,没有一个答案适用于我需要以跨平台的方式重定向输出,以便我可以使用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
ob_start(function($buffer) use($logFp){
” 这是什么神奇的操作?自从什么时候可以将函数作为参数传递了?这个 'use' 是什么意思?这种 PHP 语法从什么时候开始有效? - Lucob_start();
echo 'xxx';
$contents = ob_get_flush();
file_put_contents($file,$contents);
$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模块测试了这个例子。
并复制文件描述符。pecl install 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);
这将创建一个新的文件描述符并重定向标准输出流
同样可以对标准错误流进行操作
常量STD[OUT|ERR]仍然可用
pcntl_fork()
将进程后台化为守护进程之后立即进行(这似乎是这样做的最常见需求))。fclose( STDIN ); // fd 3
fclose( STDERR); // fd 2
fclose( STDOUT ); // fd 1
然后重新打开文件描述符,将它们分配给一个不会超出作用域而被垃圾回收的变量。因为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
这里有一个丑陋的解决方案,对我遇到的问题(需要调试)非常有用。
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 变量。 但你不必把它放在开头。