php curl常见错误:SSL错误、bool(false)

PHP cURL 调用问题及解决
症状:php curl调用https出错
排查方法:在命令行中使用curl调用试试。
原因:服务器所在机房无法验证SSL证书。
解决办法:跳过SSL证书检查。
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

症状:php curl调用curl_exec返回bool(false),命令行curl调用正常。
排查方法:
var_dump(curl_error($ch));
返回:
string(23) "Empty reply from server"
再排查:
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
返回:
HTTP/1.1 100 Continue
Connection: close
原因:php curl接收到HTTP 100就结束了,应该继续接收HTTP 200
解决方案:
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));

PHP and cURL: Disabling 100-continue header
Published June 15th, 2006
I've been using cURL (through PHP) to build a sort of proxy for a project I'm working on. I need to parse the returned headers (to recover the HTTP status), so had included a very simple script to do so. It had worked fine in the past, but for some reason barfed in this case. A closer look at what was being returned revealed that for some reason, Apache was prepending the ‘normal' headers with an extra response header:

HTTP/1.1 100 Continue

HTTP/1.1 200 OK Date: Fri, 09 Jun 2006 15:23:42 GMT
Server: Apache
...A bit of Googling revealed that this was to do with a header that cURL sends by default:

Expect: 100-continue

…which in turns tells Apache to send the extra header. I poked around a fair bit but couldn't quite find a workable solution short of manually removing the header in PHP, which seemed a bit clumsy. Finally, on a hunch I tried this:

curl_setopt( $curl_handle, CURLOPT_HTTPHEADER, array( 'Expect:' ) );

…which basically overrides the original ‘Expect:' header with an empty one.

