其實(shí) CURL 這個(gè)擴(kuò)展本來也不打算寫得,畢竟這個(gè)也是大家最常用的功能之一的。不過既然是在刷文檔,學(xué)習(xí)到了就分享出來吧,不要陷入“知識的詛咒”。本身自己的知識體系就不完整,說不定也有很多小伙伴和我一樣只是平常追求業(yè)務(wù)快速開發(fā)而簡單地使用,并沒有深入地去了解過。今天,我們就來深入地了解一下 CURL 吧。
PHP 的這個(gè) CURL 擴(kuò)展其實(shí)是基于的 libcurl 這個(gè)系統(tǒng)的擴(kuò)展軟件。在 linux 相關(guān)的系統(tǒng)中,這個(gè)軟件基本就是標(biāo)配的,像是 CentOS 、Ubuntu 這些都是裝好系統(tǒng)就有的,不需要我們再單獨(dú)安裝這個(gè) libcurl ,就算沒有的話,也可以方便地安裝到。
而對于 PHP 來說,這個(gè)擴(kuò)展更是已經(jīng)集成在了 PHP 的源碼安裝包中,只需要我們在編譯安裝 PHP 的時(shí)候加上 --with-curl 就可以了。
先來看看最簡單地使用 CURL 來請求一個(gè) GET 地址。
$ch = curl_init("https://www.baidu.com");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1); // 檢查證書中是否設(shè)置域名
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // 信任任何證書
$res = curl_exec($ch);
$info = curl_getinfo($ch);
if(curl_error($ch)) {
var_dump(curl_error($ch));
}
var_dump($res);
// string(14722) "<!DOCTYPE html><!--STATUS OK-->
// <html>
// <head>
// <meta http-equiv="content-type" content="text/html;charset=utf-8">
// <meta http-equiv="X-UA-Compatible" content="IE=Edge">
// <link rel="dns-prefetch" />
// <link rel="dns-prefetch" />
// <link rel="dns-prefetch" />
// <link rel="dns-prefetch" />
// <link rel="dns-prefetch" />
// <link rel="dns-prefetch" />
// <link rel="dns-prefetch" />
// <link rel="dns-prefetch" />
// <title>百度一下,你就知道</title>
// ……………………
// ……………………
// ……………………
// </body></html>
// "
var_dump($info);
// array(37) {
// ["url"]=>
// string(21) "https://www.baidu.com"
// ["content_type"]=>
// string(9) "text/html"
// ["http_code"]=>
// int(200)
// ["header_size"]=>
// int(960)
// ["request_size"]=>
// int(52)
// ["filetime"]=>
// int(-1)
// ["ssl_verify_result"]=>
// int(0)
// ["redirect_count"]=>
// int(0)
// ["total_time"]=>
// float(0.127654)
// ["namelookup_time"]=>
// float(0.004669)
// ["connect_time"]=>
// float(0.030823)
// ["pretransfer_time"]=>
// float(0.100782)
// ["size_upload"]=>
// float(0)
// ["size_download"]=>
// float(14722)
// ["speed_download"]=>
// float(115921)
// ["speed_upload"]=>
// float(0)
// ["download_content_length"]=>
// float(14722)
// ["upload_content_length"]=>
// float(-1)
// ["starttransfer_time"]=>
// float(0.127519)
// ["redirect_time"]=>
// float(0)
// ["redirect_url"]=>
// string(0) ""
// ["primary_ip"]=>
// string(15) "183.232.231.172"
// ["certinfo"]=>
// array(0) {
// }
// ["primary_port"]=>
// int(443)
// ["local_ip"]=>
// string(13) "192.168.51.11"
// ["local_port"]=>
// int(52795)
// ["http_version"]=>
// int(2)
// ["protocol"]=>
// int(2)
// ["ssl_verifyresult"]=>
// int(0)
// ["scheme"]=>
// string(5) "HTTPS"
// ["appconnect_time_us"]=>
// int(100710)
// ["connect_time_us"]=>
// int(30823)
// ["namelookup_time_us"]=>
// int(4669)
// ["pretransfer_time_us"]=>
// int(100782)
// ["redirect_time_us"]=>
// int(0)
// ["starttransfer_time_us"]=>
// int(127519)
// ["total_time_us"]=>
// int(127654)
// }
curl_close($ch);
標(biāo)準(zhǔn)的 CURL 套路其實(shí)就是三步。curl_init() 打開一個(gè)句柄,句柄中包含 URL 地址,curl_exec() 執(zhí)行句柄輸出或返回結(jié)果,curl_close() 關(guān)閉句柄。為什么說 curl_exec() 是輸出或者返回結(jié)果呢?因?yàn)槿绻谀J(rèn)的情況下,curl_exec() 是會像 phpinfo() 這類函數(shù)一樣直接打印輸出結(jié)果的,我們需要使用 curl_setopt() 設(shè)置 CURLOPT_RETURNTRANSFER 這個(gè)常量為 true 或 1 來讓 curl_exec() 將訪問結(jié)果作為返回值進(jìn)行返回,而不是直接輸出。由此可以看出,curl_setopt() 也是 CURL 中非常重要的一個(gè)函數(shù)。其實(shí)它的作用就是為這個(gè) CURL 句柄設(shè)置各種配置參數(shù),包括我們在代碼中看到的 CURLOPT_SSL_VERIFYHOST 和 CURLOPT_SSL_VERIFYPEER 就是為 HTTPS 鏈接的訪問而準(zhǔn)備的配置參數(shù),以及后面我們要看到的 POST 請求也是需要使用 curl_setopt() 來實(shí)現(xiàn)的。
curl_exec() 返回的是訪問的 URL 返回的結(jié)果,curl_getinfo() 返回的則是這個(gè)請求一些詳細(xì)信息,比如我們可以看到請求的 url 地址、Content-Type 類型、http_code 等信息,對于一些業(yè)務(wù)需求判斷來說,這些信息非常重要。curl_error() 則是在本次請求中的錯(cuò)誤信息的顯示,如果產(chǎn)生了錯(cuò)誤,錯(cuò)誤信息就可以通過這個(gè)函數(shù)獲取到。
GET 請求是非常簡單的,當(dāng)然,POST 請求也不復(fù)雜,就像前面說的,只是配置參數(shù)有一些變化而已。
$ch = curl_init("http://localhost:9001");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt_array($ch, [
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => ['a'=>'post測試'],
]);
$res = curl_exec($ch);
$info = curl_getinfo($ch);
if(curl_error($ch)) {
var_dump(curl_error($ch));
}
var_dump($res);
// string(22) "測試數(shù)據(jù)post測試"
curl_close($ch);
在這里,我們用了一個(gè)新的函數(shù) curl_setopt_array() ,其實(shí)就是可以更加方便地使用數(shù)組來定義配置參數(shù)而已,和一個(gè)一個(gè)地寫 curl_setopt() 沒有什么區(qū)別,只是更加地方便。我們只需要指定 CURLOPT_POST 為 true 或 1,然后通過 CURLOPT_POSTFIELDS 為 POST 參數(shù)賦值就可以了,是不是也非常簡單。
之前我們已經(jīng)學(xué)習(xí)過一些編碼相關(guān)的函數(shù),在 CURL 擴(kuò)展中,也有對應(yīng)的 URL 編碼函數(shù),其實(shí)它和使用 urlencode() 并沒有什么太大的區(qū)別。
$ch = curl_init("http://localhost:9001");
$str = "測試編碼";
$escapeStr = curl_escape($ch, $str);
var_dump($escapeStr); // string(36) "%E6%B5%8B%E8%AF%95%E7%BC%96%E7%A0%81"
var_dump(curl_unescape($ch, $escapeStr)); // string(12) "測試編碼"
curl_close($ch);
使用 curl_escape() 就可以對數(shù)據(jù)進(jìn)行 URL 編碼,使用 curl_unescape() 就可以非常方便地實(shí)現(xiàn)解碼。不過,這兩個(gè)函數(shù)是必須要一個(gè) CURL 句柄參數(shù)的,也就是說,它們不能脫離 CURL 來直接使用。我們?nèi)粘i_發(fā)還是使用 urlencode() 這類更為通用的函數(shù)就好了。
最后,我們再學(xué)習(xí)一個(gè)非常簡單的函數(shù),就是查看一下當(dāng)前系統(tǒng)的 CURL 版本情況以及一些相關(guān)的軟件信息,比如支持的協(xié)議列表之類的內(nèi)容。
var_dump(curl_version());
// array(16) {
// ["version_number"]=>
// int(475648)
// ["age"]=>
// int(5)
// ["features"]=>
// int(11519901)
// ["ssl_version_number"]=>
// int(0)
// ["version"]=>
// string(6) "7.66.0"
// ["host"]=>
// string(25) "x86_64-apple-darwin19.5.0"
// ["ssl_version"]=>
// string(14) "OpenSSL/1.1.1d"
// ["libz_version"]=>
// string(6) "1.2.11"
// ["protocols"]=>
// array(23) {
// [0]=>
// string(4) "dict"
// [1]=>
// string(4) "file"
// [2]=>
// string(3) "ftp"
// [3]=>
// string(4) "ftps"
// [4]=>
// string(6) "gopher"
// [5]=>
// string(4) "http"
// [6]=>
// string(5) "https"
// [7]=>
// string(4) "imap"
// [8]=>
// string(5) "imaps"
// [9]=>
// string(4) "ldap"
// [10]=>
// string(5) "ldaps"
// [11]=>
// string(4) "pop3"
// [12]=>
// string(5) "pop3s"
// [13]=>
// string(4) "rtmp"
// [14]=>
// string(4) "rtsp"
// [15]=>
// string(3) "scp"
// [16]=>
// string(4) "sftp"
// [17]=>
// string(3) "smb"
// [18]=>
// string(4) "smbs"
// [19]=>
// string(4) "smtp"
// [20]=>
// string(5) "smtps"
// [21]=>
// string(6) "telnet"
// [22]=>
// string(4) "tftp"
// }
// ["ares"]=>
// string(6) "1.15.0"
// ["ares_num"]=>
// int(69376)
// ["libidn"]=>
// string(5) "2.2.0"
// ["iconv_ver_num"]=>
// int(0)
// ["libssh_version"]=>
// string(13) "libssh2/1.9.0"
// ["brotli_ver_num"]=>
// int(16777223)
// ["brotli_version"]=>
// string(5) "1.0.7"
// }
就像文章開頭所說的,CURL 的內(nèi)容其實(shí)并不復(fù)雜,核心的就那幾步,它最復(fù)雜的部分是在于非常多的配置常量信息,而且這些信息并不是太好記,掌握常用的就可以了,后面我們還將繼續(xù)講解 CURL 中其它的內(nèi)容,不要錯(cuò)過哦。
測試代碼:https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/02/source/4.學(xué)習(xí)CURL擴(kuò)展功能的使用(一).php
參考文檔:
https://www.php.net/manual/zh/ref.curl.php