PHP curl_errno函数

定义和用法

curl_errno() 函数用于返回最后一次cURL操作的错误代码。与curl_error()返回可读的错误描述不同,curl_errno()返回一个数字代码,便于程序进行精确的错误识别和分类处理。

提示: curl_errno()通常与curl_error()结合使用,curl_errno()用于程序逻辑判断,curl_error()用于显示给开发人员或记录日志。

语法

int curl_errno ( resource $ch )

参数

参数 描述 类型 必需
ch curl_init() 返回的cURL句柄 resource

返回值

返回错误代码的整数值。如果没有错误发生,返回 0 (CURLE_OK)。

常见错误代码

错误代码 常量 描述 解决方案
1 CURLE_UNSUPPORTED_PROTOCOL 不支持的协议 检查URL协议是否支持(http/https)
6 CURLE_COULDNT_RESOLVE_HOST 无法解析主机 检查域名拼写和DNS设置
7 CURLE_COULDNT_CONNECT 无法连接到服务器 检查网络连接和服务器状态
28 CURLE_OPERATION_TIMEDOUT 操作超时 增加超时时间或优化网络
35 CURLE_SSL_CONNECT_ERROR SSL连接错误 检查SSL证书设置
47 CURLE_TOO_MANY_REDIRECTS 重定向过多 检查重定向循环或限制重定向次数
52 CURLE_GOT_NOTHING 服务器未返回任何数据 检查服务器响应和连接稳定性
56 CURLE_RECV_ERROR 接收网络数据失败 检查网络连接稳定性
60 CURLE_SSL_CACERT SSL证书问题 验证SSL证书或禁用验证(仅开发)

curl_errno() vs curl_error()

特性 curl_errno() curl_error()
返回值类型 整数(错误代码) 字符串(错误描述)
主要用途 程序逻辑判断和错误分类 显示给开发人员或记录日志
无错误时的返回值 0 (CURLE_OK) 空字符串 ("")
国际化支持 代码是固定的,不受语言影响 描述文本可能随系统语言变化
推荐使用场景 条件判断、错误分类、自动化处理 调试信息、用户提示、日志记录

示例

示例 1:基本的错误代码检查

@php
// 初始化cURL会话
$ch = curl_init();

// 设置一个可能出错的URL
curl_setopt_array($ch, [
    CURLOPT_URL => "https://invalid-domain-12345.com",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 5
]);

// 执行请求
$response = curl_exec($ch);

// 检查错误代码
$errorCode = curl_errno($ch);

if ($errorCode !== 0) {
    // 获取错误描述
    $errorMessage = curl_error($ch);

    echo "cURL请求失败!\n";
    echo "错误代码: " . $errorCode . "\n";
    echo "错误描述: " . $errorMessage . "\n";

    // 根据错误代码进行特定处理
    switch ($errorCode) {
        case CURLE_COULDNT_RESOLVE_HOST: // 6
            echo "处理: 域名解析失败,检查域名拼写\n";
            break;
        case CURLE_OPERATION_TIMEDOUT: // 28
            echo "处理: 请求超时,尝试增加超时时间\n";
            break;
        case CURLE_SSL_CONNECT_ERROR: // 35
            echo "处理: SSL连接错误,检查证书设置\n";
            break;
        default:
            echo "处理: 未知错误,查看cURL文档了解错误代码 " . $errorCode . "\n";
    }
} else {
    echo "请求成功!\n";
    echo "响应长度: " . strlen($response) . " 字节\n";
}

curl_close($ch);
@endphp

示例 2:错误分类与自动化处理

@php
class CurlErrorHandler {
    // 可重试的错误代码
    const RETRYABLE_ERRORS = [
        CURLE_COULDNT_CONNECT,      // 7
        CURLE_OPERATION_TIMEDOUT,   // 28
        CURLE_GOT_NOTHING,          // 52
        CURLE_RECV_ERROR,           // 56
        CURLE_SEND_ERROR            // 55
    ];

    // 配置错误
    const CONFIGURATION_ERRORS = [
        CURLE_UNSUPPORTED_PROTOCOL, // 1
        CURLE_URL_MALFORMAT,        // 3
        CURLE_SSL_CACERT            // 60
    ];

    // 网络错误
    const NETWORK_ERRORS = [
        CURLE_COULDNT_RESOLVE_HOST, // 6
        CURLE_COULDNT_CONNECT       // 7
    ];