Hope this helps someone.
<?php declare(strict_types=1); namespace App\Services\Platform; use App\Models\Shop; use CURLFile; use Exception; use Illuminate\Support\Facades\Redis; class KfzClient { private string $appKey; private string $appSecret; private string $method; private Shop $shop; public function __construct(Shop $shop) { $this->appKey = "448"; $this->appSecret = "82da9ff496e4244717f149d7e671fbd9bb0e0bbfc8d1041429a9244254cf2b3a"; $this->shop = $shop; if ($shop['app_key'] && $shop['app_secret']) { $this->appKey = $shop['app_key']; $this->appSecret = $shop['app_secret']; } } public function setAccessToken(string $accessToken): void { $this->shop['access_token'] = $accessToken; } protected function getCommonParams() { return [ 'appId' => $this->appKey, 'format' => 'json', 'v' => '1.0', 'signMethod' => 'md5', 'simplify' => 1, 'accessToken' => $this->shop['access_token'], 'method' => $this->method, 'datetime' => date('Y-m-d H:i:s', time()), ]; } /** * 获取授权链接 * * @param $state * @return string */ public function getAuthorizedUrl($state): string { $redirectUri = urlencode('https://token.shumaibao.com/index/kfz'); return "https://open.kongfz.com/v1/oauth2/authorize?appId=$this->appKey&responseType=code&state=$state&redirectUri=$redirectUri"; } public function decrApiNumOptimized($adminKey, $adminId, $num) { $lua = <<<LUA local adminKey = KEYS[1] local adminField = KEYS[2] local num = tonumber(KEYS[3]) local total = tonumber(redis.call('HGET', adminKey, adminField) or "0") if total < -num then return -1 end redis.call('HINCRBY', adminKey, adminField, num) return 1 LUA; $result = Redis::eval( $lua, 3, [$adminKey, $adminId, $num] ); return $result == 1; } /** * 请求接口 * @param string $method * @param array $paramMap * @return array * @throws Exception */ public function request(string $method, array $paramMap = []): array { $this->method = $method; $url = 'https://open.kongfz.com/router/rest'; $data = array_merge($this->getCommonParams(), $paramMap); $data['sign'] = $this->getSign($data); try { $response = $this->sendRequest($url, $data); if ($method === 'kongfz.image.upload') { $num = -4; if (!$this->decrApiNumOptimized('admin_api_num', $this->shop['admin_id'], $num)) { throw new Exception('接口请求次数不足'); } Redis::zadd('api_log',time(), json_encode([ 'admin_id' => $this->shop['admin_id'], 'title'=> '孔夫子-'.$this->shop['name'].'上传图片', 'num' => $num, 'createtime'=> time() ])); } } catch (Exception $e) { if ($e->getMessage() === "接口请求次数不足") { Shop::where('id', $this->shop['id'])->update([ 'status' => "0", 'warning_msg' => '接口请求次数不足' ]); throw new Exception('接口请求次数不足'); } else { return [ 'errorResponse' => [ 'code' => 500, 'subMsg' => '接口请求失败' . $e->getMessage() ], 'successResponse' => null ]; } } return $this->returnResult($response); } /** * 发送请求 * * @param string $url * @param array $params * @param bool $hasFile * * @return array * @throws Exception */ protected function sendRequest(string $url, array $params, bool $hasFile = false): array { $ch = curl_init($url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 禁用SSL证书验证 if ($hasFile) { curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: multipart/form-data']); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); } else { curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); } //超时 curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $ret = curl_exec($ch); if (false === $ret) { $err = curl_error($ch); $errno = curl_errno($ch); $info = curl_getinfo($ch); curl_close($ch); throw new Exception("请求失败,错误:{$errno},错误信息:{$err},curl信息:{$info['url']}"); } $err = curl_error($ch); if (!empty($err)) { $errno = curl_errno($ch); $info = curl_getinfo($ch); curl_close($ch); return [ 'ret' => false, 'errno' => $errno, 'msg' => $err, 'info' => $info, ]; } curl_close($ch); return [ 'ret' => true, 'msg' => $ret, ]; } /** * 上传图片 * * @param string $imagePath * * @return array * @throws Exception */ public function upload(string $imagePath): array { $this->method = 'kongfz.image.upload'; $postData = $this->getCommonParams(); $postData['bucket'] = 'book'; $postData['sign'] = $this->getSign($postData); $postData['image'] = new CURLFile($imagePath); $uri = 'https://open.kongfz.com/router/image/upload'; $response = $this->sendRequest($uri, $postData, true); return $this->returnResult($response); } /** * 返回结果 * * @param $response * @return array */ protected function returnResult($response): array { if ($response['ret']) { $body = json_decode($response['msg'], true); if (empty($body)) { return [ 'errorResponse' => [ 'code' => 500, 'subMsg' => '接口请求失败:' . $response['msg'] ], 'successResponse' => null ]; } if (!empty($body['errorResponse']['code']) && in_array($body['errorResponse']['code'], [2000, 2001])) { //授权过期 // 2000 => '无效的Access Token', // 2001 => 'Access Token已经过期', Shop::where('id', $this->shop['id']) ->update([ 'status' => 0, 'warning_msg' => '授权过期或失效' ]); } return $body; } else { return [ 'errorResponse' => [ 'code' => 500, 'subMsg' => '接口请求失败:' . $response['msg'] ], 'successResponse' => null ]; } } /** * 刷新token * @param $refreshToken * @return array * @throws \Exception */ public function refreshToken($refreshToken): array { $api = 'https://open.kongfz.com/v1/oauth2/refresh'; $data = [ 'grantType' => 'refresh_token', 'appId' => $this->appKey, 'appSecret' => $this->appSecret, 'refreshToken' => $refreshToken ]; return $this->returnResult($this->sendRequest($api, $data)); } /** * 获取access_token * @param $code * @return array[] * @throws \Exception */ public function getAccessToken($code): array { $data = [ 'code' => $code, 'grantType' => 'authorization_code', 'appId' => $this->appKey, 'appSecret' => $this->appSecret, 'redirectUri' => 'https://token.shumaibao.com/index/kfz' ]; $api = 'https://open.kongfz.com/v1/oauth2/token'; return $this->returnResult($this->sendRequest($api, $data)); } /** * 获取店铺分类 * @return array * @throws Exception */ public function getShopCategory() { $shopCategoryResponse = $this->request('kongfz.shop.category.name.list'); $shopCategoryList = []; if ($shopCategoryResponse['successResponse']) { foreach ($shopCategoryResponse['successResponse'] as $value) { $shopCategoryList[$value['value']] = $value['name']; } } return $shopCategoryList; } /** * 获取店铺运费模板 * @return array * @throws Exception */ public function getDeliveryTemplate() { $shopDeliveryResponse = $this->request('kongfz.delivery.template.name.list'); $shopDeliveryList = []; if ($shopDeliveryResponse['successResponse']) { foreach ($shopDeliveryResponse['successResponse'] as $value) { $shopDeliveryList[$value['mouldId']] = $value['mouldName']; } } return $shopDeliveryList; } // /** // * 获取错误信息 // * @param $code // * @param $msg // * @return string // */ /*private function getErrorMessage($code, $msg) { $error = [ 1000 => '授权码错误或已经过期', 1001 => '授权码不存在', // 1002 => '系统错误,建议清理浏览器缓存,稍后重试', // 1003 => '参数错误,xx参数必须', 1004 => '无效的appId', 1005 => 'appId对应的应用目前不可用', 1006 => '权限错误', 1007 => '应用回调地址错误,请检查回调地址是否为空,或含有非法字符', 1008 => '用户没有同意授权', 1009 => '调用次数受限', 1010 => '用户没有登录', 1011 => '无效的App Secret', 1012 => '无效的scope', 1013 => '无效的Refresh Token', 2000 => '无效的Access Token', 2001 => 'Access Token已经过期', // 2002 => '参数错误,缺少xx参数或xx参数错误', 2003 => '无效的签名', 2004 => '编码错误', 2005 => '用户权限不足', 2006 => '不存在的方法', 2007 => '服务不可用', // 3000 => '参数错误,缺少xx参数或xx参数错误', 3001 => '权限不足,非法访问', 3002 => '查询无结果', 3003 => '系统错误,业务逻辑错误', 500 => '接口请求失败' ]; return $error[$code] ?? $msg; }*/ public function getSign($paramMap): string { //按字母顺序排序索引 ksort($paramMap); $signString = ''; foreach ($paramMap as $key => $value) { if ($key != 'sign' && !is_array($value)) { $signString .= "$key$value"; } } switch ($paramMap['signMethod']) { case 'hmac': return strtoupper(hash_hmac('md5', $signString, $this->appSecret)); case 'md5': default: return strtoupper(hash('md5', "$this->appSecret$signString{$this->appSecret}")); } } } 改为node实现
最新发布
10-02
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值