PHP:试图让 fgets() 在 CRLF、CR 和 LF 上触发

okd*_*wit 5 php newline proc-open fgets

我正在 PHP 中读取流,使用 proc_open 和 fgets($stdout),试图获取每一行。

许多 linux 程序(包管理器、wget、rsync)只使用 CR(回车)字符来表示定期“就地”更新的行,例如下载进度。我想在它们发生时立即捕获这些更新(作为单独的行)。

目前,fgets($stdout) 只会一直读取直到 LF,所以当进度非常缓慢时(例如大文件),它只会继续读取直到完全完成,然后将所有更新的行作为一个长字符串返回,包括CR。

我尝试设置“mac”选项以将 CR 检测为行尾:

ini_set('auto_detect_line_endings',true); 
Run Code Online (Sandbox Code Playgroud)

但这似乎不起作用。

现在,stream_get_line 将允许我将 CR 设置为换行符,但不是将 CRLF、CR 和 LF 都视为分隔符的“一网打尽”解决方案。

我当然可以阅读整行,使用各种 PHP 方法拆分它并用 LF 替换所有类型的换行符,但它是一个流,我希望 PHP 能够在它仍在运行时获得进度指示。

所以我的问题:

如何从 STDOUT 管道(从 proc_open)读取数据,直到发生 LFCR,而不必等到整行都输入?

提前致谢!

解决方案:

我使用 Fleshgrinder 的过滤器类将流中的 \r 替换为 \n(参见已接受的答案),并将 fgets() 替换为 fgetc() 以获得对 STDOUT 内容的更多“实时”访问:

$stdout = $proc->pipe(1);
stream_filter_register("EOL", "EOLStreamFilter");
stream_filter_append($stdout, "EOL"); 

while (($o = fgetc($stdout))!== false){
    $out .= $o;                            // buffer the characters into line, until \n.
    if ($o == "\n"){echo $out;$out='';}    // can now easily wrap the $out lines in JSON
}
Run Code Online (Sandbox Code Playgroud)

Fle*_*der 2

在使用流之前,使用流过滤器标准化新行字符。我创建了以下代码,该代码应该根据stream_filter_register.

\n\n

代码未经测试!

\n\n
<?php\n\n// https://php.net/php-user-filter\nfinal class EOLStreamFilter extends php_user_filter {\n\n    public function filter($in, $out, &$consumed, $closing)\n    {\n        while ($bucket = stream_bucket_make_writeable($in)) {\n            $bucket->data = str_replace([ "\\r\\n", "\\r" ], "\\n", $bucket->data);\n            $consumed += $bucket->datalen;\n            stream_bucket_append($out, $bucket);\n        }\n        return PSFS_PASS_ON;\n    }\n\n}\n\nstream_filter_register("EOL", "EOLStreamFilter");\n\n// Open stream \xe2\x80\xa6\n\nstream_filter_append($yourStreamHandle, "EOL");\n\n// Perform your work with normalized EOLs \xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

编辑:马克·贝克对你的问题发表的评论是正确的。大多数 Linux 发行版都使用行缓冲区STDOUT,Apple 可能也在做同样的事情。另一方面,大多数STDERR流都是无缓冲的。您可以尝试将程序的输出重定向到另一个管道(例如STDERR或任何其他管道),看看您是否更幸运。

\n