首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

解決 file_get_contents 無法請求 HTTPS:OpenSSL 配置與 cURL 替代方案

     編輯:PHP 相關知識

    錯誤原因

    Warning: fopen(): Unable to find the wrapper "https" 
    - did you forget to enable it when you configured PHP?

    PHP 的 file_get_contents 依賴 OpenSSL 擴展 才能支援 https:// 協議封裝協定。預設編譯或安裝時若未啟用 OpenSSL,將無法直接讀取 HTTPS 資源。

    方案一:Windows 環境啟用 OpenSSL

    1. 開啟 php.ini
    2. 搜尋 ;extension=openssl
    3. 移除前面的分號 ; → extension=openssl
    4. 確認 extension_dir 指向正確的 `ext` 目錄
    5. 重啟 Web 服務 (Apache/Nginx/PHP-FPM)

    方案二:Linux 環境安裝 OpenSSL 模組

    # Ubuntu/Debian
    sudo apt-get install php-openssl
    # 或指定版本
    sudo apt-get install php8.1-openssl
    
    # CentOS/RHEL/Rocky
    sudo yum install php-openssl
    # 或
    sudo dnf install php-openssl
    
    # 重啟 PHP-FPM
    sudo systemctl restart php-fpm

    方案三:使用 cURL 替代 (通用、功能更強、無需 OpenSSL 擴展即可支援 HTTPS)

    cURL 是專為 URL 傳輸設計的庫,支援 HTTPS、HTTP/2、憑證驗證、代理、超時控制、重定向追蹤等豐富功能,是生產環境請求外部 API 的標準選擇。

    健壯的 cURL 封裝函數

    <?php
    /**
     * 通用 HTTP 請求函數 (支援 GET/POST、HTTPS、標頭、超時、重定向)
     * 
     * @param string $url       目標 URL
     * @param int    $timeout   超時秒數
     * @param array  $headers   額外請求標頭 ['Header-Name: value', ...]
     * @param array  $postData  POST 資料 (關聯陣列),為空則 GET
     * @return string|false     回應內容,失敗回傳 false
     */
    function http_request(string $url, int $timeout = 30, array $headers = [], array $postData = []) {
        if (!function_exists('curl_init')) {
            throw new RuntimeException('cURL 擴展未安裝');
        }
    
        $ch = curl_init();
        
        // 基礎設定
        curl_setopt_array($ch, [
            CURLOPT_URL            => $url,
            CURLOPT_RETURNTRANSFER => true,     // 回傳字串而非直接輸出
            CURLOPT_HEADER         => false,    // 不包含回應標頭
            CURLOPT_FOLLOWLOCATION => true,     // 自動追蹤 301/302 重定向
            CURLOPT_MAXREDIRS      => 5,        // 最大重定向次數
            CURLOPT_TIMEOUT        => $timeout, // 總超時
            CURLOPT_CONNECTTIMEOUT => 10,       // 連線超時
            CURLOPT_SSL_VERIFYPEER => true,     // 驗證 SSL 憑證 (生產環境必須開啟)
            CURLOPT_SSL_VERIFYHOST => 2,        // 驗證主機名
            // CURLOPT_CAINFO       => '/path/to/cacert.pem', // 若憑證驗證失敗,指定 CA 憑證包
            CURLOPT_USERAGENT      => 'PHP-cURL/'.phpversion(),
        ]);
    
        // 自訂標頭
        if ($headers) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }
    
        // POST 請求
        if ($postData) {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
        }
    
        $response = curl_exec($ch);
        
        if ($response === false) {
            $error = curl_error($ch);
            $errno = curl_errno($ch);
            curl_close($ch);
            error_log("cURL error [$errno]: $error for URL: $url");
            return false;
        }
    
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
    
        // 可選:非 2xx 視為失敗
        if ($httpCode < 200 || $httpCode >= 300) {
            error_log("HTTP $httpCode for URL: $url");
            // 視業務決定是否回傳 false 或內容
        }
    
        return $response;
    }
    
    // 使用範例
    $html = http_request('https://api.example.com/users', 10, ['Authorization: Bearer token123']);
    $json = http_request('https://api.example.com/data', 10, ['Content-Type: application/json'], ['key' => 'value']);

    進階:非同步並發請求 (cURL Multi)

    <?php
    function multi_request(array $urls, int $timeout = 30): array {
        $mh = curl_multi_init();
        $handles = [];
        $results = [];
    
        foreach ($urls as $i => $url) {
            $ch = curl_init($url);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT        => $timeout,
                CURLOPT_FOLLOWLOCATION => true,
                CURLOPT_SSL_VERIFYPEER => true,
            ]);
            curl_multi_add_handle($mh, $ch);
            $handles[$i] = $ch;
        }
    
        $running = null;
        do {
            curl_multi_exec($mh, $running);
            curl_multi_select($mh, 0.1); // 避免 CPU 空轉
        } while ($running > 0);
    
        foreach ($handles as $i => $ch) {
            $results[$i] = [
                'content'  => curl_multi_getcontent($ch),
                'http_code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
                'error'    => curl_error($ch),
            ];
            curl_multi_remove_handle($mh, $ch);
            curl_close($ch);
        }
        curl_multi_close($mh);
        return $results;
    }

    方案對比總結

    方案優點缺點建議場景
    開啟 OpenSSL + file_get_contents程式碼簡潔、無額外依賴功能極簡、無超時控制、無標頭控制、難除錯極簡單的 GET 請求、開發測試
    cURL (同步)功能完整、標準庫、效能好、支援並發程式碼較冗長✅ 生產環境標準選擇
    Guzzle / Symfony HttpClientPSR-18/PSR-7 相容、中介軟體、異步、重試、Mock 測試Composer 依賴現代框架專案 (Laravel/Symfony/獨立)

    常見 HTTPS 問題排查

    • SSL certificate problem: self signed certificate:開發環境自簽憑證 → 臨時關閉驗證 CURLOPT_SSL_VERIFYPEER => false (僅開發) 或信任 CA。
    • cURL error 60: SSL certificate problem:缺少 CA 憑證包 → 下載 cacert.pem 並設定 CURLOPT_CAINFO。
    • Protocol "https" not supported:cURL 編譯時未支援 SSL → 重新安裝含 OpenSSL 的 cURL / php-curl。

    建議專案統一使用 cURL 或成熟 HTTP Client 套件,徹底解決 HTTPS 請求問題並獲得企業級功能支援。

    1. 上一篇文章:
    2. 下一篇文章:
    Copyright © 程式碼之家 All Rights Reserved