cURL(Client URL),是PHP中最强大、最常用的网络请求库。它支持HTTP、HTTPS、FTP、FTPS、SMTP、POP3等多种协议,可以发送GET、POST、PUT、DELETE等各种请求,支持文件上传、Cookie、代理、SSL认证等高级功能。

在Web开发中,cURL的应用非常广泛:调用第三方API接口、抓取网页内容、发送邮件、下载文件、OAuth认证、支付接口对接……几乎所有需要和外部服务交互的场景,都能用到cURL。

PHP也提供了filegetcontents()函数来发送简单的HTTP请求,但cURL的功能更强大,更灵活,更可控。对于复杂的网络请求,cURL是首选。

今天,我们来详细学习PHP cURL的使用,从基础请求到高级功能,从常用选项到实战案例,帮你掌握这个强大的网络请求工具。

cURL基础

发送GET请求

最基本的cURL请求:

// 1. 初始化cURL会话
$ch = curl_init();

// 2. 设置选项
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/users');  // 请求URL
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  // 返回响应而不是直接输出
curl_setopt($ch, CURLOPT_HEADER, false);  // 不返回响应头

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

// 4. 检查错误
if (curl_errno($ch)) {
    echo 'cURL错误:' . curl_error($ch);
}

// 5. 获取HTTP状态码
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo 'HTTP状态码:' . $httpCode;

// 6. 关闭会话
curl_close($ch);

echo $response;

发送POST请求

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/users');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);  // 设置为POST请求

// POST数据(数组会自动编码为multipart/form-data)
$postData = [
    'name' => '张三',
    'email' => 'zhangsan@example.com',
    'age' => 25,
];
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);

$response = curl_exec($ch);
curl_close($ch);

发送JSON请求

调用RESTful API时,通常需要发送JSON数据:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/users');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);

// JSON数据
$data = [
    'name' => '张三',
    'email' => 'zhangsan@example.com',
];
$jsonData = json_encode($data);

curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
// 设置Content-Type为application/json
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Content-Length: ' . strlen($jsonData),
]);

$response = curl_exec($ch);
curl_close($ch);

发送PUT/DELETE请求

// PUT请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/users/1');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');  // 自定义请求方法
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['name' => '李四']));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
curl_close($ch);

// DELETE请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/users/1');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
$response = curl_exec($ch);
curl_close($ch);

常用cURL选项

基本选项

  • CURLOPT_URL:请求的URL
  • CURLOPT_RETURNTRANSFER:true返回响应字符串,false直接输出
  • CURLOPT_HEADER:true包含响应头,false不包含
  • CURLOPT_CUSTOMREQUEST:自定义请求方法(GET/POST/PUT/DELETE等)
  • CURLOPT_POST:true发送POST请求
  • CURLOPT_POSTFIELDS:POST数据(数组或字符串)
  • CURLOPT_HTTPHEADER:请求头数组
  • CURLOPT_REFERER:设置Referer头
  • CURLOPT_USERAGENT:设置User-Agent

超时和重定向

  • CURLOPT_TIMEOUT:整个请求的超时时间(秒)
  • CURLOPT_CONNECTTIMEOUT:连接超时时间(秒)
  • CURLOPT_FOLLOWLOCATION:true跟随重定向(Location头)
  • CURLOPT_MAXREDIRS:最大重定向次数
  • CURLOPT_AUTOREFERER:true自动设置Referer(重定向时)
curl_setopt($ch, CURLOPT_TIMEOUT, 30);  // 30秒超时
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);  // 10秒连接超时
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);  // 跟随重定向
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);  // 最多重定向5次

SSL/HTTPS选项

  • CURLOPTSSLVERIFYPEER:true验证SSL证书,false不验证
  • CURLOPTSSLVERIFYHOST:验证证书主机名(0不验证,1检查,2严格检查)
  • CURLOPT_CAINFO:CA证书文件路径
  • CURLOPT_SSLCERT:客户端证书路径
  • CURLOPT_SSLCERTPASSWD:客户端证书密码
