// +---------------------------------------------------------------------- namespace crmeb\services; /** * Class HttpService * @package crmeb\services */ class HttpService { /** * 错误信息 * @var string|null */ private static string|null $curlError; /** * header头信息 * @var string|null */ private static string|null $headerStr; /** * 请求状态 * @var int */ private static $status; /** * @return string */ public static function getCurlError() { return self::$curlError; } /** * @return mixed */ public static function getStatus() { return self::$status; } /** * 模拟GET发起请求 * @param $url * @param array $data * @param bool $header * @param int $timeout * @return bool|string */ public static function getRequest($url, $data = array(), $header = false, $timeout = 10) { if (!empty($data)) { $url .= (stripos($url, '?') === false ? '?' : '&'); $url .= (is_array($data) ? http_build_query($data) : $data); } return self::request($url, 'get', array(), $header, $timeout); } /** * 在 Swoole 下对 HTTPS 使用 PHP 流请求,避免未编译 OpenSSL 的 Swoole curl 报错 * @param string $url * @param string $method * @param array $data * @param array|false $header * @param int $timeout * @return bool|string */ private static function requestByStream(string $url, string $method, array $data, $header, int $timeout): bool|string { $method = strtoupper($method); if ($method === 'GET' && !empty($data)) { $url .= (strpos($url, '?') === false ? '?' : '&') . http_build_query($data); } $opts = [ 'http' => [ 'method' => $method, 'timeout' => (float) $timeout, 'ignore_errors' => true, ], 'ssl' => [ 'verify_peer' => false, 'verify_peer_name' => false, ], ]; if ($header !== false && is_array($header)) { $opts['http']['header'] = implode("\r\n", $header); } if ($method === 'POST' && !empty($data)) { $opts['http']['content'] = is_array($data) ? http_build_query($data) : $data; } $ctx = stream_context_create($opts); $content = @file_get_contents($url, false, $ctx); if ($content === false) { self::$status = null; self::$curlError = 'stream request failed'; return false; } $responseHeaders = $http_response_header ?? []; self::$headerStr = implode("\r\n", $responseHeaders); $code = 0; if (!empty($responseHeaders[0]) && preg_match('/\d{3}/', $responseHeaders[0], $m)) { $code = (int) $m[0]; } self::$status = ['http_code' => $code, 'header_size' => 0]; return $code === 200 ? $content : false; } /** * curl 请求 * @param $url * @param string $method * @param array $data * @param bool $header * @param int $timeout * @return bool|string */ public static function request($url, $method = 'get', $data = array(), $header = false, $timeout = 15) { self::$status = null; self::$curlError = null; self::$headerStr = null; $method = strtoupper($method); $isHttps = (strpos($url, 'https://') === 0); $inSwoole = extension_loaded('swoole') && class_exists(\Swoole\Coroutine::class, false); // Swoole 未编译 OpenSSL 时,curl 无法请求 HTTPS,改用 PHP 流 if ($inSwoole && $isHttps) { return self::requestByStream($url, $method, is_array($data) ? $data : [], $header, $timeout ?: 300); } $curl = curl_init($url); //请求方式 curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); //post请求 if ($method == 'POST') { curl_setopt($curl, CURLOPT_POSTFIELDS, is_array($data) ? http_build_query($data) : $data); } elseif ($method == 'GET' && count($data)) { $url .= '?' . http_build_query($data); curl_setopt($curl, CURLOPT_URL, $url); } //超时时间 curl_setopt($curl, CURLOPT_TIMEOUT, 300); //设置header头 if ($header !== false) curl_setopt($curl, CURLOPT_HTTPHEADER, $header); curl_setopt($curl, CURLOPT_FAILONERROR, false); //返回抓取数据 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); //输出header头信息 curl_setopt($curl, CURLOPT_HEADER, true); //TRUE 时追踪句柄的请求字符串,从 PHP 5.1.3 开始可用。这个很关键,就是允许你查看请求header curl_setopt($curl, CURLINFO_HEADER_OUT, true); //https请求 if (1 == strpos("$" . $url, "https://")) { curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); } self::$curlError = curl_error($curl); [$content, $status] = [curl_exec($curl), curl_getinfo($curl), curl_close($curl)]; self::$status = $status; self::$headerStr = trim(substr($content, 0, $status['header_size'])); $content = trim(substr($content, $status['header_size'])); return (intval($status["http_code"]) === 200) ? $content : false; } /** * 模拟POST发起请求 * @param $url * @param array $data * @param bool $header * @param int $timeout * @return bool|string */ public static function postRequest($url, $data = [], $header = false, $timeout = 10) { return self::request($url, 'post', $data, $header, $timeout); } /** * 获取header头字符串类型 * @return mixed */ public static function getHeaderStr() { return self::$headerStr; } /** * 获取header头数组类型 * @return array */ public static function getHeader() { $headArr = explode("\r\n", self::$headerStr); return $headArr; } }