_data = $data; } /** * @return string */ public function getSslCertFile(): string { return $this->ssl_cert_file; } /** * @param string $ssl_cert_file */ public function setSslCertFile(string $ssl_cert_file) { $this->ssl_cert_file = $ssl_cert_file; } /** * @return string */ public function getSslKeyFile(): string { return $this->ssl_key_file; } /** * @param string $ssl_key_file */ public function setSslKeyFile(string $ssl_key_file) { $this->ssl_key_file = $ssl_key_file; } /** */ public static function NewRequest() { static $client = null; if (!($client instanceof HttpClient)) { $client = new HttpClient(); } return $client; } /** * @param string $name * @return $this */ public function setErrorField(string $name) { $this->errorCodeField = $name; return $this; } /** * @param $bool * @return $this */ public function setUseSwoole($bool) { $this->use_swoole = $bool; return $this; } /** * @param string $name * @return $this */ public function setErrorMsgField(string $name) { $this->errorMsgField = $name; return $this; } /** * @param string $host */ public function setHost(string $host) { $this->host = $host; } /** * @param array $header */ public function setHeader(array $header) { $this->header = $header; } /** * @param $key * @param $value */ public function addHeader($key, $value) { $this->header[$key] = $value; } /** * @param null $callback */ public function setCallback($callback) { $this->callback = $callback; } /** * @param string $method */ public function setMethod(string $method) { $this->method = $method; } /** * @param string $url */ public function setUrl(string $url) { $this->url = $url; } public function setAgent(string $agent) { $this->agent = $agent; } /** * @param bool $isSSL */ public function setIsSSL(bool $isSSL) { $this->isSSL = $isSSL; if ($this->isSSL) { $this->port = 443; } } public function setPort($port) { $this->port = $port; } /** * @return bool */ public function getIsSSL() { return $this->isSSL; } /** * @param bool $isFIle * 设置返回类型 */ public function asFileStream($isFIle = true) { $this->isFileStream = $isFIle; } /** * @param $url * @param array $data * @return array|mixed|Result * @throws \Exception */ private function request($url, $data = []) { $data = $this->paramEncode($data); if ($this->use_swoole === false) { return $this->useCurl($url, $data); } else if ($this->use_swoole) { return $this->coroutine($this->parseUrlHost($url), $url, $data); } else { if (function_exists('getIsCli') && getIsCli()) { return $this->coroutine($this->parseUrlHost($url), $url, $data); } return $this->useCurl($url, $data); } } /** * @param $url * @param $data * @return array|Result|mixed */ private function useCurl($url, $data) { if ($this->isHttp($url) || $this->isHttps($url)) { return $this->curl($url, $data); } if ($this->isSSL) { return $this->curl('https://' . $this->host . '/' . $url, $data); } else { return $this->curl('http://' . $this->host . '/' . $url, $data); } } /** * @param $url * @return string */ private function parseUrlHost(&$url) { if (!empty($this->host)) { $url = $this->host . '/' . ltrim($url, '/'); } if (strpos($url, 'http') === false) { $url = 'https://' . $url; } $host = $this->cutRequestUrl($url); if ($this->checkIsIp($host)) { return $host; } return System::gethostbyname($host); } /** * @param $url * @return mixed */ private function cutRequestUrl(&$url) { $url = $this->replaceHost($url); $explode = explode('/', $url); $first = array_shift($explode); if (strpos($first, ':') !== false) { [$first, $this->port] = explode(':', $first); if ($this->port !== 443) { $this->isSSL = false; } } $url = '/' . implode('/', $explode); return $first; } /** * @param $host * @return string|string[] */ private function replaceHost($host) { if ($this->isHttp($host)) { $host = str_replace('http://', '', $host); } else if ($this->isHttps($host)) { $host = str_replace('https://', '', $host); } return $host; } /** * @param $url * @return false|int */ private function checkIsIp($url) { return preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $url); } /** * @param $url * @return bool */ private function isHttp($url) { return strpos($url, 'http://') === 0; } /** * @param $url * @return bool */ private function isHttps($url) { return strpos($url, 'https://') === 0; } /** * @param $ip * @param $url * @param array $data * @return array|mixed|Result * @throws \Exception * 使用swoole协程方式请求 */ private function coroutine($ip, $url, $data = []) { $client = $this->generate_client($ip, $url, $data); if ($client->statusCode < 0) { throw new \Exception($client->errMsg); } $body = $client->body; $client->close(); $header = $client->getHeaders(); $body = $this->resolve($header, $body); if ($client->getStatusCode() != 200) { return new Result(['code' => $client->getStatusCode(), 'message' => $body]); } return $this->structure($body, $data, $header); } /** * @return mixed */ private function getHostIp() { $host = \Co::getAddrInfo($this->host); return array_shift($host); } /** * @return int */ private function getHostPort() { if (!empty($this->port)) { return $this->port; } $port = 80; if ($this->isSSL) $port = 443; return $port; } /** * @param $host * @param $port * @param $url * @param $data * @return \Swoole\Coroutine\Http\Client */ private function generate_client($host, $url, $data) { $client = new SClient($host, $this->getHostPort(), $this->isSSL); if (!empty($this->agent)) { $this->header['User-Agent'] = $this->agent; } if (!empty($this->header)) { $client->setHeaders($this->parseHeaderMat()); } if (strtolower($this->method) == self::GET) { if (!empty($this->_data)) { $client->setData($this->_data); } var_dump($this->_data); $client->get($url . '?' . $data); } else if (strtolower($this->method) == self::PUT) { $client->setMethod('PUT'); if (!is_string($data)) { $data = json_encode($data, JSON_UNESCAPED_UNICODE); } $client->setData($data); $client->execute($url); } else if (strtolower($this->method) == self::DELETE) { $client->setMethod('DELETE'); if (!is_string($data)) { $data = json_encode($data, JSON_UNESCAPED_UNICODE); } $client->setData($data); $client->execute($url); } else { $client->post($url, $data); } return $client; } /** * @param $url * @param array $data * @return array|mixed|Result */ private function curl($url, $data = []) { try { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->createRequestUrl($url, $data)); curl_setopt($ch, CURLOPT_TIMEOUT, 5);// 超时设置 curl_setopt($ch, CURLOPT_HEADER, true); if ($headers = $this->parseHeaderMat()) { curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); } if (!empty($this->agent)) { curl_setopt($ch, CURLOPT_USERAGENT, $this->agent); } curl_setopt($ch, CURLOPT_SSLCERT, $this->getSslCertFile()); curl_setopt($ch, CURLOPT_SSLKEY, $this->getSslKeyFile()); curl_setopt($ch, CURLOPT_NOBODY, FALSE); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1); // 超时设置 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);//返回内容 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);// 跟踪重定向 curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); if ($this->method == self::POST) { curl_setopt($ch, CURLOPT_POST, 1); } if ($this->method != self::GET) { if (!empty($this->_data)) { curl_setopt($ch, CURLOPT_POSTFIELDS, $this->_data); } else { curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } } curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($this->method)); $output = curl_exec($ch); curl_close($ch); if ($output === FALSE) { $data = new Result(['code' => 500, 'message' => curl_error($ch)]); } else { [$header, $body, $status] = $this->explode($output); if ($status != 200) { $data = new Result(['code' => 500, 'message' => $body, 'header' => $header]); } else { $data = $this->structure($body, $data, $header); } } unset($ch); return $data; } catch (\Exception $exception) { return new Result(['code' => 500, 'message' => $exception->getMessage(), 'header' => []]); } } /** * @param $output * @return array */ private function explode($output) { [$header, $body] = explode("\r\n\r\n", $output, 2); if ($header == 'HTTP/1.1 100 Continue') { [$header, $body] = explode("\r\n\r\n", $body, 2); } else if (strpos($body, "\r\n\r\n") !== false) { [$header, $body] = explode("\r\n\r\n", $body, 2); } $header = explode("\r\n", $header); unset($output); $status = (int)explode(' ', trim($header[0]))[1]; $header = $this->headerFormat($header); return [$header, $this->resolve($header, $body), $status]; } /** * @param $url * @param $data * @return string */ private function createRequestUrl($url, $data) { if ($this->isGet()) { return $url . '?' . $data; } return $url; } /** * @param $data * @param $body * @return mixed */ private function resolve($data, $body) { if (is_array($body)) { return $body; } $type = $data['content-type']; if (strpos($type, 'text/html') !== false) { return $body; } else if (strpos($type, 'json') !== false) { return Help::toArray($body); } else if (strpos($type, 'xml') !== false) { return Help::toArray($body); } else if (strpos($type, 'plain') !== false) { return Help::toArray($body); } return $body; } /** * @param $headers * @return array */ private function headerFormat($headers) { $_tmp = []; foreach ($headers as $key => $val) { $trim = explode(': ', trim($val)); $_tmp[strtolower($trim[0])] = $trim[1] ?? ''; } return $_tmp; } /** * @param $body * @param $_data * @param $header * @param $statusCode * @return array|mixed|Result * 构建返回体 */ private function structure($body, $_data, $header = [], $statusCode = 200) { $this->setIsSSL(false); $this->setHeaders([]); if ($this->callback !== NULL) { $result = call_user_func($this->callback, $body, $_data, $header); $this->setCallback(null); return $result; } if (!is_array($body)) { return $body; } $result['code'] = $body[$this->errorCodeField] ?? 0; $result['message'] = $body[$this->errorMsgField] ?? 'system success.'; $result['data'] = $body; $result['header'] = $header; $result['httpStatus'] = $statusCode; return new Result($result); } /** * @return bool * check isPost Request */ public function isPost() { return strtolower($this->method) === self::POST; } /** * @return bool * * check isGet Request */ public function isGet() { return strtolower($this->method) === self::GET; } /** * @param $arr * * @return array|string * 将请求参数进行编码 */ private function paramEncode($arr) { if (!is_array($arr)) { return $arr; } $_tmp = []; foreach ($arr as $Key => $val) { $_tmp[$Key] = $val; } if ($this->isGet()) { return http_build_query($_tmp); } return $_tmp; } /** * @param $url * @param array $data * @return array|mixed|Result * @throws */ public function post($url, $data = []) { $this->setMethod(self::POST); return $this->request($url, $data); } /** * @param $url * @param array $data * @return array|mixed|Result * @throws */ public function put($url, $data = []) { $this->setMethod(self::PUT); return $this->request($url, $data); } /** * @param $url * @param array $data * @return array|mixed|Result * @throws */ public function get($url, $data = []) { $this->setMethod(self::GET); return $this->request($url, $data); } /** * @param $url * @param array $data * @return array|mixed|Result * @throws \Exception */ public function option($url, $data = []) { $this->setMethod(self::OPTIONS); return $this->request($url, $data); } /** * @param $url * @param array $data * @return array|mixed|Result * @throws \Exception */ public function delete($url, $data = []) { $this->setMethod(self::DELETE); return $this->request($url, $data); } /** * @param $url * @param array $data * @return array|mixed|Result * @throws \Exception */ public function send($url, $data = []) { return $this->request($url, $data); } /** * @return array */ private function parseHeaderMat() { if ($this->use_swoole) { return $this->header; } $headers = []; foreach ($this->header as $key => $val) { $header = $key . ':' . $val; if (in_array($header, $headers)) { continue; } $headers[] = $header; } $this->header = []; return $headers; } /** * @param array $headers * @return array */ public function setHeaders(array $headers) { if (empty($headers)) { return []; } foreach ($headers as $key => $val) { $header = $key . ':' . $val; if (in_array($header, $this->header)) { continue; } $this->header[] = $header; } return $this->header; } }