// 生产环境应该验证SSL证书
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_CAINFO, '/path/to/ca-bundle.crt');

// 开发环境可以临时关闭验证(不推荐生产环境使用)
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

Cookie选项

  • CURLOPT_COOKIE:设置Cookie字符串
  • CURLOPT_COOKIEFILE:从文件读取Cookie
  • CURLOPT_COOKIEJAR:把Cookie保存到文件
  • CURLOPT_COOKIESESSION:true开启新的Cookie会话
// 登录后保存Cookie,后续请求携带Cookie
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://example.com/login');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, ['username' => 'admin', 'password' => '123456']);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');  // 保存Cookie
curl_exec($ch);
curl_close($ch);

// 后续请求携带Cookie
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://example.com/dashboard');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');  // 读取Cookie
$response = curl_exec($ch);
curl_close($ch);

文件上传

// 文件上传(PHP 5.5+用CURLFile)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://example.com/upload');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);

$postData = [
    'name' => '头像',
    'file' => new CURLFile('/path/to/avatar.jpg', 'image/jpeg', 'avatar.jpg'),
];
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);

$response = curl_exec($ch);
curl_close($ch);

代理选项

  • CURLOPT_PROXY:代理服务器地址
  • CURLOPT_PROXYPORT:代理端口
  • CURLOPTPROXYTYPE:代理类型(CURLPROXYHTTP/CURLPROXY_SOCKS5)
  • CURLOPT_PROXYUSERPWD:代理用户名密码(user:password)
curl_setopt($ch, CURLOPT_PROXY, '127.0.0.1');
curl_setopt($ch, CURLOPT_PROXYPORT, 1080);
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);

下载文件

// 下载文件到本地
$fp = fopen('/path/to/download.zip', 'w');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://example.com/file.zip');
curl_setopt($ch, CURLOPT_FILE, $fp);  // 写入文件
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
fclose($fp);

获取请求信息

curl_getinfo()可以获取请求的各种信息:

$info = curl_getinfo($ch);
echo 'URL:' . $info['url'] . "\n";
echo 'HTTP状态码:' . $info['http_code'] . "\n";
echo '总时间:' . $info['total_time'] . "秒\n";
echo 'DNS解析时间:' . $info['namelookup_time'] . "秒\n";
echo '连接时间:' . $info['connect_time'] . "秒\n";
echo '传输时间:' . $info['starttransfer_time'] . "秒\n";
echo '下载大小:' . $info['size_download'] . "字节\n";
echo '下载速度:' . $info['speed_download'] . "字节/秒\n";
echo 'Content-Type:' . $info['content_type'] . "\n";

也可以指定获取某个信息:

$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$totalTime = curl_getinfo($ch, CURLINFO_TOTAL_TIME);

错误处理

$response = curl_exec($ch);

// 检查cURL错误
if (curl_errno($ch)) {
    $errorCode = curl_errno($ch);
    $errorMsg = curl_error($ch);
    echo "cURL错误($errorCode):$errorMsg";
    // 记录日志、重试、返回错误等
}

// 检查HTTP状态码
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode >= 400) {
    echo "HTTP错误:$httpCode";
}

常见cURL错误码:

  • 6:Couldn't resolve host(DNS解析失败)
  • 7:Failed to connect(连接失败)
  • 28:Operation timeout(超时)
  • 35:SSL connect error(SSL连接错误)
  • 51:SSL certificate problem(SSL证书问题)
  • 56:Failure with receiving network data(接收数据失败)
  • 77:Problem with reading the SSL CA cert(CA证书读取失败)

实战案例

1. 封装一个cURL工具类

class HttpClient {
    private $ch;
    private $options = [];
    
    public function __construct() {
        $this->ch = curl_init();
        $this->setDefaults();
    }
    
