话题虽然是关于 AI 的,但解决的问题还是关于订阅。

问题的来源:

我这个订阅功能是基于一个开源的rssphp 库而写出来的,时至今日个人感觉基本趋于稳定,可唯独因为不能读取品味苏州的订阅算是一个小小的遗憾。而且,我苦这个问题久矣!

晚上,我将代码进行了一下复盘,问题回到这个库上面,然后通过设置一个一个的校验代码,最终将问题定位到了下面这个函数

private static function httpRequest($url, $user, $pass)
{
    if (extension_loaded('curl')) {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        if ($user !== null || $pass !== null) {
            curl_setopt($curl, CURLOPT_USERPWD, "$user:$pass");
        }
        curl_setopt($curl, CURLOPT_USERAGENT, self::$userAgent); // some feeds require a user agent
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_TIMEOUT, 20);
        curl_setopt($curl, CURLOPT_ENCODING, '');
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // no echo, just return result
        curl_setopt($curl, CURLOPT_USERAGENT, '');
        if (!ini_get('open_basedir')) {
            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // sometime is useful :)
        }
        $result = curl_exec($curl);
        return curl_errno($curl) === 0 && curl_getinfo($curl, CURLINFO_HTTP_CODE) === 200
            ? $result
            : false;

    } else {
        $context = null;
        if ($user !== null && $pass !== null) {
            $options = [
                'http' => [
                    'method' => 'GET',
                    'header' => 'Authorization: Basic ' . base64_encode($user . ':' . $pass) . "\r\n",
                ],
            ];
            $context = stream_context_create($options);
        }

        return file_get_contents($url, false, $context);
    }
}

AI 帮帮忙

看着这眼花缭乱的条件,我短路了,怎么办?于是也效仿大家,用腾讯元宝进行了提问:“请检查函数有没有错误”

元宝经过一番深度思考(就不贴出来了),然后给出了回复。

AI 之答

该函数存在几个潜在问题,以下是主要错误及改进建议:

1.​重复设置 User-Agent

cURL 部分两次设置 CURLOPT_USERAGENT,第二次覆盖为空字符串,导致 User-Agent 头被清空。

修复:​ 移除第二个 curl_setopt($curl, CURLOPT_USERAGENT, '');。

2.​HTTP 认证条件不一致

  • cURL 使用 $user !== null || $pass !== null,允许单个参数为 null,可能导致认证头错误(如 "user:")。
  • file_get_contents 使用 $user !== null && $pass !== null,要求两者均非空。

​修复:​ 统一条件为两者均非空(&&),确保认证正确性。

3.​HTTP 状态码检查过严

cURL 仅接受 200 状态码,忽略了其他成功状态(如 201、204)。

​修复:​ 检查状态码是否为 2xx:

curl_getinfo($curl, CURLINFO_HTTP_CODE) >= 200 && curl_getinfo($curl, CURLINFO_HTTP_CODE) < 300

4.​file_get_contents 未处理错误

当使用 file_get_contents 时,无法检测 HTTP 错误状态码,可能返回错误内容。

​修复建议:​ 尽量统一使用 cURL,或在 file_get_contents 后解析响应头(复杂,推荐优先使用 cURL)。

5.​User-Agent 不一致

file_get_contents 部分未设置 User-Agent,而 cURL 部分尝试设置但被覆盖。

​修复:​ 在 file_get_contents 的 header 中添加 User-Agent。

改进后的代码示例:​

private static function httpRequest($url, $user, $pass) {
    if (extension_loaded('curl')) {
        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL => $url,
            CURLOPT_HEADER => false,
            CURLOPT_TIMEOUT => 20,
            CURLOPT_ENCODING => '',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_USERAGENT => self::$userAgent, // 统一设置User-Agent
        ]);
        // 仅当两者均非空时设置认证
        if ($user !== null && $pass !== null) {
            curl_setopt($curl, CURLOPT_USERPWD, "$user:$pass");
        }
        if (!ini_get('open_basedir')) {
            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
        }
        $result = curl_exec($curl);
        $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        $success = curl_errno($curl) === 0 && $statusCode >= 200 && $statusCode < 300;
        curl_close($curl);
        return $success ? $result : false;
    } else {
        $options = ['http' => ['method' => 'GET']];
        // 统一认证条件
        if ($user !== null && $pass !== null) {
            $options['http']['header'] = "Authorization: Basic " . base64_encode("$user:$pass") . "\r\n";
        }
        // 添加User-Agent
        if (self::$userAgent) {
            $options['http']['header'] .= "User-Agent: " . self::$userAgent . "\r\n";
        }
        $context = stream_context_create($options);
        $result = @file_get_contents($url, false, $context);
        // 简单检测是否出错(无法精确检查状态码)
        return $result !== false ? $result : false;
    }
}

其他建议:​

  • 添加 curl_close($curl) 释放资源。
  • 在 file_get_contents 使用 @ 抑制警告,并通过返回值判断成功(但无法获取状态码)。
  • 考虑强制使用 cURL 或处理 file_get_contents 的错误响应(如检查 $http_response_header)。

无脑抄袭

看着这个长篇大论、有论有据、很负责任的解答,我还是一脸懵逼啊...

不会看还不会抄啊,我直接将这个函数复制、替换、应用。哈哈,没有奇迹,品味苏州的订阅成功获取到了。困扰我数月之久的问题总算得到了圆满解决,感谢 AI !!!!