You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PHP调用CLI程序时透传输出并执行自定义函数(旧方法失效)

问题根因

  • 原有代码存在逻辑错误:仅在循环外执行了1次fgets()读取流内容,循环体内没有重新读取新的流数据,导致循环始终复用第一次读取的内容,出现第一行无限重复的问题。
  • yt-dlp会自动检测输出目标类型:当输出目标是交互式终端(如cmd.exe)时会实时刷新输出,当输出目标是管道(被PHP的popen调用时属于管道输出)时会默认开启全缓冲,且进度类输出默认用回车符\r而非换行符\n实现同屏刷新,而fgets()默认以\n作为读取结束标记,因此无法实时读取单行进度内容。
  • PHP 8.0.13的输出缓冲、流处理底层逻辑有细微调整,放大了上述问题的影响。

修复方案

第一步:调整yt-dlp调用参数

在原$yt_dlp_command中加入以下参数:
--progress-template "%(progress._percent_str)s %(progress._eta_str)s" --progress-newline --quiet --no-colors
以上参数强制yt-dlp用换行输出进度、关闭颜色转义符、使用固定模板输出进度内容,避免非TTY场景下的缓冲问题。

第二步:修复PHP读取逻辑

修复后的代码如下:

// 关闭PHP顶层输出缓冲,避免多层缓冲导致无法实时输出
while (ob_get_level() > 0) {
    ob_end_clean();
}

$a = popen($yt_dlp_command, 'r');
if ($a) {
    // 设置流为非阻塞模式,避免fread卡住
    stream_set_blocking($a, false);
    
    while (!feof($a)) {
        // 每次读取任意长度的可用内容,不等待换行符
        $row_data = fread($a, 1024);
        if ($row_data !== false && $row_data !== '') {
            // 在这里执行你的自定义函数
            my_function();
            echo $row_data;
            // 强制刷新输出缓冲
            flush();
        }
        // 避免CPU空转,可根据需求调整等待时长
        usleep(100000);
    }
    pclose($a);
}

可选兼容方案

如果上述方案仍不生效,可通过proc_open替代popen,显式指定输出为PTY(伪终端),模拟交互式终端环境,让yt-dlp按照终端场景的逻辑输出内容:

$descriptorspec = [
    0 => ['pty'], // 标准输入为伪终端
    1 => ['pty'], // 标准输出为伪终端
    2 => ['pty']  // 错误输出为伪终端
];

$process = proc_open($yt_dlp_command, $descriptorspec, $pipes);
if (is_resource($process)) {
    stream_set_blocking($pipes[1], false);
    while (!feof($pipes[1])) {
        $row_data = fread($pipes[1], 1024);
        if ($row_data) {
            my_function();
            echo $row_data;
            flush();
        }
        usleep(100000);
    }
    proc_close($process);
}

内容的提问来源于stack exchange,提问作者user17535142

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 19:06:01