    public static function handleCurlError($ch, $url) {
        $errorCode = curl_errno($ch);
        $errorMessage = curl_error($ch);

        echo "URL: {$url}\n";
        echo "错误代码: {$errorCode}\n";
        echo "错误描述: {$errorMessage}\n";

        $category = self::categorizeError($errorCode);
        $action = self::getRecommendedAction($category, $errorCode);

        echo "错误分类: {$category}\n";
        echo "推荐操作: {$action}\n";

        return [
            'code' => $errorCode,
            'message' => $errorMessage,
            'category' => $category,
            'action' => $action
        ];
    }

    private static function categorizeError($errorCode) {
        if ($errorCode === 0) {
            return 'success';
        } elseif (in_array($errorCode, self::RETRYABLE_ERRORS)) {
            return 'retryable';
        } elseif (in_array($errorCode, self::CONFIGURATION_ERRORS)) {
            return 'configuration';
        } elseif (in_array($errorCode, self::NETWORK_ERRORS)) {
            return 'network';
        } else {
            return 'unknown';
        }
    }

    private static function getRecommendedAction($category, $errorCode) {
        switch ($category) {
            case 'retryable':
                return '实现重试机制,使用指数退避策略';
            case 'configuration':
                return '检查cURL选项配置和URL格式';
            case 'network':
                return '检查网络连接和DNS设置';
            case 'unknown':
                return '查看cURL文档了解错误代码 ' . $errorCode;
            default:
                return '无错误';
        }
    }
}

// 测试错误处理
$testUrls = [
    "https://invalid-domain-12345.com",  // 会触发CURLE_COULDNT_RESOLVE_HOST (6)
    "https://httpbin.org/delay/10",      // 如果超时设置短,会触发CURLE_OPERATION_TIMEDOUT (28)
    "httpx://example.com"                // 会触发CURLE_UNSUPPORTED_PROTOCOL (1)
];

foreach ($testUrls as $url) {
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 2  // 短超时以便测试
    ]);

    curl_exec($ch);
    $result = CurlErrorHandler::handleCurlError($ch, $url);
    curl_close($ch);

    echo str_repeat("-", 50) . "\n";
}
@endphp

示例 3:基于错误代码的重试机制

@php
function makeCurlRequestWithSmartRetry($url, $options = []) {
    $maxRetries = 3;
    $retryDelay = 1;
    $attempt = 0;

    // 可重试的错误代码
    $retryableErrors = [
        CURLE_COULDNT_CONNECT,    // 7
        CURLE_OPERATION_TIMEDOUT, // 28
        CURLE_GOT_NOTHING,        // 52
        CURLE_RECV_ERROR          // 56
    ];

    while ($attempt < $maxRetries) {
        $attempt++;
        echo "尝试第 {$attempt} 次请求...\n";

        $ch = curl_init();

        // 默认选项
        $defaultOptions = [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10
        ];

        $finalOptions = $options + $defaultOptions;
        curl_setopt_array($ch, $finalOptions);

        $response = curl_exec($ch);
        $errorCode = curl_errno($ch);
        $errorMessage = curl_error($ch);

        if ($errorCode === 0) {
            echo "第 {$attempt} 次尝试成功!\n";
            curl_close($ch);
            return [
                'success' => true,
                'response' => $response,
                'attempts' => $attempt,
                'final_error' => null
            ];
        }

        echo "第 {$attempt} 次尝试失败 - 错误代码: {$errorCode}, 描述: {$errorMessage}\n";

        // 检查是否应该重试
        if (in_array($errorCode, $retryableErrors) && $attempt < $maxRetries) {
            echo "错误代码 {$errorCode} 是可重试的,等待 {$retryDelay} 秒后重试...\n";
            sleep($retryDelay);
            $retryDelay *= 2; // 指数退避
        } else {
            echo "错误代码 {$errorCode} 不可重试或已达到最大重试次数\n";
            break;
        }

        curl_close($ch);
    }

    return [
        'success' => false,
        'response' => null,
        'attempts' => $attempt,
        'final_error' => [
            'code' => $errorCode,
            'message' => $errorMessage
        ]
    ];
}

// 测试智能重试(使用一个可能超时的URL)
$result = makeCurlRequestWithSmartRetry("https://httpbin.org/delay/5");

if ($result['success']) {
    echo "最终请求成功!经过 {$result['attempts']} 次尝试\n";
    echo "响应数据长度: " . strlen($result['response']) . " 字节\n";
} else {
    echo "所有尝试都失败了!\n";
    echo "最后错误代码: " . $result['final_error']['code'] . "\n";
    echo "最后错误描述: " . $result['final_error']['message'] . "\n";
    echo "总共尝试了 {$result['attempts']} 次\n";
}
@endphp