    private function setDefaults() {
        $this->options = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => false,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS => 5,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        ];
    }
    
    public function setOption($key, $value) {
        $this->options[$key] = $value;
        return $this;
    }
    
    public function setHeaders($headers) {
        $this->options[CURLOPT_HTTPHEADER] = $headers;
        return $this;
    }
    
    public function get($url, $params = []) {
        if (!empty($params)) {
            $url .= '?' . http_build_query($params);
        }
        $this->options[CURLOPT_URL] = $url;
        $this->options[CURLOPT_CUSTOMREQUEST] = 'GET';
        return $this->execute();
    }
    
    public function post($url, $data = [], $isJson = false) {
        $this->options[CURLOPT_URL] = $url;
        $this->options[CURLOPT_POST] = true;
        
        if ($isJson) {
            $jsonData = json_encode($data);
            $this->options[CURLOPT_POSTFIELDS] = $jsonData;
            $this->options[CURLOPT_HTTPHEADER] = [
                'Content-Type: application/json',
                'Content-Length: ' . strlen($jsonData),
            ];
        } else {
            $this->options[CURLOPT_POSTFIELDS] = $data;
        }
        
        return $this->execute();
    }
    
    public function put($url, $data = []) {
        $this->options[CURLOPT_URL] = $url;
        $this->options[CURLOPT_CUSTOMREQUEST] = 'PUT';
        $this->options[CURLOPT_POSTFIELDS] = json_encode($data);
        $this->options[CURLOPT_HTTPHEADER] = ['Content-Type: application/json'];
        return $this->execute();
    }
    
    public function delete($url) {
        $this->options[CURLOPT_URL] = $url;
        $this->options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
        return $this->execute();
    }
    
    private function execute() {
        curl_setopt_array($this->ch, $this->options);
        
        $response = curl_exec($this->ch);
        $httpCode = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
        $error = curl_errno($this->ch) ? curl_error($this->ch) : null;
        
        return [
            'success' => $error === null && $httpCode < 400,
            'code' => $httpCode,
            'body' => $response,
            'error' => $error,
        ];
    }
    
    public function __destruct() {
        curl_close($this->ch);
    }
}

// 使用示例
$client = new HttpClient();

// GET请求
$result = $client->get('https://api.example.com/users', ['page' => 1, 'size' => 10]);
if ($result['success']) {
    $data = json_decode($result['body'], true);
}

// POST JSON请求
$result = $client->post('https://api.example.com/users', [
    'name' => '张三',
    'email' => 'zhangsan@example.com',
], true);

2. 调用微信API

// 获取微信access_token
$appId = 'your_app_id';
$appSecret = 'your_app_secret';
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appId&secret=$appSecret";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
$accessToken = $result['access_token'];

3. 发送邮件(通过SMTP)

cURL也可以通过SMTP协议发送邮件:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'smtp://smtp.example.com:587');
curl_setopt($ch, CURLOPT_MAIL_FROM, 'sender@example.com');
curl_setopt($ch, CURLOPT_MAIL_RCPT, ['recipient@example.com']);
curl_setopt($ch, CURLOPT_USERNAME, 'sender@example.com');
curl_setopt($ch, CURLOPT_PASSWORD, 'password');
curl_setopt($ch, CURLOPT_USE_SSL, CURLUSESSL_ALL);

$email = "From: sender@example.com\r\n";
$email .= "To: recipient@example.com\r\n";
$email .= "Subject: 测试邮件\r\n";
$email .= "Content-Type: text/plain; charset=utf-8\r\n\r\n";
$email .= "这是一封测试邮件。";

curl_setopt($ch, CURLOPT_INFILE, fopen('data://text/plain,' . $email, 'r'));
curl_setopt($ch, CURLOPT_INFILESIZE, strlen($email));
curl_setopt($ch, CURLOPT_UPLOAD, true);

curl_exec($ch);
curl_close($ch);

