From b905b580534441791a816016830cd813d404b6aa Mon Sep 17 00:00:00 2001 From: mkm <727897186@qq.com> Date: Tue, 12 Sep 2023 12:36:32 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=AF=E9=A3=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/controller/XunFeiController.php | 45 +++ composer.json | 3 +- composer.lock | 46 +-- .../Xfyun/Core/Config/ConfigInterface.php | 41 +++ .../Xfyun/Core/Handler/Guzzle7HttpHandler.php | 53 ++++ .../Xfyun/Core/Handler/HttpHandlerFactory.php | 45 +++ .../IFlytek/Xfyun/Core/Handler/WsHandler.php | 126 +++++++++ extend/IFlytek/Xfyun/Core/HttpClient.php | 163 +++++++++++ .../IFlytek/Xfyun/Core/Traits/ArrayTrait.php | 45 +++ .../Xfyun/Core/Traits/DecideRetryTrait.php | 96 +++++++ .../IFlytek/Xfyun/Core/Traits/JsonTrait.php | 68 +++++ .../IFlytek/Xfyun/Core/Traits/SignTrait.php | 93 ++++++ extend/IFlytek/Xfyun/Core/WsClient.php | 70 +++++ .../IFlytek/Xfyun/Speech/Config/IgrConfig.php | 110 ++++++++ .../IFlytek/Xfyun/Speech/Config/IseConfig.php | 265 ++++++++++++++++++ .../Xfyun/Speech/Config/LfasrConfig.php | 142 ++++++++++ .../IFlytek/Xfyun/Speech/Config/TcConfig.php | 76 +++++ .../IFlytek/Xfyun/Speech/Config/TtsConfig.php | 191 +++++++++++++ .../Xfyun/Speech/Constants/IgrConstants.php | 31 ++ .../Xfyun/Speech/Constants/IseConstants.php | 31 ++ .../Xfyun/Speech/Constants/LfasrConstants.php | 34 +++ .../Xfyun/Speech/Constants/TcConstants.php | 31 ++ .../Xfyun/Speech/Constants/TtsConstants.php | 30 ++ .../Xfyun/Speech/Helper/SliceIdGenerator.php | 62 ++++ extend/IFlytek/Xfyun/Speech/IgrClient.php | 111 ++++++++ extend/IFlytek/Xfyun/Speech/IseClient.php | 126 +++++++++ extend/IFlytek/Xfyun/Speech/LfasrClient.php | 231 +++++++++++++++ extend/IFlytek/Xfyun/Speech/TcClient.php | 104 +++++++ .../IFlytek/Xfyun/Speech/Traits/IgrTrait.php | 59 ++++ .../IFlytek/Xfyun/Speech/Traits/IseTrait.php | 78 ++++++ .../Xfyun/Speech/Traits/LfasrTrait.php | 56 ++++ .../IFlytek/Xfyun/Speech/Traits/TcTrait.php | 64 +++++ .../IFlytek/Xfyun/Speech/Traits/TtsTrait.php | 56 ++++ extend/IFlytek/Xfyun/Speech/TtsClient.php | 100 +++++++ vendor/composer/installed.json | 46 +-- vendor/composer/installed.php | 10 +- vendor/guzzlehttp/guzzle/CHANGELOG.md | 15 + vendor/guzzlehttp/guzzle/UPGRADING.md | 2 +- vendor/guzzlehttp/guzzle/composer.json | 4 +- vendor/guzzlehttp/guzzle/src/Client.php | 2 +- .../guzzlehttp/guzzle/src/ClientInterface.php | 2 +- .../guzzle/src/Cookie/CookieJar.php | 38 +-- .../guzzle/src/Cookie/CookieJarInterface.php | 3 +- .../guzzle/src/Handler/CurlFactory.php | 18 +- .../guzzle/src/Handler/CurlMultiHandler.php | 10 +- vendor/guzzlehttp/guzzle/src/HandlerStack.php | 4 +- .../guzzle/src/MessageFormatter.php | 2 +- .../guzzle/src/MessageFormatterInterface.php | 2 +- .../guzzle/src/RedirectMiddleware.php | 4 +- .../guzzlehttp/guzzle/src/TransferStats.php | 4 +- vendor/services.php | 2 +- 51 files changed, 2920 insertions(+), 130 deletions(-) create mode 100644 app/api/controller/XunFeiController.php create mode 100644 extend/IFlytek/Xfyun/Core/Config/ConfigInterface.php create mode 100644 extend/IFlytek/Xfyun/Core/Handler/Guzzle7HttpHandler.php create mode 100644 extend/IFlytek/Xfyun/Core/Handler/HttpHandlerFactory.php create mode 100644 extend/IFlytek/Xfyun/Core/Handler/WsHandler.php create mode 100644 extend/IFlytek/Xfyun/Core/HttpClient.php create mode 100644 extend/IFlytek/Xfyun/Core/Traits/ArrayTrait.php create mode 100644 extend/IFlytek/Xfyun/Core/Traits/DecideRetryTrait.php create mode 100644 extend/IFlytek/Xfyun/Core/Traits/JsonTrait.php create mode 100644 extend/IFlytek/Xfyun/Core/Traits/SignTrait.php create mode 100644 extend/IFlytek/Xfyun/Core/WsClient.php create mode 100644 extend/IFlytek/Xfyun/Speech/Config/IgrConfig.php create mode 100644 extend/IFlytek/Xfyun/Speech/Config/IseConfig.php create mode 100644 extend/IFlytek/Xfyun/Speech/Config/LfasrConfig.php create mode 100644 extend/IFlytek/Xfyun/Speech/Config/TcConfig.php create mode 100644 extend/IFlytek/Xfyun/Speech/Config/TtsConfig.php create mode 100644 extend/IFlytek/Xfyun/Speech/Constants/IgrConstants.php create mode 100644 extend/IFlytek/Xfyun/Speech/Constants/IseConstants.php create mode 100644 extend/IFlytek/Xfyun/Speech/Constants/LfasrConstants.php create mode 100644 extend/IFlytek/Xfyun/Speech/Constants/TcConstants.php create mode 100644 extend/IFlytek/Xfyun/Speech/Constants/TtsConstants.php create mode 100644 extend/IFlytek/Xfyun/Speech/Helper/SliceIdGenerator.php create mode 100644 extend/IFlytek/Xfyun/Speech/IgrClient.php create mode 100644 extend/IFlytek/Xfyun/Speech/IseClient.php create mode 100644 extend/IFlytek/Xfyun/Speech/LfasrClient.php create mode 100644 extend/IFlytek/Xfyun/Speech/TcClient.php create mode 100644 extend/IFlytek/Xfyun/Speech/Traits/IgrTrait.php create mode 100644 extend/IFlytek/Xfyun/Speech/Traits/IseTrait.php create mode 100644 extend/IFlytek/Xfyun/Speech/Traits/LfasrTrait.php create mode 100644 extend/IFlytek/Xfyun/Speech/Traits/TcTrait.php create mode 100644 extend/IFlytek/Xfyun/Speech/Traits/TtsTrait.php create mode 100644 extend/IFlytek/Xfyun/Speech/TtsClient.php diff --git a/app/api/controller/XunFeiController.php b/app/api/controller/XunFeiController.php new file mode 100644 index 000000000..597dfa09e --- /dev/null +++ b/app/api/controller/XunFeiController.php @@ -0,0 +1,45 @@ +app_id, $this->api_key, $this->api_secret); + + // 文本纠错请求,返回格式为json字符串 + $content = $client->request('历史上有很多注明的人物,其中唐太宗李世民就是一位。')->getBody()->getContents(); +halt($content); + // 黑白名单上传请求,成功返回true,失败返回false(失败请检查uid、res_id是否设置) + // $client = new TcClient($this->app_id, $this->api_key, $api_secret, $uid, $res_id); + // $client->listUpload($white_list, $black_list); + } +} diff --git a/composer.json b/composer.json index c94855f77..ed1534b12 100755 --- a/composer.json +++ b/composer.json @@ -36,7 +36,8 @@ "w7corp/easywechat": "^6.8", "yunwuxin/think-cron": "^3.0", "topthink/think-queue": "^3.0", - "firebase/php-jwt": "^6.8" + "firebase/php-jwt": "^6.8", + "guzzlehttp/guzzle": "^7.8" }, "require-dev": { "symfony/var-dumper": "^4.2", diff --git a/composer.lock b/composer.lock index 59f2f9dd4..9b32a9bab 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fdd9d31f9958a1263e4623ab634b9cc9", + "content-hash": "24f0a1ed76959703117054b6cb06afd8", "packages": [ { "name": "adbario/php-dot-notation", @@ -542,28 +542,17 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.7.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5" - }, + "version": "7.8.0", "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "url": "https://mirrors.tencent.com/repository/composer/guzzlehttp/guzzle/7.8.0/guzzlehttp-guzzle-7.8.0.zip", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -599,7 +588,6 @@ "GuzzleHttp\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -652,25 +640,7 @@ "rest", "web service" ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.7.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2023-05-21T14:04:53+00:00" + "time": "2023-08-27T10:20:53+00:00" }, { "name": "guzzlehttp/guzzle-services", diff --git a/extend/IFlytek/Xfyun/Core/Config/ConfigInterface.php b/extend/IFlytek/Xfyun/Core/Config/ConfigInterface.php new file mode 100644 index 000000000..46fb6518f --- /dev/null +++ b/extend/IFlytek/Xfyun/Core/Config/ConfigInterface.php @@ -0,0 +1,41 @@ +client = $client; + } + + /** + * 接收Psr-7的request对象和请求参数,返回Psr-7的response对象 + * + * @param RequestInterface $request + * @param array $options + * @return ResponseInterface + */ + public function __invoke(RequestInterface $request, array $options = []) + { + return $this->client->send($request, $options); + } +} \ No newline at end of file diff --git a/extend/IFlytek/Xfyun/Core/Handler/HttpHandlerFactory.php b/extend/IFlytek/Xfyun/Core/Handler/HttpHandlerFactory.php new file mode 100644 index 000000000..d7eb339c4 --- /dev/null +++ b/extend/IFlytek/Xfyun/Core/Handler/HttpHandlerFactory.php @@ -0,0 +1,45 @@ +client = new Client($uri); + $this->client->setTimeout($timeout); + $this->input = $input; + $this->logger = $logger ?: new NullLogger(); + } + + /** + * 发送并等待获取返回 + * + * 这是一个同步阻塞的过程,调用将持续到结果返回或者出现超时 + */ + public function sendAndReceive() + { + $result = ''; + try { + $this->logger->info("Start to send data, input: {$this->input}"); + $this->client->send($this->input); + $printSid = true; + while (true) { + $message = $this->jsonDecode($this->client->receive()); + if ($message->code !== 0) { + throw new \Exception(json_encode($message)); + } + if ($printSid) { + $this->logger->info("Receiving data, sid-[{$message->sid}]"); + $printSid = false; + } + switch ($message->data->status) { + case 1: + $result .= base64_decode($message->data->audio); + break; + case 2: + $result .= base64_decode($message->data->audio); + break 2; + } + } + $this->logger->info("Receive data successfully, total length: " . strlen($result)); + return new Response(200, [], $result); + } catch (Exception $ex) { + throw $ex; + } + } + + public function send($message = null) + { + try { + if (empty($message)) { + if (!empty($this->input)) { + $message = $this->input; + } else { + throw new Exception(); + } + } + return $this->client->send($message); + } catch (Exception $ex) { + throw $ex; + } + } + + public function receive() + { + try { + return $this->client->receive(); + } catch (Exception $ex) { + throw $ex; + } + } + + public function setLogger(LoggerInterface $logger = null) + { + $this->logger = $logger ?: new NullLogger(); + } + +} diff --git a/extend/IFlytek/Xfyun/Core/HttpClient.php b/extend/IFlytek/Xfyun/Core/HttpClient.php new file mode 100644 index 000000000..8f69c17a3 --- /dev/null +++ b/extend/IFlytek/Xfyun/Core/HttpClient.php @@ -0,0 +1,163 @@ + null, + 'httpHeaders' => [], + 'requestTimeout' => 3000, + 'retries' => 3, + 'decideRetryFunction' => null, + 'delayFunction' => null, + 'calcDelayFunction' => null + ]; + $this->httpHandler = $config['httpHandler'] ?: HttpHandlerFactory::build(); + $this->httpHeaders = $config['httpHeaders']; + $this->retries = $config['retries']; + $this->decideRetryFunction = $config['decideRetryFunction'] ?: $this->getDecideRetryFunction(); + $this->calcDelayFunction = $config['calcDelayFunction'] ?: [$this, 'calculateDelay']; + $this->delayFunction = $config['delayFunction'] ?: static function ($delay) { + usleep($delay); + }; + } + + /** + * 发送请求,并接受返回 + * + * @param RequestInterface $request + * @param array $options 请求的配置参数 + * @return Response + * @throws Exception + */ + public function sendAndReceive(RequestInterface $request, array $options = []) + { + try { + $delayFunction = $this->delayFunction; + $calcDelayFunction = $this->calcDelayFunction; + $retryAttempt = 0; + + while (true) { + try { + return call_user_func_array($this->httpHandler, [$this->applyHeaders($request), $options]); + } catch (Exception $exception) { + if ($this->decideRetryFunction) { + if (!call_user_func($this->decideRetryFunction, $exception)) { + throw $exception; + } + } + + if ($retryAttempt >= $this->retries) { + break; + } + + $delayFunction($calcDelayFunction($retryAttempt)); + $retryAttempt++; + } + } + + throw $exception; + } catch (Exception $ex) { + throw $ex; + } + } + + /** + * 将头部信息添加到请求对象中 + * + * @param $request 请求对象 + * @return RequestInterface + */ + private function applyHeaders($request) + { + return Utils::modifyRequest($request, ['set_headers' => $this->httpHeaders]); + } + + /** + * 根据重试次数计算下次重试延迟时间 + * + * @param int $attempt 重试次数 + * @return int + */ + public static function calculateDelay($attempt) + { + return min( + mt_rand(0, 1000000) + (pow(2, $attempt) * 1000000), + self::MAX_DELAY_MICROSECONDS + ); + } +} diff --git a/extend/IFlytek/Xfyun/Core/Traits/ArrayTrait.php b/extend/IFlytek/Xfyun/Core/Traits/ArrayTrait.php new file mode 100644 index 000000000..72f53f4aa --- /dev/null +++ b/extend/IFlytek/Xfyun/Core/Traits/ArrayTrait.php @@ -0,0 +1,45 @@ + $item) { + if (is_array($item)) { + $array[$key] = self::removeNull($item); + } else if (is_null($item)) { + unset($array[$key]); + } + } + return $array; + } +} diff --git a/extend/IFlytek/Xfyun/Core/Traits/DecideRetryTrait.php b/extend/IFlytek/Xfyun/Core/Traits/DecideRetryTrait.php new file mode 100644 index 000000000..d7801d3e4 --- /dev/null +++ b/extend/IFlytek/Xfyun/Core/Traits/DecideRetryTrait.php @@ -0,0 +1,96 @@ +httpRetryCodes; + $httpRetryMessages = $this->httpRetryMessages; + + return function (\Exception $ex) use ($httpRetryCodes, $httpRetryMessages, $shouldRetryMessages) { + $statusCode = $ex->getCode(); + + if (in_array($statusCode, $httpRetryCodes)) { + return true; + } + + if (!$shouldRetryMessages) { + return false; + } + + $message = ($ex instanceof RequestException && $ex->hasResponse()) + ? (string) $ex->getResponse()->getBody() + : $ex->getMessage(); + + try { + $message = $this->jsonDecode( + $message, + true + ); + } catch (\InvalidArgumentException $ex) { + return false; + } + + if (!isset($message['errors'])) { + return false; + } + + foreach ($message['errors'] as $error) { + if (in_array($error['reason'], $httpRetryMessages)) { + return true; + } + } + + return false; + }; + } +} diff --git a/extend/IFlytek/Xfyun/Core/Traits/JsonTrait.php b/extend/IFlytek/Xfyun/Core/Traits/JsonTrait.php new file mode 100644 index 000000000..cb8383b9c --- /dev/null +++ b/extend/IFlytek/Xfyun/Core/Traits/JsonTrait.php @@ -0,0 +1,68 @@ + $host, + 'date' => $date, + 'authorization' => $authrization + ]); + return $uri; + } + + /** + * 根据所提供信息返回签名 + * + * @param string $appId appid + * @param string $secretKey secretKey + * @param string $timestamp 时间戳,不传的话使用系统时间 + * @return string + */ + public static function signV1($appId, $secretKey, $timestamp = null) + { + $timestamp = $timestamp ?: time(); + $baseString = $appId . $timestamp; + $signa_origin = hash_hmac('sha1', md5($baseString), $secretKey, true); + return base64_encode($signa_origin); + } + + /** + * https调用的鉴权参数构造 + * + * @param string $appId appId + * @param string $apiKey apiKey + * @param string $curTime curTime + * @param string $param param + * @return array + */ + public static function signV2($appId, $apiKey, $param, $curTime = null) + { + if (empty($curTime)) { + $curTime = time(); + } + return [ + 'X-Appid' => $appId, + 'X-CurTime' => $curTime, + 'X-Param' => base64_encode($param), + 'X-CheckSum' => md5($apiKey . $curTime . base64_encode($param)) + ]; + } +} diff --git a/extend/IFlytek/Xfyun/Core/WsClient.php b/extend/IFlytek/Xfyun/Core/WsClient.php new file mode 100644 index 000000000..0d6035e1c --- /dev/null +++ b/extend/IFlytek/Xfyun/Core/WsClient.php @@ -0,0 +1,70 @@ + null, + ]; + $this->handler = $config['handler']; + } + + /** + * 发送请求,并接受返回 + * + * @return Response + */ + public function sendAndReceive() + { + try { + return call_user_func_array([$this->handler, 'sendAndReceive'], []); + } catch (\Exception $ex) { + throw $ex; + } + } + + public function send($message = null) + { + try { + return call_user_func_array([$this->handler, 'send'], [$message]); + } catch (\Exception $ex) { + throw $ex; + } + } + + public function receive() + { + try { + return call_user_func_array([$this->handler, 'receive'], []); + } catch (\Exception $ex) { + throw $ex; + } + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Config/IgrConfig.php b/extend/IFlytek/Xfyun/Speech/Config/IgrConfig.php new file mode 100644 index 000000000..b7f83fec3 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Config/IgrConfig.php @@ -0,0 +1,110 @@ + 'raw', + 'rate' => 16000 + ]; + + $this->ent = 'igr'; + $this->aue = $config['aue']; + $this->rate = $config['rate']; + } + + /** + * 去除null项后返回数组形式 + * + * @return array + */ + public function toArray() + { + return $this->removeNull([ + 'ent' => $this->ent, + 'aue' => $this->aue, + 'rate' => $this->rate + ]); + } + + /** + * 返回toArray的Json格式 + * + * @return string + */ + public function toJson() + { + return $this->jsonEncode($this->toArray()); + } + + /** + * @return string + */ + public function getAue() + { + return $this->aue; + } + + /** + * @return int + */ + public function getRate() + { + return $this->rate; + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Config/IseConfig.php b/extend/IFlytek/Xfyun/Speech/Config/IseConfig.php new file mode 100644 index 000000000..8c22a9477 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Config/IseConfig.php @@ -0,0 +1,265 @@ + 'cn_vip', + 'category' => 'read_sentence', + 'aus' => 1, + 'cmd' => 'ssb', + 'text' => '', + 'tte' => 'utf-8', + 'ttp_skip' => true, + 'extra_ability' => null, + 'aue' => 'raw', + 'rstcd' => 'utf8', + 'group' => 'adult', + 'check_type' => 'common', + 'grade' => 'middle', + 'rst' => 'entirety', + 'ise_unite' => '0', + 'plev' => '0' + ]; + + $this->sub = 'ise'; + $this->ent = $config['ent']; + $this->category = $config['category']; + $this->aus = $config['aus']; + $this->cmd = $config['cmd']; + $this->text = chr(239) . chr(187) . chr(191) . $config['text']; + $this->tte = $config['tte']; + $this->ttpSkip = $config['ttp_skip']; + $this->extraAbility = $config['extra_ability']; + $this->aue = $config['aue']; + $this->rstcd = $config['rstcd']; + $this->group = $config['group']; + $this->checkType = $config['check_type']; + $this->grade = $config['grade']; + $this->rst = $config['rst']; + $this->iseUnite = $config['ise_unite']; + $this->plev = $config['plev']; + } + + /** + * 去除null项后返回数组形式 + * + * @return array + */ + public function toArray() + { + return $this->removeNull([ + 'sub' => $this->sub, + 'ent' => $this->ent, + 'category' => $this->category, + 'aus' => $this->aus, + 'cmd' => $this->cmd, + 'text' => $this->text, + 'tte' => $this->tte, + 'ttp_skip' => $this->ttpSkip, + 'extra_ability' => $this->extraAbility, + 'aue' => $this->aue, + 'rstcd' => $this->rstcd, + 'group' => $this->group, + 'check_type' => $this->checkType, + 'grade' => $this->grade, + 'rst' => $this->rst, + 'ise_unite' => $this->iseUnite, + 'plev' => $this->plev + ]); + } + + /** + * 返回toArray的Json格式 + * + * @return string + */ + public function toJson() + { + return $this->jsonEncode($this->toArray()); + } + + public function setText($text) + { + $this->text = chr(239) . chr(187) . chr(191) . $text; + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Config/LfasrConfig.php b/extend/IFlytek/Xfyun/Speech/Config/LfasrConfig.php new file mode 100644 index 000000000..cdc2b65c4 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Config/LfasrConfig.php @@ -0,0 +1,142 @@ + '0', + 'has_participle' => 'false', + 'max_alternatives' => '0', + 'speaker_number' => '2', + 'has_seperate' => 'false', + 'role_type' => '1', + 'language' => 'cn', + 'pd' => null + ]; + + $this->lfasrType = $config['lfasr_type']; + $this->hasParticiple = $config['has_participle']; + $this->maxAlternatives = $config['max_alternatives']; + $this->speakerNumber = $config['speaker_number']; + $this->hasSeperate = $config['has_seperate']; + $this->roleType = $config['role_type']; + $this->language = $config['language']; + $this->pd = $config['pd']; + } + + /** + * 去除null项后返回数组形式 + * + * @return array + */ + public function toArray() + { + return $this->removeNull([ + 'lfasr_type' => $this->lfasrType, + 'has_participle' => $this->hasParticiple, + 'max_alternatives' => $this->maxAlternatives, + 'speaker_number' => $this->speakerNumber, + 'has_seperate' => $this->hasSeperate, + 'role_type' => $this->roleType, + 'language' => $this->language, + 'pd' => $this->pd + ]); + } + + /** + * 返回toArray的Json格式 + * + * @return string + */ + public function toJson() + { + return $this->jsonEncode($this->toArray()); + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Config/TcConfig.php b/extend/IFlytek/Xfyun/Speech/Config/TcConfig.php new file mode 100644 index 000000000..980417b64 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Config/TcConfig.php @@ -0,0 +1,76 @@ + 'utf8', + 'resultCompress' => 'raw', + 'resultFormat' => 'json', + 'inputEncoding' => 'utf8', + 'inputCompress' => 'raw', + 'inputFormat' => 'json' + ]; + + $this->resultEncoding = $config['resultEncoding']; + $this->resultCompress = $config['resultCompress']; + $this->resultFormat = $config['resultFormat']; + $this->inputEncoding = $config['inputEncoding']; + $this->inputCompress = $config['inputCompress']; + $this->inputFormat = $config['inputFormat']; + } + + public function toJson() + { + // TODO: Implement toJson() method. + } + + public function toArray() + { + return self::removeNull([ + 'resultEncoding' => $this->resultEncoding, + 'resultCompress' => $this->resultCompress, + 'resultFormat' => $this->resultFormat, + 'inputEncoding' => $this->inputEncoding, + 'inputCompress' => $this->inputCompress, + 'inputFormat' => $this->inputFormat + ]); + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Config/TtsConfig.php b/extend/IFlytek/Xfyun/Speech/Config/TtsConfig.php new file mode 100644 index 000000000..c9f6b3189 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Config/TtsConfig.php @@ -0,0 +1,191 @@ + 'lame', + 'sfl' => 1, + 'auf' => null, + 'vcn' => 'xiaoyan', + 'speed' => 50, + 'volume' => 50, + 'pitch' => 50, + 'bgs' => 0, + 'tte' => 'UTF8', + 'reg' => '2', + 'rdn' => '0', + 'ent' => '' + ]; + + $this->aue = $config['aue']; + $this->sfl = $config['sfl']; + $this->auf = $config['auf']; + $this->vcn = $config['vcn']; + $this->speed = $config['speed']; + $this->volume = $config['volume']; + $this->pitch = $config['pitch']; + $this->bgs = $config['bgs']; + $this->tte = $config['tte']; + $this->reg = $config['reg']; + $this->rdn = $config['rdn']; + $this->ent = $config['ent']; + } + + /** + * 去除null项后返回数组形式 + * + * @return array + */ + public function toArray() + { + return $this->removeNull([ + 'aue' => $this->aue, + 'sfl' => $this->sfl, + 'auf' => $this->auf, + 'vcn' => $this->vcn, + 'speed' => $this->speed, + 'volume' => $this->volume, + 'pitch' => $this->pitch, + 'bgs' => $this->bgs, + 'tte' => $this->tte, + 'reg' => $this->reg, + 'rdn' => $this->rdn, + 'ent' => $this->ent + ]); + } + + /** + * 返回toArray的Json格式 + * + * @return string + */ + public function toJson() + { + return $this->jsonEncode($this->toArray()); + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Constants/IgrConstants.php b/extend/IFlytek/Xfyun/Speech/Constants/IgrConstants.php new file mode 100644 index 000000000..5b9362648 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Constants/IgrConstants.php @@ -0,0 +1,31 @@ +id = LfasrConstants::ORIGIN_SLICE_ID; + } + + /** + * 返回当前切片ID,并生成下一个切片ID,赋值给对象的当前ID + * + * @return string + */ + public function getId() + { + $currentId = $this->id; + $nextId = $currentId; + $pos = strlen($currentId) - 1; + while ($pos >= 0) { + $charAtPos = $nextId[$pos]; + if ($charAtPos != 'z') { + $nextId = substr($nextId, 0, $pos) . chr((ord($charAtPos) + 1)) . substr($nextId, $pos + 1); + break; + } else { + $nextId = substr($nextId, 0, $pos) . 'a'; + $pos = $pos - 1; + } + } + $this->id = $nextId; + return $currentId; + } +} diff --git a/extend/IFlytek/Xfyun/Speech/IgrClient.php b/extend/IFlytek/Xfyun/Speech/IgrClient.php new file mode 100644 index 000000000..423a00472 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/IgrClient.php @@ -0,0 +1,111 @@ +appId = $appId; + $this->apiKey = $apiKey; + $this->apiSecret = $apiSecret; + $this->requestConfig = new IgrConfig($requestConfig); + } + + /** + * 请求并返回结果 + * + * @param string $audioPath 待识别音频路径 + * @return string + * @throws Exception + */ + public function request($audioPath) + { + $ttsHandler = new WsHandler( + $this->signUriV1(IgrConstants::URI, [ + 'appId' => $this->appId, + 'apiKey' => $this->apiKey, + 'apiSecret' => $this->apiSecret, + 'host' => IgrConstants::HOST, + 'requestLine' => IgrConstants::REQUEST_LINE, + ]), + null + ); + $client = new WsClient([ + 'handler' => $ttsHandler + ]); + + // 音频上传 + $frameNum = ceil(fileSize($audioPath) / IgrConstants::FRAME_SIZE); + $fileStream = new Stream(fopen($audioPath, 'r')); + // 发送第一帧 + $client->send($this->generateAudioInput($fileStream->read(IgrConstants::FRAME_SIZE), true, false)); + + // 发送中间帧 + for ($i = 1; $i < $frameNum; $i++) { + $client->send($this->generateAudioInput($fileStream->read(IgrConstants::FRAME_SIZE), false, false)); + usleep(4000); + } + // 发送最后一帧 + $client->send($this->generateAudioInput('', false, true)); + + // 接受数据 + $message = $this->jsonDecode($client->receive(), true); + if ($message['code'] !== 0) { + throw new Exception(json_encode($message)); + } + return $message['data']; + } +} diff --git a/extend/IFlytek/Xfyun/Speech/IseClient.php b/extend/IFlytek/Xfyun/Speech/IseClient.php new file mode 100644 index 000000000..8e9cf68ef --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/IseClient.php @@ -0,0 +1,126 @@ +appId = $appId; + $this->apiKey = $apiKey; + $this->apiSecret = $apiSecret; + $this->requestConfig = new IseConfig($requestConfig); + } + + /** + * 请求评测,并返回结果(xml格式) + * + * @param string $audioPath 待评测音频路径 + * @param string $text 待评测文本 + * @return string + */ + public function request($audioPath, $text = null) + { + $ttsHandler = new WsHandler( + $this->signUriV1(IseConstants::URI, [ + 'appId' => $this->appId, + 'apiKey' => $this->apiKey, + 'apiSecret' => $this->apiSecret, + 'host' => IseConstants::HOST, + 'requestLine' => IseConstants::REQUEST_LINE, + ]), + null + ); + $client = new WsClient([ + 'handler' => $ttsHandler + ]); + + // 参数上传 + if (!empty($text)) { + $this->requestConfig->setText($text); + } + $client->send($this->generateParamsInput($this->appId, $this->requestConfig->toArray())); + + // 音频上传 + $frameSize = ceil(fileSize($audioPath) / IseConstants::FRAME_SIZE); + $fileStream = new Stream(fopen($audioPath, 'r')); + // 发送第一帧 + $result = $client->send($this->generateAudioInput($fileStream->read(IseConstants::FRAME_SIZE), true, false)); + // 发送中间帧 + for ($i = 1; $i < $frameSize - 1; $i++) { + $client->send($this->generateAudioInput($fileStream->read(IseConstants::FRAME_SIZE), false, false)); + usleep(4000); + } + // 发送最后一帧 + $client->send($this->generateAudioInput($fileStream->read(IseConstants::FRAME_SIZE), false, true)); + + // 接受数据 + $result = ''; + while (true) { + $message = $this->jsonDecode($client->receive()); + if ($message->code !== 0) { + throw new \Exception(json_encode($message)); + } + switch ($message->data->status) { + case 1: + $result .= base64_decode($message->data->data); + break; + case 2: + $result .= base64_decode($message->data->data); + break 2; + } + } + return $result; + } +} diff --git a/extend/IFlytek/Xfyun/Speech/LfasrClient.php b/extend/IFlytek/Xfyun/Speech/LfasrClient.php new file mode 100644 index 000000000..ddc2962ab --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/LfasrClient.php @@ -0,0 +1,231 @@ +appId = $appId; + $this->secretKey = $secretKey; + $this->requestConfig = new LfasrConfig($requestConfig); + $this->client = new HttpClient([]); + $timestamp = time(); + $this->requestBody = [ + 'app_id' => $this->appId, + 'signa' => $this->signV1($this->appId, $this->secretKey, $timestamp), + 'ts' => $timestamp, + ]; + } + + /** + * 打包上传接口,封装了准备、分片上传和合并三个接口,并返回task_id + * + * @param string $filePath 文件路径 + * @return string + * @throws Exception + */ + public function combineUpload($filePath) + { + $prepareResponse = $this->prepare($filePath); + $prepareContent = $this->jsonDecode($prepareResponse->getBody()->getContents(), true); + $taskId = $prepareContent['data']; + $this->upload($taskId, $filePath); + $this->merge($taskId); + return $taskId; + } + + /** + * 准备接口 + * + * @param $file_path + * @return Response + * @throws Exception + */ + public function prepare($file_path) + { + $this->requestBody += $this->fileInfo($file_path); + $this->requestBody += $this->sliceInfo($file_path); + $this->requestBody += $this->requestConfig->toArray(); + + return $this->client->sendAndReceive( + new Request( + 'POST', + LfasrConstants::URI_PREPARE, + ['Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'], + Query::build($this->requestBody) + ) + ); + } + + /** + * 分片上传接口 + * + * @param string $taskId task_id + * @param string $filePath 文件路径 + * @return Response + * @throws Exception + */ + public function upload($taskId, $filePath) + { + $sliceIdGenerator = new SliceIdGenerator(); + $sliceInfo = $this->sliceInfo($filePath); + $fileStream = new Stream(fopen($filePath, 'r')); + + $this->requestBody += [ + 'task_id' => $taskId + ]; + + $request = new Request( + 'POST', + LfasrConstants::URI_UPLOAD + ); + + for ($i = 0; $i < $sliceInfo['slice_num']; $i++) { + $multipartStream = new MultipartStream([ + [ + 'name' => 'app_id', + 'contents' => $this->requestBody['app_id'] + ], + [ + 'name' => 'signa', + 'contents' => $this->requestBody['signa'] + ], + [ + 'name' => 'ts', + 'contents' => $this->requestBody['ts'] + ], + [ + 'name' => 'task_id', + 'contents' => $taskId, + ], + [ + 'name' => 'slice_id', + 'contents' => $sliceIdGenerator->getId() + ], + [ + 'name' => 'content', + 'contents' => $fileStream->read(LfasrConstants::SLICE_PIECE_SIZE), + 'filename' => '1.pcm' + ] + ]); + + $this->client->sendAndReceive( + $request->withBody($multipartStream) + ); + } + return new Response(200); + } + + /** + * 合并接口 + * + * @param string $taskId task_id + * @return Response + */ + public function merge($taskId) + { + return $this->process(LfasrConstants::URI_MERGE, $taskId); + } + + /** + * 查询进度接口 + * + * @param string $taskId task_id + * @return Response + */ + public function getProgress($taskId) + { + return $this->process(LfasrConstants::URI_GET_PROGRESS, $taskId); + } + + /** + * 获取结果接口 + * + * @param string $taskId task_id + * @return Response + */ + public function getResult($taskId) + { + return $this->process(LfasrConstants::URI_GET_RESULT, $taskId); + } + + /** + * 封装操作 + * + * @param string $task 操作 + * @param string $taskId task_id + * @return Response + */ + private function process($task, $taskId) + { + $this->requestBody += ['task_id' => $taskId]; + return $this->client->sendAndReceive( + new Request( + 'POST', + $task, + ['Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'], + Query::build($this->requestBody) + ) + ); + } +} diff --git a/extend/IFlytek/Xfyun/Speech/TcClient.php b/extend/IFlytek/Xfyun/Speech/TcClient.php new file mode 100644 index 000000000..d328d2bd0 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/TcClient.php @@ -0,0 +1,104 @@ +appId = $appId; + $this->apiKey = $apiKey; + $this->apiSecret = $apiSecret; + $this->uid = $uid; + $this->resId = $resId; + $this->requestConfig = new TcConfig($requestConfig); + $this->client = new HttpClient([]); + } + + public function request($text) + { + $uri = self::signUriV1(TcConstants::URI, [ + 'apiKey' => $this->apiKey, + 'apiSecret' => $this->apiSecret, + 'host' => TcConstants::HOST, + 'requestLine' => TcConstants::REQUEST_LINE + ]); + $body = self::generateInput($text, $this->appId, $this->uid, $this->resId, $this->requestConfig->toArray()); + return $this->client->sendAndReceive( + new Request( + 'POST', + $uri, + ['Content-Type' => 'application/json;charset=UTF-8'], + $body + ) + ); + } + + public function listUpload($whiteList, $blackList) + { + if (empty($this->uid) || empty($this->resId)) { + return false; + } + $response = $this->client->sendAndReceive( + new Request( + 'POST', + TcConstants::LIST_UPLOAD_URI, + ['Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'], + self::jsonEncode([ + 'common' => [ + 'app_id' => $this->appId, + 'uid' => $this->uid, + + ], + 'business' => [ + 'res_id' => $this->resId + ], + 'data' => base64_encode(self::jsonEncode([ + 'white_list' => $whiteList, + 'black_list' => $blackList + ])) + ]) + ) + ); + $content = json_decode($response->getBody()->getContents(), true); + return $content['message'] == 'success'; + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Traits/IgrTrait.php b/extend/IFlytek/Xfyun/Speech/Traits/IgrTrait.php new file mode 100644 index 000000000..216184ae2 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Traits/IgrTrait.php @@ -0,0 +1,59 @@ + !$isFirstFrame ? null : [ + "app_id" => $this->appId + ], + 'business' => !$isFirstFrame ? null : [ + "aue" => $this->requestConfig->getAue(), + "rate" => $this->requestConfig->getRate() + ], + 'data' => [ + 'status' => $isFirstFrame ? 0 : ($isLastFrame ? 2 : 1), + 'audio' => base64_encode($frameData) + ] + ]) + ); + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Traits/IseTrait.php b/extend/IFlytek/Xfyun/Speech/Traits/IseTrait.php new file mode 100644 index 000000000..c8fe85b84 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Traits/IseTrait.php @@ -0,0 +1,78 @@ + [ + 'app_id' => $appId + ], + 'business' => $iseConfigArray, + 'data' => [ + 'status' => 0 + ] + ]) + ); + } + + /** + * 根据音频数据、是否是第一帧、最后一帧,生成音频上传请求体 + * + * @param string $frameData 音频数据 + * @param boolean $isFirstFrame 是否是第一帧 + * @param boolean $isLastFrame 是否是最后一帧 + * @return string + */ + public static function generateAudioInput($frameData, $isFirstFrame = false, $isLastFrame = false) + { + return self::jsonEncode( + self::removeNull([ + 'business' => [ + "cmd" => "auw", + "aus" => $isFirstFrame ? 1 : ($isLastFrame ? 4 : 2) + ], + 'data' => [ + 'status' => $isLastFrame ? 2 : 1, + 'data' => base64_encode($frameData) + ] + ]) + ); + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Traits/LfasrTrait.php b/extend/IFlytek/Xfyun/Speech/Traits/LfasrTrait.php new file mode 100644 index 000000000..fb1bf1ce2 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Traits/LfasrTrait.php @@ -0,0 +1,56 @@ + basename($filePath), + 'file_len' => filesize($filePath) + ]; + } + + /** + * 根据文件大小和SDK默认分片大小,获取文件分片数目信息 + * + * @param string $filePath 文件路径 + * @return array + */ + public static function sliceInfo($filePath) + { + $fileSize = filesize($filePath); + return [ + 'slice_num' => ceil($fileSize / LfasrConstants::SLICE_PIECE_SIZE) + ]; + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Traits/TcTrait.php b/extend/IFlytek/Xfyun/Speech/Traits/TcTrait.php new file mode 100644 index 000000000..a6cbb944e --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Traits/TcTrait.php @@ -0,0 +1,64 @@ + [ + 'app_id' => $appId, + 'uid' => $uid, + 'status' => 3 + ], + 'parameter' => [ + 's9a87e3ec' => [ + 'res_id' => $resId, + 'result' => [ + 'encoding' => $tcConfigArray['resultEncoding'], + 'compress' => $tcConfigArray['resultCompress'], + 'format' => $tcConfigArray['resultFormat'], + ] + ] + ], + 'payload' => [ + 'input' => [ + 'encoding' => $tcConfigArray['inputEncoding'], + 'compress' => $tcConfigArray['inputCompress'], + 'format' => $tcConfigArray['inputFormat'], + 'status' => 3, + 'text' => base64_encode($text) + ] + ] + ]) + ); + } +} diff --git a/extend/IFlytek/Xfyun/Speech/Traits/TtsTrait.php b/extend/IFlytek/Xfyun/Speech/Traits/TtsTrait.php new file mode 100644 index 000000000..d2de50730 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/Traits/TtsTrait.php @@ -0,0 +1,56 @@ + [ + 'app_id' => $appId + ], + 'business' => $ttsConfigArray, + 'data' => [ + 'text' => base64_encode($text), + 'status' => 2 + ] + ]) + ); + } +} diff --git a/extend/IFlytek/Xfyun/Speech/TtsClient.php b/extend/IFlytek/Xfyun/Speech/TtsClient.php new file mode 100644 index 000000000..f4978f8b9 --- /dev/null +++ b/extend/IFlytek/Xfyun/Speech/TtsClient.php @@ -0,0 +1,100 @@ +appId = $appId; + $this->apiKey = $apiKey; + $this->apiSecret = $apiSecret; + $this->requestConfig = new TtsConfig($requestConfig); + $this->logger = $logger; + } + + /** + * 合成文本,并返回结果(字节数组)在Response->getBody()->getContents() + * + * @param string $text 待合成的文本 + * @return \GuzzleHttp\Psr7\Response + * @throws Exception + */ + public function request($text) + { + $ttsHandler = new WsHandler( + $this->signUriV1(TtsConstants::URI, [ + 'appId' => $this->appId, + 'apiKey' => $this->apiKey, + 'apiSecret' => $this->apiSecret, + 'host' => TtsConstants::HOST, + 'requestLine' => TtsConstants::REQUEST_LINE, + ]), + $this->generateInput($text, $this->appId, $this->requestConfig->toArray()) + ); + if ($this->logger) { + $ttsHandler->setLogger($this->logger); + } + $client = new WsClient([ + 'handler' => $ttsHandler + ]); + return $client->sendAndReceive(); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 1db2ac302..733c20fd4 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -560,29 +560,18 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.7.0", - "version_normalized": "7.7.0.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5" - }, + "version": "7.8.0", + "version_normalized": "7.8.0.0", "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "url": "https://mirrors.tencent.com/repository/composer/guzzlehttp/guzzle/7.8.0/guzzlehttp-guzzle-7.8.0.zip", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -603,7 +592,7 @@ "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, - "time": "2023-05-21T14:04:53+00:00", + "time": "2023-08-27T10:20:53+00:00", "type": "library", "extra": { "bamarni-bin": { @@ -620,7 +609,6 @@ "GuzzleHttp\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -673,24 +661,6 @@ "rest", "web service" ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.7.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], "install-path": "../guzzlehttp/guzzle" }, { diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 4f665f84b..25254649f 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'topthink/think', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '6fd3d2a925259e9b1d493a43d011318e538a0a1b', + 'reference' => 'c4b518e85efaed9caf7ac607e18217dfeb717f8b', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -83,9 +83,9 @@ 'dev_requirement' => false, ), 'guzzlehttp/guzzle' => array( - 'pretty_version' => '7.7.0', - 'version' => '7.7.0.0', - 'reference' => 'fb7566caccf22d74d1ab270de3551f72a58399f5', + 'pretty_version' => '7.8.0', + 'version' => '7.8.0.0', + 'reference' => '1110f66a6530a40fe7aea0378fe608ee2b2248f9', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/guzzle', 'aliases' => array(), @@ -622,7 +622,7 @@ 'topthink/think' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '6fd3d2a925259e9b1d493a43d011318e538a0a1b', + 'reference' => 'c4b518e85efaed9caf7ac607e18217dfeb717f8b', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), diff --git a/vendor/guzzlehttp/guzzle/CHANGELOG.md b/vendor/guzzlehttp/guzzle/CHANGELOG.md index 1144eb763..990b86c9e 100644 --- a/vendor/guzzlehttp/guzzle/CHANGELOG.md +++ b/vendor/guzzlehttp/guzzle/CHANGELOG.md @@ -3,6 +3,21 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version. +## 7.8.0 - 2023-08-27 + +### Added + +- Support for PHP 8.3 +- Added automatic closing of handles on `CurlFactory` object destruction + + +## 7.7.1 - 2023-08-27 + +### Changed + +- Remove the need for `AllowDynamicProperties` in `CurlMultiHandler` + + ## 7.7.0 - 2023-05-21 ### Added diff --git a/vendor/guzzlehttp/guzzle/UPGRADING.md b/vendor/guzzlehttp/guzzle/UPGRADING.md index 45417a7e1..8fa0afb5d 100644 --- a/vendor/guzzlehttp/guzzle/UPGRADING.md +++ b/vendor/guzzlehttp/guzzle/UPGRADING.md @@ -27,7 +27,7 @@ Please make sure: - Function `GuzzleHttp\Exception\RequestException::getResponseBodySummary` is removed. Use `\GuzzleHttp\Psr7\get_message_body_summary` as an alternative. - Function `GuzzleHttp\Cookie\CookieJar::getCookieValue` is removed. -- Request option `exception` is removed. Please use `http_errors`. +- Request option `exceptions` is removed. Please use `http_errors`. - Request option `save_to` is removed. Please use `sink`. - Pool option `pool_size` is removed. Please use `concurrency`. - We now look for environment variables in the `$_SERVER` super global, due to thread safety issues with `getenv`. We continue to fallback to `getenv` in CLI environments, for maximum compatibility. diff --git a/vendor/guzzlehttp/guzzle/composer.json b/vendor/guzzlehttp/guzzle/composer.json index 3207f8c3a..72defd614 100644 --- a/vendor/guzzlehttp/guzzle/composer.json +++ b/vendor/guzzlehttp/guzzle/composer.json @@ -53,8 +53,8 @@ "require": { "php": "^7.2.5 || ^8.0", "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" }, diff --git a/vendor/guzzlehttp/guzzle/src/Client.php b/vendor/guzzlehttp/guzzle/src/Client.php index 9b0d71070..bc6efc90f 100644 --- a/vendor/guzzlehttp/guzzle/src/Client.php +++ b/vendor/guzzlehttp/guzzle/src/Client.php @@ -202,7 +202,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface * * @deprecated Client::getConfig will be removed in guzzlehttp/guzzle:8.0. */ - public function getConfig(?string $option = null) + public function getConfig(string $option = null) { return $option === null ? $this->config diff --git a/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/vendor/guzzlehttp/guzzle/src/ClientInterface.php index 6aaee61af..1788e16ab 100644 --- a/vendor/guzzlehttp/guzzle/src/ClientInterface.php +++ b/vendor/guzzlehttp/guzzle/src/ClientInterface.php @@ -80,5 +80,5 @@ interface ClientInterface * * @deprecated ClientInterface::getConfig will be removed in guzzlehttp/guzzle:8.0. */ - public function getConfig(?string $option = null); + public function getConfig(string $option = null); } diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php index b4ced5a1a..fa2b10a8c 100644 --- a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php +++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php @@ -96,9 +96,6 @@ class CookieJar implements CookieJarInterface return null; } - /** - * {@inheritDoc} - */ public function toArray(): array { return \array_map(static function (SetCookie $cookie): array { @@ -106,10 +103,7 @@ class CookieJar implements CookieJarInterface }, $this->getIterator()->getArrayCopy()); } - /** - * {@inheritDoc} - */ - public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void + public function clear(string $domain = null, string $path = null, string $name = null): void { if (!$domain) { $this->cookies = []; @@ -126,25 +120,22 @@ class CookieJar implements CookieJarInterface $this->cookies = \array_filter( $this->cookies, static function (SetCookie $cookie) use ($path, $domain): bool { - return !($cookie->matchesPath($path) && - $cookie->matchesDomain($domain)); + return !($cookie->matchesPath($path) + && $cookie->matchesDomain($domain)); } ); } else { $this->cookies = \array_filter( $this->cookies, static function (SetCookie $cookie) use ($path, $domain, $name) { - return !($cookie->getName() == $name && - $cookie->matchesPath($path) && - $cookie->matchesDomain($domain)); + return !($cookie->getName() == $name + && $cookie->matchesPath($path) + && $cookie->matchesDomain($domain)); } ); } } - /** - * {@inheritDoc} - */ public function clearSessionCookies(): void { $this->cookies = \array_filter( @@ -155,9 +146,6 @@ class CookieJar implements CookieJarInterface ); } - /** - * {@inheritDoc} - */ public function setCookie(SetCookie $cookie): bool { // If the name string is empty (but not 0), ignore the set-cookie @@ -182,9 +170,9 @@ class CookieJar implements CookieJarInterface foreach ($this->cookies as $i => $c) { // Two cookies are identical, when their path, and domain are // identical. - if ($c->getPath() != $cookie->getPath() || - $c->getDomain() != $cookie->getDomain() || - $c->getName() != $cookie->getName() + if ($c->getPath() != $cookie->getPath() + || $c->getDomain() != $cookie->getDomain() + || $c->getName() != $cookie->getName() ) { continue; } @@ -286,10 +274,10 @@ class CookieJar implements CookieJarInterface $path = $uri->getPath() ?: '/'; foreach ($this->cookies as $cookie) { - if ($cookie->matchesPath($path) && - $cookie->matchesDomain($host) && - !$cookie->isExpired() && - (!$cookie->getSecure() || $scheme === 'https') + if ($cookie->matchesPath($path) + && $cookie->matchesDomain($host) + && !$cookie->isExpired() + && (!$cookie->getSecure() || $scheme === 'https') ) { $values[] = $cookie->getName().'=' .$cookie->getValue(); diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php index 50bc36398..8c55cc6f7 100644 --- a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php +++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php @@ -14,6 +14,7 @@ use Psr\Http\Message\ResponseInterface; * cookies from a file, database, etc. * * @see https://docs.python.org/2/library/cookielib.html Inspiration + * * @extends \IteratorAggregate */ interface CookieJarInterface extends \Countable, \IteratorAggregate @@ -61,7 +62,7 @@ interface CookieJarInterface extends \Countable, \IteratorAggregate * @param string|null $path Clears cookies matching a domain and path * @param string|null $name Clears cookies matching a domain, path, and name */ - public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void; + public function clear(string $domain = null, string $path = null, string $name = null): void; /** * Discard all sessions cookies. diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php index 3a6a8db26..be88d9e49 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php @@ -367,11 +367,11 @@ class CurlFactory implements CurlFactoryInterface // If it's a directory or a link to a directory use CURLOPT_CAPATH. // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO. if ( - \is_dir($options['verify']) || - ( - \is_link($options['verify']) === true && - ($verifyLink = \readlink($options['verify'])) !== false && - \is_dir($verifyLink) + \is_dir($options['verify']) + || ( + \is_link($options['verify']) === true + && ($verifyLink = \readlink($options['verify'])) !== false + && \is_dir($verifyLink) ) ) { $conf[\CURLOPT_CAPATH] = $options['verify']; @@ -627,4 +627,12 @@ class CurlFactory implements CurlFactoryInterface return \strlen($h); }; } + + public function __destruct() + { + foreach ($this->handles as $id => $handle) { + \curl_close($handle); + unset($this->handles[$id]); + } + } } diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php index f0acde145..a64e1821a 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php @@ -15,11 +15,8 @@ use Psr\Http\Message\RequestInterface; * associative array of curl option constants mapping to values in the * **curl** key of the provided request options. * - * @property resource|\CurlMultiHandle $_mh Internal use only. Lazy loaded multi-handle. - * * @final */ -#[\AllowDynamicProperties] class CurlMultiHandler { /** @@ -56,6 +53,9 @@ class CurlMultiHandler */ private $options = []; + /** @var resource|\CurlMultiHandle */ + private $_mh; + /** * This handler accepts the following options: * @@ -79,6 +79,10 @@ class CurlMultiHandler } $this->options = $options['options'] ?? []; + + // unsetting the property forces the first access to go through + // __get(). + unset($this->_mh); } /** diff --git a/vendor/guzzlehttp/guzzle/src/HandlerStack.php b/vendor/guzzlehttp/guzzle/src/HandlerStack.php index 1ce9c4b19..6cb12f07a 100644 --- a/vendor/guzzlehttp/guzzle/src/HandlerStack.php +++ b/vendor/guzzlehttp/guzzle/src/HandlerStack.php @@ -44,7 +44,7 @@ class HandlerStack * handler is provided, the best handler for your * system will be utilized. */ - public static function create(?callable $handler = null): self + public static function create(callable $handler = null): self { $stack = new self($handler ?: Utils::chooseHandler()); $stack->push(Middleware::httpErrors(), 'http_errors'); @@ -131,7 +131,7 @@ class HandlerStack * @param callable(callable): callable $middleware Middleware function * @param string $name Name to register for this middleware. */ - public function unshift(callable $middleware, ?string $name = null): void + public function unshift(callable $middleware, string $name = null): void { \array_unshift($this->stack, [$middleware, $name]); $this->cached = null; diff --git a/vendor/guzzlehttp/guzzle/src/MessageFormatter.php b/vendor/guzzlehttp/guzzle/src/MessageFormatter.php index 9b77eee83..04e9eb37a 100644 --- a/vendor/guzzlehttp/guzzle/src/MessageFormatter.php +++ b/vendor/guzzlehttp/guzzle/src/MessageFormatter.php @@ -68,7 +68,7 @@ class MessageFormatter implements MessageFormatterInterface * @param ResponseInterface|null $response Response that was received * @param \Throwable|null $error Exception that was received */ - public function format(RequestInterface $request, ?ResponseInterface $response = null, ?\Throwable $error = null): string + public function format(RequestInterface $request, ResponseInterface $response = null, \Throwable $error = null): string { $cache = []; diff --git a/vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php b/vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php index a39ac248e..47934614a 100644 --- a/vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php +++ b/vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php @@ -14,5 +14,5 @@ interface MessageFormatterInterface * @param ResponseInterface|null $response Response that was received * @param \Throwable|null $error Exception that was received */ - public function format(RequestInterface $request, ?ResponseInterface $response = null, ?\Throwable $error = null): string; + public function format(RequestInterface $request, ResponseInterface $response = null, \Throwable $error = null): string; } diff --git a/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php b/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php index f32808a75..7aa21a623 100644 --- a/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php +++ b/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php @@ -166,8 +166,8 @@ class RedirectMiddleware // not forcing RFC compliance, but rather emulating what all browsers // would do. $statusCode = $response->getStatusCode(); - if ($statusCode == 303 || - ($statusCode <= 302 && !$options['allow_redirects']['strict']) + if ($statusCode == 303 + || ($statusCode <= 302 && !$options['allow_redirects']['strict']) ) { $safeMethods = ['GET', 'HEAD', 'OPTIONS']; $requestMethod = $request->getMethod(); diff --git a/vendor/guzzlehttp/guzzle/src/TransferStats.php b/vendor/guzzlehttp/guzzle/src/TransferStats.php index 93fa334c8..2ce9e38f2 100644 --- a/vendor/guzzlehttp/guzzle/src/TransferStats.php +++ b/vendor/guzzlehttp/guzzle/src/TransferStats.php @@ -46,8 +46,8 @@ final class TransferStats */ public function __construct( RequestInterface $request, - ?ResponseInterface $response = null, - ?float $transferTime = null, + ResponseInterface $response = null, + float $transferTime = null, $handlerErrorData = null, array $handlerStats = [] ) { diff --git a/vendor/services.php b/vendor/services.php index a9e2a89ad..a718f984e 100644 --- a/vendor/services.php +++ b/vendor/services.php @@ -1,5 +1,5 @@ 'think\\app\\Service',