| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- <?php
- /*
- * This file is part of the overtrue/wechat.
- *
- * (c) overtrue <i@overtrue.me>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
- /**
- * Http.php.
- *
- * This file is part of the wechat-components.
- *
- * (c) overtrue <i@overtrue.me>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
- namespace EasyWeChat\Core;
- use EasyWeChat\Core\Exceptions\HttpException;
- use EasyWeChat\Support\Log;
- use GuzzleHttp\Client as HttpClient;
- use GuzzleHttp\HandlerStack;
- use Psr\Http\Message\ResponseInterface;
- /**
- * Class Http.
- */
- class Http
- {
- /**
- * Used to identify handler defined by client code
- * Maybe useful in the future.
- */
- const USER_DEFINED_HANDLER = 'userDefined';
- /**
- * Http client.
- *
- * @var HttpClient
- */
- protected $client;
- /**
- * The middlewares.
- *
- * @var array
- */
- protected $middlewares = [];
- /**
- * @var array
- */
- protected static $globals = [
- 'curl' => [
- CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
- ],
- ];
- /**
- * Guzzle client default settings.
- *
- * @var array
- */
- protected static $defaults = [];
- /**
- * Set guzzle default settings.
- *
- * @param array $defaults
- */
- public static function setDefaultOptions($defaults = [])
- {
- self::$defaults = array_merge(self::$globals, $defaults);
- }
- /**
- * Return current guzzle default settings.
- *
- * @return array
- */
- public static function getDefaultOptions()
- {
- return self::$defaults;
- }
- /**
- * GET request.
- *
- * @param string $url
- * @param array $options
- *
- * @return ResponseInterface
- *
- * @throws HttpException
- */
- public function get($url, array $options = [])
- {
- return $this->request($url, 'GET', ['query' => $options]);
- }
- /**
- * POST request.
- *
- * @param string $url
- * @param array|string $options
- *
- * @return ResponseInterface
- *
- * @throws HttpException
- */
- public function post($url, $options = [])
- {
- $key = is_array($options) ? 'form_params' : 'body';
- return $this->request($url, 'POST', [$key => $options]);
- }
- /**
- * JSON request.
- *
- * @param string $url
- * @param string|array $options
- * @param array $queries
- * @param int $encodeOption
- *
- * @return ResponseInterface
- *
- * @throws HttpException
- */
- public function json($url, $options = [], $encodeOption = JSON_UNESCAPED_UNICODE, $queries = [])
- {
- is_array($options) && $options = json_encode($options, $encodeOption);
- return $this->request($url, 'POST', ['query' => $queries, 'body' => $options, 'headers' => ['content-type' => 'application/json']]);
- }
- /**
- * Upload file.
- *
- * @param string $url
- * @param array $files
- * @param array $form
- *
- * @return ResponseInterface
- *
- * @throws HttpException
- */
- public function upload($url, array $files = [], array $form = [], array $queries = [])
- {
- $multipart = [];
- foreach ($files as $name => $path) {
- $multipart[] = [
- 'name' => $name,
- 'contents' => fopen($path, 'r'),
- ];
- }
- foreach ($form as $name => $contents) {
- $multipart[] = compact('name', 'contents');
- }
- return $this->request($url, 'POST', ['query' => $queries, 'multipart' => $multipart]);
- }
- /**
- * Set GuzzleHttp\Client.
- *
- * @param \GuzzleHttp\Client $client
- *
- * @return Http
- */
- public function setClient(HttpClient $client)
- {
- $this->client = $client;
- return $this;
- }
- /**
- * Return GuzzleHttp\Client instance.
- *
- * @return \GuzzleHttp\Client
- */
- public function getClient()
- {
- if (!($this->client instanceof HttpClient)) {
- $this->client = new HttpClient();
- }
- return $this->client;
- }
- /**
- * Add a middleware.
- *
- * @param callable $middleware
- *
- * @return $this
- */
- public function addMiddleware(callable $middleware)
- {
- array_push($this->middlewares, $middleware);
- return $this;
- }
- /**
- * Return all middlewares.
- *
- * @return array
- */
- public function getMiddlewares()
- {
- return $this->middlewares;
- }
- /**
- * Make a request.
- *
- * @param string $url
- * @param string $method
- * @param array $options
- *
- * @return ResponseInterface
- *
- * @throws HttpException
- */
- public function request($url, $method = 'GET', $options = [])
- {
- $method = strtoupper($method);
- $options = array_merge(self::$defaults, $options);
- Log::debug('Client Request:', compact('url', 'method', 'options'));
- $options['handler'] = $this->getHandler();
- $response = $this->getClient()->request($method, $url, $options);
- Log::debug('API response:', [
- 'Status' => $response->getStatusCode(),
- 'Reason' => $response->getReasonPhrase(),
- 'Headers' => $response->getHeaders(),
- 'Body' => strval($response->getBody()),
- ]);
- return $response;
- }
- /**
- * @param \Psr\Http\Message\ResponseInterface|string $body
- *
- * @return mixed
- *
- * @throws \EasyWeChat\Core\Exceptions\HttpException
- */
- public function parseJSON($body)
- {
- if ($body instanceof ResponseInterface) {
- $body = mb_convert_encoding($body->getBody(), 'UTF-8');
- }
- // XXX: json maybe contains special chars. So, let's FUCK the WeChat API developers ...
- $body = $this->fuckTheWeChatInvalidJSON($body);
- if (empty($body)) {
- return false;
- }
- $contents = json_decode($body, true, 512, JSON_BIGINT_AS_STRING);
- Log::debug('API response decoded:', compact('contents'));
- if (JSON_ERROR_NONE !== json_last_error()) {
- throw new HttpException('Failed to parse JSON: '.json_last_error_msg());
- }
- return $contents;
- }
- /**
- * Filter the invalid JSON string.
- *
- * @param \Psr\Http\Message\StreamInterface|string $invalidJSON
- *
- * @return string
- */
- protected function fuckTheWeChatInvalidJSON($invalidJSON)
- {
- return preg_replace('/[\x00-\x1F\x80-\x9F]/u', '', trim($invalidJSON));
- }
- /**
- * Build a handler.
- *
- * @return HandlerStack
- */
- protected function getHandler()
- {
- $stack = HandlerStack::create();
- foreach ($this->middlewares as $middleware) {
- $stack->push($middleware);
- }
- if (isset(static::$defaults['handler']) && is_callable(static::$defaults['handler'])) {
- $stack->push(static::$defaults['handler'], self::USER_DEFINED_HANDLER);
- }
- return $stack;
- }
- }
|