示例 4:错误监控和报警系统

@php
class CurlMonitor {
    private static $errorStats = [];

    public static function trackRequest($url, $ch) {
        $errorCode = curl_errno($ch);

        if ($errorCode !== 0) {
            self::recordError($errorCode, $url);

            // 检查是否需要触发报警
            if (self::shouldTriggerAlert($errorCode)) {
                self::triggerAlert($errorCode, $url);
            }
        }

        return $errorCode;
    }

    private static function recordError($errorCode, $url) {
        $timestamp = time();
        $minute = date('Y-m-d H:i:00', $timestamp);

        if (!isset(self::$errorStats[$minute])) {
            self::$errorStats[$minute] = [];
        }

        if (!isset(self::$errorStats[$minute][$errorCode])) {
            self::$errorStats[$minute][$errorCode] = [
                'count' => 0,
                'urls' => []
            ];
        }

        self::$errorStats[$minute][$errorCode]['count']++;

        if (!in_array($url, self::$errorStats[$minute][$errorCode]['urls'])) {
            self::$errorStats[$minute][$errorCode]['urls'][] = $url;
        }

        // 清理过期的统计数据(保留最近10分钟)
        self::cleanupOldStats();
    }

    private static function shouldTriggerAlert($errorCode) {
        $criticalErrors = [
            CURLE_COULDNT_RESOLVE_HOST, // 6 - DNS问题
            CURLE_SSL_CONNECT_ERROR     // 35 - SSL问题
        ];

        return in_array($errorCode, $criticalErrors);
    }

    private static function triggerAlert($errorCode, $url) {
        $errorName = self::getErrorName($errorCode);

        echo "[ALERT] 检测到关键错误!\n";
        echo "错误代码: {$errorCode} ({$errorName})\n";
        echo "发生URL: {$url}\n";
        echo "时间: " . date('Y-m-d H:i:s') . "\n";

        // 在实际应用中,这里可以发送邮件、短信或调用监控系统API
        // mail('admin@example.com', 'cURL Critical Error', $alertMessage);
    }

    public static function getErrorStats() {
        return self::$errorStats;
    }

    public static function getErrorName($errorCode) {
        $errorNames = [
            1 => 'CURLE_UNSUPPORTED_PROTOCOL',
            6 => 'CURLE_COULDNT_RESOLVE_HOST',
            7 => 'CURLE_COULDNT_CONNECT',
            28 => 'CURLE_OPERATION_TIMEDOUT',
            35 => 'CURLE_SSL_CONNECT_ERROR',
            52 => 'CURLE_GOT_NOTHING'
        ];

        return $errorNames[$errorCode] ?? 'UNKNOWN_ERROR';
    }

    private static function cleanupOldStats() {
        $tenMinutesAgo = time() - 600;
        $cutoffMinute = date('Y-m-d H:i:00', $tenMinutesAgo);

        foreach (array_keys(self::$errorStats) as $minute) {
            if ($minute < $cutoffMinute) {
                unset(self::$errorStats[$minute]);
            }
        }
    }
}

// 模拟多个请求并监控错误
$urls = [
    "https://invalid-host-123.com",
    "https://httpbin.org/status/500",
    "https://valid-but-may-timeout.com"
];

foreach ($urls as $url) {
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 3
    ]);

    curl_exec($ch);
    $errorCode = CurlMonitor::trackRequest($url, $ch);

    if ($errorCode !== 0) {
        $errorName = CurlMonitor::getErrorName($errorCode);
        echo "请求失败: {$url} - 错误: {$errorCode} ({$errorName})\n";
    } else {
        echo "请求成功: {$url}\n";
    }

    curl_close($ch);
    echo "---\n";
}

// 显示错误统计
echo "错误统计:\n";
print_r(CurlMonitor::getErrorStats());
@endphp

最佳实践

使用常量

使用CURLE_*常量而不是硬编码数字,提高代码可读性。

错误分类

根据错误代码将错误分类,实现不同的处理策略。

智能重试

只为可重试的错误代码实现重试机制。

注意事项

重要:
  • curl_errno()必须在curl_exec()之后调用才能获取有效的错误代码
  • 错误代码0表示没有错误发生(CURLE_OK)
  • 不同的cURL版本可能支持不同的错误代码
  • 某些错误代码可能在特定条件下不会出现
  • 在生产环境中,建议结合curl_error()一起使用以获得完整的错误信息
  • 注意错误代码的范围,cURL错误代码通常是正整数