不过,发送邮件更推荐用PHPMailer等专门的库,cURL发邮件比较底层。

4. 网页爬虫

function crawl($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MyBot/1.0)');
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    
    $html = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode !== 200) {
        return false;
    }
    
    // 用DOMDocument解析HTML
    $dom = new DOMDocument();
    @$dom->loadHTML($html);
    
    // 提取所有链接
    $links = [];
    $anchors = $dom->getElementsByTagName('a');
    foreach ($anchors as $anchor) {
        $href = $anchor->getAttribute('href');
        if ($href) {
            $links[] = $href;
        }
    }
    
    return [
        'html' => $html,
        'links' => $links,
        'title' => $dom->getElementsByTagName('title')->item(0)->nodeValue ?? '',
    ];
}

$result = crawl('https://example.com');
print_r($result['links']);

cURL最佳实践

  1. 总是设置超时:不要让请求无限等待,设置CURLOPTTIMEOUT和CURLOPTCONNECTTIMEOUT。
  2. 生产环境验证SSL:生产环境不要关闭CURLOPTSSLVERIFYPEER,确保SSL安全。
  3. 使用封装类:封装一个HttpClient类,统一处理错误、超时、重试,避免重复代码。
  4. 错误处理:总是检查curl_errno()和HTTP状态码,不要假设请求一定成功。
  5. 关闭连接:请求完成后用curl_close()关闭连接,释放资源。
  6. 并发请求:需要同时请求多个URL时,用curlmulti*函数实现并发,提高效率。
  7. 设置User-Agent:有些网站会检查User-Agent,设置一个合理的UA。
  8. 处理重定向:需要跟随重定向时,设置CURLOPT_FOLLOWLOCATION。
  9. 重试机制:网络请求可能失败,对幂等请求(GET)实现重试机制。
  10. 记录日志:记录请求URL、参数、响应、错误,方便调试和排查问题。

并发请求(curl_multi)

当需要同时请求多个URL时,用curl_multi可以并发执行,提高效率:

$urls = [
    'https://api.example.com/users',
    'https://api.example.com/posts',
    'https://api.example.com/comments',
];

$mh = curl_multi_init();
$handles = [];

foreach ($urls as $i => $url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_multi_add_handle($mh, $ch);
    $handles[$i] = $ch;
}

// 执行并发请求
$active = null;
do {
    curl_multi_exec($mh, $active);
    curl_multi_select($mh);
} while ($active > 0);

// 获取结果
$results = [];
foreach ($handles as $i => $ch) {
    $results[$i] = curl_multi_getcontent($ch);
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
}

curl_multi_close($mh);
print_r($results);

总结

cURL是PHP中最强大、最常用的网络请求库,功能强大,灵活可控。

核心要点:

  1. 基础请求:curlinit初始化、curlsetopt设置选项、curlexec执行、curlclose关闭
  2. 请求方法:GET、POST(表单/JSON)、PUT、DELETE(CURLOPT_CUSTOMREQUEST)
  3. 常用选项:URL、RETURNTRANSFER、POST、POSTFIELDS、HTTPHEADER、超时、重定向、SSL、Cookie、文件上传、代理
  4. 获取信息:curl_getinfo获取HTTP状态码、耗时、速度等
  5. 错误处理:curlerrno/curlerror检查错误,检查HTTP状态码
  6. 实战案例:封装HttpClient类、调用微信API、SMTP发邮件、网页爬虫
  7. 最佳实践:设置超时、验证SSL、封装类、错误处理、关闭连接、并发请求、设置UA、记录日志
  8. 并发请求:curl_multi实现并发,提高效率

cURL是PHP开发者必须掌握的技能之一。无论是调用API、抓取网页、下载文件,还是发送邮件,cURL都能胜任。掌握cURL,能让你的PHP应用和外部世界更好地交互。

希望这篇文章能帮你更好地使用PHP cURL。