commit 8b1b10caa35b4340722e591c6aa41f3339caf64c Author: yangbowen Date: Tue Apr 2 10:25:01 2024 +0800 v1.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f1681c --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# ref: https://github.com/github/gitignore/blob/master/Composer.gitignore + +composer.phar +/vendor/ + +# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +# composer.lock + +# php-cs-fixer cache +.php_cs.cache +.php-cs-fixer.cache + +# PHPUnit cache +.phpunit.result.cache diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 0000000..ee0a6e0 --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..dd05ab0 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php-sdk.iml b/.idea/php-sdk.iml new file mode 100644 index 0000000..877f80c --- /dev/null +++ b/.idea/php-sdk.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..29059d0 --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/phpunit.xml b/.idea/phpunit.xml new file mode 100644 index 0000000..422c581 --- /dev/null +++ b/.idea/phpunit.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.openapi-generator-ignore b/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES new file mode 100644 index 0000000..c98e7dd --- /dev/null +++ b/.openapi-generator/FILES @@ -0,0 +1,49 @@ +.gitignore +.openapi-generator-ignore +.php-cs-fixer.dist.php +.travis.yml +README.md +README.md +composer.json +composer.json +docs/Api/AiApi.md +docs/Api/AiApi.md +docs/Model/ChatParam.md +docs/Model/ChatParam.md +docs/Model/ChatResult.md +docs/Model/ChatResult.md +docs/Model/ResponseOpenAPIChatResult.md +docs/Model/ResponseOpenAPIChatResult.md +docs/Model/SpeechResult.md +docs/Model/SpeechResult.md +docs/Model/StreamModule.md +docs/Model/StreamModule.md +docs/Model/SynthesizerParam.md +docs/Model/SynthesizerParam.md +git_push.sh +lib/ApiClient.php +lib/ApiException.php +lib/Api/AiApi.php +lib/Api/AiApi.php +lib/Configuration.php +lib/Configuration.php +lib/HeaderSelector.php +lib/Model/ChatParam.php +lib/Model/ChatResult.php +lib/Model/ModelInterface.php +lib/Model/ResponseOpenAPIChatResult.php +lib/Model/SpeechResult.php +lib/Model/StreamModule.php +lib/Model/SynthesizerParam.php +lib/NotifyHandler.php +lib/ObjectSerializer.php +lib/StreamApiClient.php +lib/StreamResponseHandler.php +phpunit.xml.dist +test/Api/AiApiTest.php +test/Model/ChatParamTest.php +test/Model/ChatResultTest.php +test/Model/ResponseOpenAPIChatResultTest.php +test/Model/SpeechResultTest.php +test/Model/StreamModuleTest.php +test/Model/SynthesizerParamTest.php diff --git a/.openapi-generator/VERSION b/.openapi-generator/VERSION new file mode 100644 index 0000000..73a86b1 --- /dev/null +++ b/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.0.1 \ No newline at end of file diff --git a/.openapi-generator/api-doc-php-sdk.sha256 b/.openapi-generator/api-doc-php-sdk.sha256 new file mode 100644 index 0000000..b5a3c4a --- /dev/null +++ b/.openapi-generator/api-doc-php-sdk.sha256 @@ -0,0 +1 @@ +08504b0248ce92ba6dce17da036909ea47284081432e1da812464ee138f82957 \ No newline at end of file diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..af9cf39 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,29 @@ +in(__DIR__) + ->exclude('vendor') + ->exclude('test') + ->exclude('tests') +; + +$config = new PhpCsFixer\Config(); +return $config->setRules([ + '@PSR12' => true, + 'phpdoc_order' => true, + 'array_syntax' => [ 'syntax' => 'short' ], + 'strict_comparison' => true, + 'strict_param' => true, + 'no_trailing_whitespace' => false, + 'no_trailing_whitespace_in_comment' => false, + 'braces' => false, + 'single_blank_line_at_eof' => false, + 'blank_line_after_namespace' => false, + 'no_leading_import_slash' => false, + ]) + ->setFinder($finder) +; diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..667b815 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: php +# Bionic environment has preinstalled PHP from 7.1 to 7.4 +# https://docs.travis-ci.com/user/reference/bionic/#php-support +dist: bionic +php: + - 7.4 +before_install: "composer install" +script: "vendor/bin/phpunit" diff --git a/README.md b/README.md new file mode 100644 index 0000000..7a66e14 --- /dev/null +++ b/README.md @@ -0,0 +1,174 @@ +# OpenAPIClient-php + +碳丝路数据开放平台 + +更多信息,请访问[https://open.tsl3060.com/team](https://open.tsl3060.com/team). + +## 安装与使用 + +### 要求 + +PHP 7.4 及更高版本。 +还应该与 PHP 8.0 一起使用。 + +### Composer + +要通过 [Composer](https:getcomposer.org) 安装绑定,请将以下内容添加到 `composer.json` 中: + +```json +{ + "repositories": [ + { + "type": "vcs", + "url": "https://git.tsl3060.com/openapi/low-carbon-platform.git" + } + ], + "require": { + "openapi/low-carbon-platform": "*@dev" + } +} +``` + +然后运行 `composer install` + +### 手动安装 + +下载文件并包含 `autoload.php`: + +```php +setAppid("your appId"); + // 配置证书 + $config->setPrivateKey(file_get_contents(__DIR__ . "your private key certs path")); + $config->setPublicKey(file_get_contents(__DIR__ . "your public key certs path")); + $config->setApiPublicKey(file_get_contents(__DIR__ . "your API public key certs path")); + // 访问服务路径 根据实际环境设置 + $config->setHost("https://opendev.tsl3060.com"); + + // 调用端点api示例 (调用api端点具体参照API端点文档) + $api = new ExampleApi($config); + // 设置请求参数 + $param = new ExampleParams(); + $param->setValue("exampleValue"); + // 发送请求 + $result = $api->auth($param); + print_r($result); +} +``` + + +### 流式调用API使用 +```php + /** + * 语音转文字 + * @throws ApiException + */ + public function speechToTextDemo() + { + // 获取完成基础配置的配置对象 + $config = ...; + // 创建流式API调用客户端 + $streamClient = new StreamApiClient($config); + // 获取二进制流数据(必须是16k采样率的音频二进制数据) + $binaryData = ...; + if ($binaryData) { + // 传入二进制流数据和StreamResponseHandler子类实例 + $streamClient->speechToText($binaryData, new class() extends StreamResponseHandler { + // 必须重写对应处理器的方法,实时处理返回的文本 + public function speechToTextHandle(SpeechResult $result): void + { + echo "result: " + // 文本内容 + . $result->getText() . "-" + // 句子是否结束(每句结束前,之前的内容可能会被修正) + . ($result->getSentenceEnd() ? "true" : "false") . "-" + // 句子对应音频的开始时间 + . $result->getBeginTime() . "-" + // 句子对应音频的结束时间(如果句子没有结束EndTime为null) + . $result->getEndTime() . "\n"; + } + }); + } + } + + /** + * 语音合成 + * @throws ApiException + */ + public function testTextToSpeech() + { + // 获取完成基础配置的配置对象 + $config = ...; + // 创建流式API调用客户端 + $streamClient = new StreamApiClient($config); + // 语音合成参数 + $param = new SynthesizerParam(); + // 需要转换的文本 + $param->setText("你好,我的名字叫知楠。"); + // 声音模型可选值参考地址:https://help.aliyun.com/zh/dashscope/developer-reference/model-list-old-version + $param->setModel("sambert-zhinan-v1"); + // 可选值wav、mp3,默认mp3 + // $param->setFormat("mp3"); + + // 传入语音合成参数和StreamResponseHandler子类实例 + $streamClient->synthesizer($param, new class() extends StreamResponseHandler { + // 必须重写对应处理器的方法,实时处理返回的二进制流 + public function synthesizerHandle(string $result): void + { + // 示例:将二进制流保存到文件 + $filePath = '.../temp.mp3'; + $file = fopen($filePath, 'ab'); + if ($file === false) { + die('无法打开文件'); + } + fwrite($file, $result); + fclose($file); + } + }); + } +``` + +## API端点文档 + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*AiApi* | [**chat**](docs/Api/AiApi.md#chat) | **POST** /v1/ai/chat | 元梦ai-对话 + +## 模型文档 + +- [ChatParam](docs/Model/ChatParam.md) +- [ChatResult](docs/Model/ChatResult.md) +- [ResponseOpenAPIChatResult](docs/Model/ResponseOpenAPIChatResult.md) +- [SpeechResult](docs/Model/SpeechResult.md) +- [StreamModule](docs/Model/StreamModule.md) +- [SynthesizerParam](docs/Model/SynthesizerParam.md) + +## 测试 + +要运行测试,请使用: + +```bash +composer install +vendor/bin/phpunit +``` + +## 关于 + +- API version: `0.4.8` + - Package version: `0.4.31` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..2431bcb --- /dev/null +++ b/composer.json @@ -0,0 +1,42 @@ +{ + "name": "openapi/low-carbon-platform-php", + "version": "1.0", + "description": "碳丝路数据开放平台", + "keywords": [ + "openapitools", + "openapi-generator", + "openapi", + "php", + "sdk", + "rest", + "api" + ], + "homepage": "https://openapi-generator.tech", + "license": "unlicense", + "authors": [ + { + "name": "OpenAPI", + "homepage": "https://openapi-generator.tech" + } + ], + "require": { + "php": "^7.4 || ^8.0", + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "guzzlehttp/guzzle": "^7.3", + "guzzlehttp/psr7": "^1.7 || ^2.0", + "ext-openssl": "*", + "amphp/websocket-client": "*" + }, + "require-dev": { + "phpunit/phpunit": "^8.0 || ^9.0", + "friendsofphp/php-cs-fixer": "^3.5" + }, + "autoload": { + "psr-4": { "OpenAPI\\Client\\" : "lib/" } + }, + "autoload-dev": { + "psr-4": { "OpenAPI\\Client\\Test\\" : "test/" } + } +} diff --git a/docs/Api/AiApi.md b/docs/Api/AiApi.md new file mode 100644 index 0000000..cd3300f --- /dev/null +++ b/docs/Api/AiApi.md @@ -0,0 +1,58 @@ +# OpenAPI\Client\AiApi + +| Method | HTTP request | Description | +| ------------- | ------------- | ------------- | +| [**chat()**](AiApi.md#chat) | **POST** /v1/ai/chat | 元梦ai-对话 | + + +## `chat()` + +```php +chat($chat_param): \OpenAPI\Client\Model\ResponseOpenAPIChatResult +``` + +元梦ai-对话 + +元梦ai对话 + +### 示例 + +```php +chat($chat_param); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling AiApi->chat: ', $e->getMessage(), PHP_EOL; +} +``` + +### 参数 + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **chat_param** | [**\OpenAPI\Client\Model\ChatParam**](../Model/ChatParam.md)| | [optional] | + +### 返回类型 + +[**\OpenAPI\Client\Model\ResponseOpenAPIChatResult**](../Model/ResponseOpenAPIChatResult.md) + +### HTTP请求头 + +- **Content-Type**: `application/json` +- **Accept**: `*/*` + +[[回到顶部]](#) [[返回API列表]](../../README.md#api端点文档) +[[返回模型列表]](../../README.md#模型文档) +[[返回README]](../../README.md) diff --git a/docs/Model/ChatParam.md b/docs/Model/ChatParam.md new file mode 100644 index 0000000..a0ff2fa --- /dev/null +++ b/docs/Model/ChatParam.md @@ -0,0 +1,9 @@ +# # ChatParam + +## 属性 + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**message** | **string** | message | + +[[返回模型列表]](../../README.md#模型文档) [[返回API列表]](../../README.md#api端点文档) [[返回README]](../../README.md) diff --git a/docs/Model/ChatResult.md b/docs/Model/ChatResult.md new file mode 100644 index 0000000..75da4a2 --- /dev/null +++ b/docs/Model/ChatResult.md @@ -0,0 +1,9 @@ +# # ChatResult + +## 属性 + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**content** | **string** | AI回答内容 | [optional] + +[[返回模型列表]](../../README.md#模型文档) [[返回API列表]](../../README.md#api端点文档) [[返回README]](../../README.md) diff --git a/docs/Model/ResponseOpenAPIChatResult.md b/docs/Model/ResponseOpenAPIChatResult.md new file mode 100644 index 0000000..745503e --- /dev/null +++ b/docs/Model/ResponseOpenAPIChatResult.md @@ -0,0 +1,20 @@ +# # ResponseOpenAPIChatResult + +## 属性 + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**time** | **string** | 该消息发生的时间 | [optional] +**openid** | **string** | | [optional] +**payload** | [**\OpenAPI\Client\Model\ChatResult**](ChatResult.md) | | [optional] +**sign** | **string** | | [optional] +**charset** | **string** | | [optional] +**description** | **string** | | [optional] +**response_id** | **string** | 消息的唯一ID | [optional] +**err_code** | **int** | 该消息的错误码 | [optional] +**err_msg** | **string** | 错误消息描述 | [optional] +**sub_err** | **int** | 子错误的具体的代号 | [optional] +**sub_msg** | **string** | 子错误对应的提示消息 | [optional] +**sign_type** | **string** | | [optional] + +[[返回模型列表]](../../README.md#模型文档) [[返回API列表]](../../README.md#api端点文档) [[返回README]](../../README.md) diff --git a/docs/Model/SpeechResult.md b/docs/Model/SpeechResult.md new file mode 100644 index 0000000..0adb237 --- /dev/null +++ b/docs/Model/SpeechResult.md @@ -0,0 +1,12 @@ +# # SpeechResult + +## 属性 + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**text** | **string** | 语音识别的内容 | [optional] +**begin_time** | **int** | 开始时间 | [optional] +**end_time** | **int** | 结束时间 | [optional] +**sentence_end** | **bool** | 句子是否结尾 | [optional] + +[[返回模型列表]](../../README.md#模型文档) [[返回API列表]](../../README.md#api端点文档) [[返回README]](../../README.md) diff --git a/docs/Model/StreamModule.md b/docs/Model/StreamModule.md new file mode 100644 index 0000000..49315ab --- /dev/null +++ b/docs/Model/StreamModule.md @@ -0,0 +1,10 @@ +# # StreamModule + +## 属性 + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**speech_to_text** | **string** | 语音转文字 | [optional] +**synthesis** | **string** | 语音合成 | [optional] + +[[返回模型列表]](../../README.md#模型文档) [[返回API列表]](../../README.md#api端点文档) [[返回README]](../../README.md) diff --git a/docs/Model/SynthesizerParam.md b/docs/Model/SynthesizerParam.md new file mode 100644 index 0000000..52d77a6 --- /dev/null +++ b/docs/Model/SynthesizerParam.md @@ -0,0 +1,11 @@ +# # SynthesizerParam + +## 属性 + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**text** | **string** | 需要转换为语音的文本 | +**model** | **string** | 使用的语音合成模型 | +**format** | **string** | 生成的音频格式 | + +[[返回模型列表]](../../README.md#模型文档) [[返回API列表]](../../README.md#api端点文档) [[返回README]](../../README.md) diff --git a/git_push.sh b/git_push.sh new file mode 100644 index 0000000..a66d737 --- /dev/null +++ b/git_push.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="git.tsl3060.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="openapi" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="low-carbon-platform" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/lib/Api/AiApi.php b/lib/Api/AiApi.php new file mode 100644 index 0000000..81a1007 --- /dev/null +++ b/lib/Api/AiApi.php @@ -0,0 +1,476 @@ + [ + 'application/json', + ], + ]; + + /** + * @param ClientInterface $client + * @param Configuration $config + * @param HeaderSelector $selector + * @param int $hostIndex (Optional) host index to select the list of hosts if defined in the OpenAPI spec + */ + public function __construct( + Configuration $config = null, + ClientInterface $client = null, + HeaderSelector $selector = null, + $hostIndex = 0 + ) { + $this->client = $client ?: new Client(); + $this->config = $config ?: new Configuration(); + $this->headerSelector = $selector ?: new HeaderSelector(); + $this->hostIndex = $hostIndex; + $this->apiClient = new ApiClient($config); + } + + /** + * Set the host index + * + * @param int $hostIndex Host index (required) + */ + public function setHostIndex($hostIndex): void + { + $this->hostIndex = $hostIndex; + } + + /** + * Get the host index + * + * @return int Host index + */ + public function getHostIndex() + { + return $this->hostIndex; + } + + /** + * @return Configuration + */ + public function getConfig() + { + return $this->config; + } + + /** + * Operation chat + * + * 元梦ai-对话 + * + * @param \OpenAPI\Client\Model\ChatParam $chat_param chat_param (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['chat'] to see the possible values for this operation + * + * @throws \OpenAPI\Client\ApiException on non-2xx response + * @throws \InvalidArgumentException + * @return \OpenAPI\Client\Model\ResponseOpenAPIChatResult + */ + public function chat($chat_param = null, string $contentType = self::contentTypes['chat'][0]) + { + list($response) = $this->chatWithHttpInfo($chat_param, $contentType); + return $response; + } + + /** + * Operation chatWithHttpInfo + * + * 元梦ai-对话 + * + * @param \OpenAPI\Client\Model\ChatParam $chat_param (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['chat'] to see the possible values for this operation + * + * @throws \OpenAPI\Client\ApiException on non-2xx response + * @throws \InvalidArgumentException + * @return array of \OpenAPI\Client\Model\ResponseOpenAPIChatResult, HTTP status code, HTTP response headers (array of strings) + */ + public function chatWithHttpInfo($chat_param = null, string $contentType = self::contentTypes['chat'][0]) + { + $request = $this->chatRequest($chat_param, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + // 响应预处理 + $data = json_decode($response->getBody(), true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new ApiException("JSON解码错误: " . json_last_error_msg()); + } + + // 验证签名 + if (!$this->apiClient->verifySign($data)) { + throw new ApiException( + 'Verification failed', + $response->getStatusCode(), + $response->getHeaders(), + $response->getBody() + ); + } + + $statusCode = $response->getStatusCode(); + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + switch($statusCode) { + case 200: + if ('\OpenAPI\Client\Model\ResponseOpenAPIChatResult' === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ('\OpenAPI\Client\Model\ResponseOpenAPIChatResult' !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, '\OpenAPI\Client\Model\ResponseOpenAPIChatResult', []), + $response->getStatusCode(), + $response->getHeaders() + ]; + } + + $returnType = '\OpenAPI\Client\Model\ResponseOpenAPIChatResult'; + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OpenAPI\Client\Model\ResponseOpenAPIChatResult', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + break; + } + throw $e; + } + } + + /** + * Operation chatAsync + * + * 元梦ai-对话 + * + * @param \OpenAPI\Client\Model\ChatParam $chat_param (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['chat'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function chatAsync($chat_param = null, string $contentType = self::contentTypes['chat'][0]) + { + return $this->chatAsyncWithHttpInfo($chat_param, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation chatAsyncWithHttpInfo + * + * 元梦ai-对话 + * + * @param \OpenAPI\Client\Model\ChatParam $chat_param (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['chat'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function chatAsyncWithHttpInfo($chat_param = null, string $contentType = self::contentTypes['chat'][0]) + { + $returnType = '\OpenAPI\Client\Model\ResponseOpenAPIChatResult'; + $request = $this->chatRequest($chat_param, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'chat' + * + * @param \OpenAPI\Client\Model\ChatParam $chat_param (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['chat'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function chatRequest($chat_param = null, string $contentType = self::contentTypes['chat'][0]) + { + + + + $resourcePath = '/v1/ai/chat'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + + + $headers = $this->headerSelector->selectHeaders( + ['*/*', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (isset($chat_param)) { + if (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the body + $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($chat_param)); + } else { + $httpBody = $chat_param; + } + } elseif (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + + // 初始化额外的参数 + $additionalParams = [ + 'payload' => json_decode($httpBody, true), + 'path' => $resourcePath, + 'charset' => $this->config->getCharset(), + 'time' => date($this->config->getDataFormat(), time()), + 'sign_type' => $this->config->getSignType(), + 'access_token' => '', + 'app_id' => $this->config->getAppid(), + 'sign' => '' + ]; + + $additionalParams['sign'] = $this->apiClient->requestSign($additionalParams); + + // 根据Content-Type决定如何合并额外的参数 + if (stripos($headers['Content-Type'], 'application/json') !== false) { + // 如果是application/json,将额外的参数合并到现有的JSON对象中 + $existingParams = json_decode($httpBody, true); + if ($existingParams === null) { + $existingParams = []; + } + $mergedParams = array_merge($existingParams, $additionalParams); + $httpBody = json_encode($mergedParams, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } else { + // 如果不是application/json,将额外的参数合并到现有的查询字符串中 + $existingParams = []; + if ($httpBody != null) { + parse_str($httpBody, $existingParams); + } + $mergedParams = array_merge($existingParams, $additionalParams); + $httpBody = ObjectSerializer::buildQuery($mergedParams); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Create http client option + * + * @throws \RuntimeException on file opening failure + * @return array of http client options + */ + protected function createHttpClientOption() + { + $options = []; + if ($this->config->getDebug()) { + $options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a'); + if (!$options[RequestOptions::DEBUG]) { + throw new \RuntimeException('Failed to open the debug file: ' . $this->config->getDebugFile()); + } + } + + return $options; + } +} diff --git a/lib/ApiClient.php b/lib/ApiClient.php new file mode 100644 index 0000000..004eb76 --- /dev/null +++ b/lib/ApiClient.php @@ -0,0 +1,330 @@ +config = $config; + // 初始化路由 + } + + /** + * 执行默认数据类型的通知方法 + * @param string $raw 源数据 + * @param NotifyHandler $handler 通知处理器 + * @return string 处理结果 + * @throws ApiException + */ + public function notifyRunDefault(string $raw, NotifyHandler $handler): string + { + return $this->notifyRun($raw, "application/json", "application/json", $handler); + } + + /** + * 执行通知方法 + * @param string $raw 源数据 + * @param string $contentType 服务端发送的数据类型 + * @param string $accept 服务端需要接收的数据类型 + * @param NotifyHandler $handler 通知处理器 + * @return string 处理结果 + * @throws ApiException + */ + public function notifyRun(string $raw, string $contentType, string $accept, NotifyHandler $handler): string + { + if (empty(trim($raw))) { + throw new ApiException("内容为空"); + } + // 解析请求数据 + $notifyRequest = null; + if ($contentType === 'application/json') { + $content = json_decode($raw); + $notifyRequest = ObjectSerializer::deserialize($content, '\OpenAPI\Client\Model\AsyncNotifyRequest', []); + } elseif ($contentType === 'application/xml') { + // XML格式暂不支持 + throw new ApiException("暂不支持的格式: " . $contentType); + } else { + throw new ApiException("不支持的数据格式: " . $contentType); + } + if ($notifyRequest instanceof AsyncNotifyRequest) { + // 检查签名类型 + if ($notifyRequest->getSignType() !== 'RSA') { + throw new ApiException("不支持的签名类型: " . $notifyRequest->getSignType()); + } + // 验证通知 + if (!$this->verifyNotify($notifyRequest)) { + throw new ApiException("签名错误: " . json_encode($notifyRequest)); + } + $resp = new AsyncNotifyResponse(); + // 调用对应通知处理方法 + $module = $notifyRequest->getModule(); + // 检查路径是否在路由表中 + if (array_key_exists($module, $this->notifyRouter)) { + // 调用对应的函数 + $function = $this->notifyRouter[$module]; + try { + $answer = $function($handler, $notifyRequest->getPayload()); + $resp->setPayload($answer); + $resp->setResult("ok"); + } catch (Exception $e) { + echo sprintf("调用通知回调处理方法失败:在 %s 的第 %d 行: [%d] %s", $e->getFile(), $e->getLine(), $e->getCode(), $e->getMessage()); + $resp->setResult("fail"); + } + $resp->setAnswerId($notifyRequest->getNotifyId()); + $resp->setAppId($this->config->getAppid()); + $resp->setTime(date($this->config->getDataFormat(), time())); + $resp->setSignType($this->config->getSignType()); + $resp->setCharset($this->config->getCharset()); + $resp->setSign($this->answerSign($resp)); + + //处理完成 + if ($accept === "application/json") { + return json_encode($resp); + } else if ($accept === "application/xml") { + //返回XML格式 + throw new ApiException("暂不支持的数据接收格式:" . $accept); + } else { + //未知的格式 + throw new ApiException("未知的数据接收格式:" . $accept); + } + } else { + // 如果路由不存在,抛出异常或进行相应的错误处理 + throw new ApiException("路由未定义: " . $module); + } + } else { + throw new ApiException("对象格式错误"); + } + } + + /** + * @throws ApiException + */ + function requestSign($params): string + { + // 获取payload和其他参数 + $payload = $params['payload']; + $content = sprintf( + "%s&%s&%s&%s&%s&%s&%s", + $params['path'], + $params['app_id'], + $params['access_token'], + $params['sign_type'], + $params['charset'], + $params['time'], + $this->object2LinkStr($payload) + ); + + // 计算SHA-256摘要 + $bytesContent = mb_convert_encoding($content, $params['charset']); + $digestData = hash('sha256', $bytesContent, true); + + // 加载私钥 + $privateKeyContent = $this->config->getPrivateKey(); + $privateKey = openssl_pkey_get_private($privateKeyContent); + if ($privateKey === false) { + while ($errMsg = openssl_error_string()) { + error_log($errMsg); + } + throw new ApiException('Invalid private key'); + } + + // 使用SHA256withRSA签名 + $signature = ''; + $result = openssl_sign($digestData, $signature, $privateKey, OPENSSL_ALGO_SHA256); + if ($result === false) { + throw new ApiException('Failed to sign data'); + } + + // 检查PHP版本,只有在PHP 7.x版本中才释放私钥资源 + if (PHP_VERSION_ID < 80000) { + openssl_pkey_free($privateKey); + } + + // 返回十六进制编码的签名 + return bin2hex($signature); + } + + + /** + * @throws ApiException + */ + private function answerSign(AsyncNotifyResponse $resp): string + { + // 获取payload和其他参数 + $content = sprintf( + "%s&%s&%s&%s&%s&%s&%s", + $resp->getAnswerId(), + $resp->getAppId(), + $resp->getResult(), + $resp->getSignType(), + $resp->getCharset(), + $resp->getTime(), + $this->object2LinkStr($resp->getPayload()) + ); + // 计算SHA-256摘要 + $bytesContent = mb_convert_encoding($content, $resp->getCharset()); + $digestData = hash('sha256', $bytesContent, true); + // 加载私钥 + $privateKeyContent = $this->config->getPrivateKey(); + $privateKey = openssl_pkey_get_private($privateKeyContent); + if ($privateKey === false) { + while ($errMsg = openssl_error_string()) { + error_log($errMsg); + } + throw new ApiException('Invalid private key'); + } + // 使用SHA256withRSA签名 + $signature = ''; + $result = openssl_sign($digestData, $signature, $privateKey, OPENSSL_ALGO_SHA256); + if ($result === false) { + throw new ApiException('Failed to sign data'); + } + // 检查PHP版本,只有在PHP 7.x版本中才释放私钥资源 + if (PHP_VERSION_ID < 80000) { + openssl_pkey_free($privateKey); + } + // 返回十六进制编码的签名 + return bin2hex($signature); + } + + function object2LinkStr($o): string + { + if ($o === null) { + return ""; + } + // 处理简单值类型 + if (is_numeric($o) || is_string($o)) { + return strval($o); + } + if (is_bool($o)) { + return $o ? 'true' : 'false'; + } + // 处理数组 + if (is_array($o)) { + // 检查数组是否是关联数组 + if (array_keys($o) !== range(0, count($o) - 1)) { + // 对关联数组按键进行字典排序 + ksort($o); + $result = []; + foreach ($o as $key => $value) { + // 递归处理数组内部的每个元素 + $result[] = $key . "=" . $this->object2LinkStr($value); + } + return implode("&", $result); + } else { + // 非关联数组,按元素顺序处理 + return implode(",", array_map([$this, 'object2LinkStr'], $o)); + } + } + // 其他类型数据处理 + return strval($o); + } + + /** + * @throws ApiException + */ + function verifySign($data): bool + { + // 检查签名类型 + $signType = $data['sign_type'] ?? null; + if ($signType !== 'RSA') { + throw new ApiException("不支持的签名类型: {$signType}"); + } + + $waitStr = $this->object2LinkStr($data['payload']); + $formatStr = sprintf( + "%s&%s&%s&%s&%s&%s&%s&%s&%s&%s&%s", + $data['response_id'] ?? '', + $data['err_code'] ?? '', + $data['err_msg'] ?? '', + $data['sub_err'] ?? '', + $data['sub_msg'] ?? '', + $data['time'] ?? '', + $data['open_id'] ?? '', + $data['sign_type'] ?? '', + $data['charset'] ?? 'UTF-8', + $data['description'] ?? '', + $waitStr + ); + $bodyCharset = $data['charset'] ?? 'UTF-8'; + $bytesContent = mb_convert_encoding($formatStr, $bodyCharset); + $digestData = hash('sha256', $bytesContent, true); + $pubKey = openssl_pkey_get_public($this->config->getApiPublicKey()); + if ($pubKey === false) { + while ($errMsg = openssl_error_string()) { + error_log($errMsg); + } + return false; + } + $signature = openssl_verify($digestData, hex2bin($data['sign']), $pubKey, OPENSSL_ALGO_SHA256); + // 检查PHP版本,只有在PHP 7.x版本中才释放公钥资源 + if (PHP_VERSION_ID < 80000) { + openssl_free_key($pubKey); + } + return $signature === 1; + } + + /** + * @throws ApiException + */ + function verifyNotify(AsyncNotifyRequest $request): bool + { + // 检查签名类型 + $signType = $request->getSignType() ?? null; + if ($signType !== 'RSA') { + throw new ApiException("不支持的签名类型: {$signType}"); + } + + // 检查payload + $waitStr = $this->object2LinkStr($request->getPayload()); + $formatStr = sprintf( + "%s&%s&%s&%s&%s&%s&%s&%s&%s&%s&%s&%s&%s", + $request->getNotifyId() ?? '', + $request->getSourceId() ?? '', + $request->getAppId() ?? '', + $request->getErrCode() ?? '', + $request->getErrMsg() ?? '', + $request->getSubErr() ?? '', + $request->getSubMsg() ?? '', + $request->getTime() ?? '', + $request->getOpenId() ?? '', + $request->getSignType() ?? '', + $request->getCharset() ?? 'UTF-8', + $request->getDescription() ?? '', + $waitStr + ); + $bodyCharset = $request->getCharset() ?? 'UTF-8'; + $bytesContent = mb_convert_encoding($formatStr, $bodyCharset); + $digestData = hash('sha256', $bytesContent, true); + $pubKey = openssl_pkey_get_public($this->config->getApiPublicKey()); + if ($pubKey === false) { + while ($errMsg = openssl_error_string()) { + error_log($errMsg); + } + return false; + } + $signature = openssl_verify($digestData, hex2bin($request->getSign()), $pubKey, OPENSSL_ALGO_SHA256); + // 检查PHP版本,只有在PHP 7.x版本中才释放公钥资源 + if (PHP_VERSION_ID < 80000) { + openssl_free_key($pubKey); + } + return $signature === 1; + } + +} \ No newline at end of file diff --git a/lib/ApiException.php b/lib/ApiException.php new file mode 100644 index 0000000..2f4bc5a --- /dev/null +++ b/lib/ApiException.php @@ -0,0 +1,119 @@ +responseHeaders = $responseHeaders; + $this->responseBody = $responseBody; + } + + /** + * Gets the HTTP response header + * + * @return string[]|null HTTP response header + */ + public function getResponseHeaders() + { + return $this->responseHeaders; + } + + /** + * Gets the HTTP body of the server response either as Json or string + * + * @return \stdClass|string|null HTTP body of the server response either as \stdClass or string + */ + public function getResponseBody() + { + return $this->responseBody; + } + + /** + * Sets the deserialized response object (during deserialization) + * + * @param mixed $obj Deserialized response object + * + * @return void + */ + public function setResponseObject($obj) + { + $this->responseObject = $obj; + } + + /** + * Gets the deserialized response object (during deserialization) + * + * @return mixed the deserialized response object + */ + public function getResponseObject() + { + return $this->responseObject; + } +} diff --git a/lib/Configuration.php b/lib/Configuration.php new file mode 100644 index 0000000..99dd3d6 --- /dev/null +++ b/lib/Configuration.php @@ -0,0 +1,623 @@ +privateKey; + } + + public function setPrivateKey(string $privateKey): void + { + $this->privateKey = $privateKey; + } + + public function getPublicKey(): string + { + return $this->publicKey; + } + + public function setPublicKey(string $publicKey): void + { + $this->publicKey = $publicKey; + } + + public function getApiPublicKey(): string + { + return $this->apiPublicKey; + } + + public function setApiPublicKey(string $apiPublicKey): void + { + $this->apiPublicKey = $apiPublicKey; + } + + public function getAppid(): string + { + return $this->appid; + } + + public function setAppid(string $appid): void + { + $this->appid = $appid; + } + + public function getSignType(): string + { + return $this->signType; + } + + public function setSignType(string $signType): void + { + $this->signType = $signType; + } + + public function getCharset(): string + { + return $this->charset; + } + + public function setCharset(string $charset): void + { + $this->charset = $charset; + } + + public function getDataFormat(): string + { + return $this->dataFormat; + } + + public function setDataFormat(string $dataFormat): void + { + $this->dataFormat = $dataFormat; + } + + public function getTimeout(): int + { + return $this->timeout; + } + + public function setTimeout(int $timeout): void + { + $this->timeout = $timeout; + } + + + /** + * Constructor + */ + public function __construct() + { + $this->tempFolderPath = sys_get_temp_dir(); + } + + /** + * Sets API key + * + * @param string $apiKeyIdentifier API key identifier (authentication scheme) + * @param string $key API key or token + * + * @return $this + */ + public function setApiKey($apiKeyIdentifier, $key) + { + $this->apiKeys[$apiKeyIdentifier] = $key; + return $this; + } + + /** + * Gets API key + * + * @param string $apiKeyIdentifier API key identifier (authentication scheme) + * + * @return null|string API key or token + */ + public function getApiKey($apiKeyIdentifier) + { + return isset($this->apiKeys[$apiKeyIdentifier]) ? $this->apiKeys[$apiKeyIdentifier] : null; + } + + /** + * Sets the prefix for API key (e.g. Bearer) + * + * @param string $apiKeyIdentifier API key identifier (authentication scheme) + * @param string $prefix API key prefix, e.g. Bearer + * + * @return $this + */ + public function setApiKeyPrefix($apiKeyIdentifier, $prefix) + { + $this->apiKeyPrefixes[$apiKeyIdentifier] = $prefix; + return $this; + } + + /** + * Gets API key prefix + * + * @param string $apiKeyIdentifier API key identifier (authentication scheme) + * + * @return null|string + */ + public function getApiKeyPrefix($apiKeyIdentifier) + { + return isset($this->apiKeyPrefixes[$apiKeyIdentifier]) ? $this->apiKeyPrefixes[$apiKeyIdentifier] : null; + } + + /** + * Sets the access token for OAuth + * + * @param string $accessToken Token for OAuth + * + * @return $this + */ + public function setAccessToken($accessToken) + { + $this->accessToken = $accessToken; + return $this; + } + + /** + * Gets the access token for OAuth + * + * @return string Access token for OAuth + */ + public function getAccessToken() + { + return $this->accessToken; + } + + /** + * Sets boolean format for query string. + * + * @param string $booleanFormatForQueryString Boolean format for query string + * + * @return $this + */ + public function setBooleanFormatForQueryString(string $booleanFormat) + { + $this->booleanFormatForQueryString = $booleanFormat; + + return $this; + } + + /** + * Gets boolean format for query string. + * + * @return string Boolean format for query string + */ + public function getBooleanFormatForQueryString(): string + { + return $this->booleanFormatForQueryString; + } + + /** + * Sets the username for HTTP basic authentication + * + * @param string $username Username for HTTP basic authentication + * + * @return $this + */ + public function setUsername($username) + { + $this->username = $username; + return $this; + } + + /** + * Gets the username for HTTP basic authentication + * + * @return string Username for HTTP basic authentication + */ + public function getUsername() + { + return $this->username; + } + + /** + * Sets the password for HTTP basic authentication + * + * @param string $password Password for HTTP basic authentication + * + * @return $this + */ + public function setPassword($password) + { + $this->password = $password; + return $this; + } + + /** + * Gets the password for HTTP basic authentication + * + * @return string Password for HTTP basic authentication + */ + public function getPassword() + { + return $this->password; + } + + /** + * Sets the host + * + * @param string $host Host + * + * @return $this + */ + public function setHost($host) + { + $this->host = $host; + return $this; + } + + /** + * Gets the host + * + * @return string Host + */ + public function getHost() + { + return $this->host; + } + + /** + * Sets the user agent of the api client + * + * @param string $userAgent the user agent of the api client + * + * @throws \InvalidArgumentException + * @return $this + */ + public function setUserAgent($userAgent) + { + if (!is_string($userAgent)) { + throw new \InvalidArgumentException('User-agent must be a string.'); + } + + $this->userAgent = $userAgent; + return $this; + } + + /** + * Gets the user agent of the api client + * + * @return string user agent + */ + public function getUserAgent() + { + return $this->userAgent; + } + + /** + * Sets debug flag + * + * @param bool $debug Debug flag + * + * @return $this + */ + public function setDebug($debug) + { + $this->debug = $debug; + return $this; + } + + /** + * Gets the debug flag + * + * @return bool + */ + public function getDebug() + { + return $this->debug; + } + + /** + * Sets the debug file + * + * @param string $debugFile Debug file + * + * @return $this + */ + public function setDebugFile($debugFile) + { + $this->debugFile = $debugFile; + return $this; + } + + /** + * Gets the debug file + * + * @return string + */ + public function getDebugFile() + { + return $this->debugFile; + } + + /** + * Sets the temp folder path + * + * @param string $tempFolderPath Temp folder path + * + * @return $this + */ + public function setTempFolderPath($tempFolderPath) + { + $this->tempFolderPath = $tempFolderPath; + return $this; + } + + /** + * Gets the temp folder path + * + * @return string Temp folder path + */ + public function getTempFolderPath() + { + return $this->tempFolderPath; + } + + /** + * Gets the default configuration instance + * + * @return Configuration + */ + public static function getDefaultConfiguration() + { + if (self::$defaultConfiguration === null) { + self::$defaultConfiguration = new Configuration(); + } + + return self::$defaultConfiguration; + } + + /** + * Sets the default configuration instance + * + * @param Configuration $config An instance of the Configuration Object + * + * @return void + */ + public static function setDefaultConfiguration(Configuration $config) + { + self::$defaultConfiguration = $config; + } + + /** + * Gets the essential information for debugging + * + * @return string The report for debugging + */ + public static function toDebugReport() + { + $report = 'PHP SDK (OpenAPI\Client) Debug Report:' . PHP_EOL; + $report .= ' OS: ' . php_uname() . PHP_EOL; + $report .= ' PHP Version: ' . PHP_VERSION . PHP_EOL; + $report .= ' The version of the OpenAPI document: 0.4.8' . PHP_EOL; + $report .= ' SDK Package Version: 0.4.31' . PHP_EOL; + $report .= ' Temp Folder Path: ' . self::getDefaultConfiguration()->getTempFolderPath() . PHP_EOL; + + return $report; + } + + /** + * Get API key (with prefix if set) + * + * @param string $apiKeyIdentifier name of apikey + * + * @return null|string API key with the prefix + */ + public function getApiKeyWithPrefix($apiKeyIdentifier) + { + $prefix = $this->getApiKeyPrefix($apiKeyIdentifier); + $apiKey = $this->getApiKey($apiKeyIdentifier); + + if ($apiKey === null) { + return null; + } + + if ($prefix === null) { + $keyWithPrefix = $apiKey; + } else { + $keyWithPrefix = $prefix . ' ' . $apiKey; + } + + return $keyWithPrefix; + } + + /** + * Returns an array of host settings + * + * @return array an array of host settings + */ + public function getHostSettings() + { + return [ + [ + "url" => "https://opendev.tsl3060.com", + "description" => "沙箱服务", + ] + ]; + } + + /** + * Returns URL based on host settings, index and variables + * + * @param array $hostSettings array of host settings, generated from getHostSettings() or equivalent from the API clients + * @param int $hostIndex index of the host settings + * @param array|null $variables hash of variable and the corresponding value (optional) + * @return string URL based on host settings + */ + public static function getHostString(array $hostsSettings, $hostIndex, array $variables = null) + { + if (null === $variables) { + $variables = []; + } + + // check array index out of bound + if ($hostIndex < 0 || $hostIndex >= count($hostsSettings)) { + throw new \InvalidArgumentException("Invalid index $hostIndex when selecting the host. Must be less than ".count($hostsSettings)); + } + + $host = $hostsSettings[$hostIndex]; + $url = $host["url"]; + + // go through variable and assign a value + foreach ($host["variables"] ?? [] as $name => $variable) { + if (array_key_exists($name, $variables)) { // check to see if it's in the variables provided by the user + if (!isset($variable['enum_values']) || in_array($variables[$name], $variable["enum_values"], true)) { // check to see if the value is in the enum + $url = str_replace("{".$name."}", $variables[$name], $url); + } else { + throw new \InvalidArgumentException("The variable `$name` in the host URL has invalid value ".$variables[$name].". Must be ".join(',', $variable["enum_values"])."."); + } + } else { + // use default value + $url = str_replace("{".$name."}", $variable["default_value"], $url); + } + } + + return $url; + } + + /** + * Returns URL based on the index and variables + * + * @param int $index index of the host settings + * @param array|null $variables hash of variable and the corresponding value (optional) + * @return string URL based on host settings + */ + public function getHostFromSettings($index, $variables = null) + { + return self::getHostString($this->getHostSettings(), $index, $variables); + } +} diff --git a/lib/HeaderSelector.php b/lib/HeaderSelector.php new file mode 100644 index 0000000..f75d27f --- /dev/null +++ b/lib/HeaderSelector.php @@ -0,0 +1,245 @@ +selectAcceptHeader($accept); + if ($accept !== null) { + $headers['Accept'] = $accept; + } + + if (!$isMultipart) { + if($contentType === '') { + $contentType = 'application/json'; + } + + $headers['Content-Type'] = $contentType; + } + + return $headers; + } + + /** + * Return the header 'Accept' based on an array of Accept provided. + * + * @param string[] $accept Array of header + * + * @return null|string Accept (e.g. application/json) + */ + private function selectAcceptHeader(array $accept): ?string + { + # filter out empty entries + $accept = array_filter($accept); + + if (count($accept) === 0) { + return null; + } + + # If there's only one Accept header, just use it + if (count($accept) === 1) { + return reset($accept); + } + + # If none of the available Accept headers is of type "json", then just use all them + $headersWithJson = preg_grep('~(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$~', $accept); + if (count($headersWithJson) === 0) { + return implode(',', $accept); + } + + # If we got here, then we need add quality values (weight), as described in IETF RFC 9110, Items 12.4.2/12.5.1, + # to give the highest priority to json-like headers - recalculating the existing ones, if needed + return $this->getAcceptHeaderWithAdjustedWeight($accept, $headersWithJson); + } + + /** + * Create an Accept header string from the given "Accept" headers array, recalculating all weights + * + * @param string[] $accept Array of Accept Headers + * @param string[] $headersWithJson Array of Accept Headers of type "json" + * + * @return string "Accept" Header (e.g. "application/json, text/html; q=0.9") + */ + private function getAcceptHeaderWithAdjustedWeight(array $accept, array $headersWithJson): string + { + $processedHeaders = [ + 'withApplicationJson' => [], + 'withJson' => [], + 'withoutJson' => [], + ]; + + foreach ($accept as $header) { + + $headerData = $this->getHeaderAndWeight($header); + + if (stripos($headerData['header'], 'application/json') === 0) { + $processedHeaders['withApplicationJson'][] = $headerData; + } elseif (in_array($header, $headersWithJson, true)) { + $processedHeaders['withJson'][] = $headerData; + } else { + $processedHeaders['withoutJson'][] = $headerData; + } + } + + $acceptHeaders = []; + $currentWeight = 1000; + + $hasMoreThan28Headers = count($accept) > 28; + + foreach($processedHeaders as $headers) { + if (count($headers) > 0) { + $acceptHeaders[] = $this->adjustWeight($headers, $currentWeight, $hasMoreThan28Headers); + } + } + + $acceptHeaders = array_merge(...$acceptHeaders); + + return implode(',', $acceptHeaders); + } + + /** + * Given an Accept header, returns an associative array splitting the header and its weight + * + * @param string $header "Accept" Header + * + * @return array with the header and its weight + */ + private function getHeaderAndWeight(string $header): array + { + # matches headers with weight, splitting the header and the weight in $outputArray + if (preg_match('/(.*);\s*q=(1(?:\.0+)?|0\.\d+)$/', $header, $outputArray) === 1) { + $headerData = [ + 'header' => $outputArray[1], + 'weight' => (int)($outputArray[2] * 1000), + ]; + } else { + $headerData = [ + 'header' => trim($header), + 'weight' => 1000, + ]; + } + + return $headerData; + } + + /** + * @param array[] $headers + * @param float $currentWeight + * @param bool $hasMoreThan28Headers + * @return string[] array of adjusted "Accept" headers + */ + private function adjustWeight(array $headers, float &$currentWeight, bool $hasMoreThan28Headers): array + { + usort($headers, function (array $a, array $b) { + return $b['weight'] - $a['weight']; + }); + + $acceptHeaders = []; + foreach ($headers as $index => $header) { + if($index > 0 && $headers[$index - 1]['weight'] > $header['weight']) + { + $currentWeight = $this->getNextWeight($currentWeight, $hasMoreThan28Headers); + } + + $weight = $currentWeight; + + $acceptHeaders[] = $this->buildAcceptHeader($header['header'], $weight); + } + + $currentWeight = $this->getNextWeight($currentWeight, $hasMoreThan28Headers); + + return $acceptHeaders; + } + + /** + * @param string $header + * @param int $weight + * @return string + */ + private function buildAcceptHeader(string $header, int $weight): string + { + if($weight === 1000) { + return $header; + } + + return trim($header, '; ') . ';q=' . rtrim(sprintf('%0.3f', $weight / 1000), '0'); + } + + /** + * Calculate the next weight, based on the current one. + * + * If there are less than 28 "Accept" headers, the weights will be decreased by 1 on its highest significant digit, using the + * following formula: + * + * next weight = current weight - 10 ^ (floor(log(current weight - 1))) + * + * ( current weight minus ( 10 raised to the power of ( floor of (log to the base 10 of ( current weight minus 1 ) ) ) ) ) + * + * Starting from 1000, this generates the following series: + * + * 1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + * + * The resulting quality codes are closer to the average "normal" usage of them (like "q=0.9", "q=0.8" and so on), but it only works + * if there is a maximum of 28 "Accept" headers. If we have more than that (which is extremely unlikely), then we fall back to a 1-by-1 + * decrement rule, which will result in quality codes like "q=0.999", "q=0.998" etc. + * + * @param int $currentWeight varying from 1 to 1000 (will be divided by 1000 to build the quality value) + * @param bool $hasMoreThan28Headers + * @return int + */ + public function getNextWeight(int $currentWeight, bool $hasMoreThan28Headers): int + { + if ($currentWeight <= 1) { + return 1; + } + + if ($hasMoreThan28Headers) { + return $currentWeight - 1; + } + + return $currentWeight - 10 ** floor( log10($currentWeight - 1) ); + } +} diff --git a/lib/Model/ChatParam.php b/lib/Model/ChatParam.php new file mode 100644 index 0000000..1ffeefb --- /dev/null +++ b/lib/Model/ChatParam.php @@ -0,0 +1,413 @@ + + */ +class ChatParam implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'ChatParam'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'message' => 'string' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'message' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'message' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'message' => 'message' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'message' => 'setMessage' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'message' => 'getMessage' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[] $data Associated array of property values + * initializing the model + */ + public function __construct(array $data = null) + { + $this->setIfExists('message', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + if ($this->container['message'] === null) { + $invalidProperties[] = "'message' can't be null"; + } + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets message + * + * @return string + */ + public function getMessage() + { + return $this->container['message']; + } + + /** + * Sets message + * + * @param string $message message + * + * @return self + */ + public function setMessage($message) + { + if (is_null($message)) { + throw new \InvalidArgumentException('non-nullable message cannot be null'); + } + $this->container['message'] = $message; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/lib/Model/ChatResult.php b/lib/Model/ChatResult.php new file mode 100644 index 0000000..33693ea --- /dev/null +++ b/lib/Model/ChatResult.php @@ -0,0 +1,410 @@ + + */ +class ChatResult implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'ChatResult'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'content' => 'string' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'content' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'content' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'content' => 'content' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'content' => 'setContent' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'content' => 'getContent' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[] $data Associated array of property values + * initializing the model + */ + public function __construct(array $data = null) + { + $this->setIfExists('content', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets content + * + * @return string|null + */ + public function getContent() + { + return $this->container['content']; + } + + /** + * Sets content + * + * @param string|null $content AI回答内容 + * + * @return self + */ + public function setContent($content) + { + if (is_null($content)) { + throw new \InvalidArgumentException('non-nullable content cannot be null'); + } + $this->container['content'] = $content; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/lib/Model/ModelInterface.php b/lib/Model/ModelInterface.php new file mode 100644 index 0000000..190760a --- /dev/null +++ b/lib/Model/ModelInterface.php @@ -0,0 +1,111 @@ + + */ +class ResponseOpenAPIChatResult implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'ResponseOpenAPIChatResult'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'time' => 'string', + 'openid' => 'string', + 'payload' => '\OpenAPI\Client\Model\ChatResult', + 'sign' => 'string', + 'charset' => 'string', + 'description' => 'string', + 'response_id' => 'string', + 'err_code' => 'int', + 'err_msg' => 'string', + 'sub_err' => 'int', + 'sub_msg' => 'string', + 'sign_type' => 'string' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'time' => null, + 'openid' => null, + 'payload' => null, + 'sign' => null, + 'charset' => null, + 'description' => null, + 'response_id' => null, + 'err_code' => 'int32', + 'err_msg' => null, + 'sub_err' => 'int32', + 'sub_msg' => null, + 'sign_type' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'time' => false, + 'openid' => false, + 'payload' => false, + 'sign' => false, + 'charset' => false, + 'description' => false, + 'response_id' => false, + 'err_code' => false, + 'err_msg' => false, + 'sub_err' => false, + 'sub_msg' => false, + 'sign_type' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'time' => 'time', + 'openid' => 'openid', + 'payload' => 'payload', + 'sign' => 'sign', + 'charset' => 'charset', + 'description' => 'description', + 'response_id' => 'response_id', + 'err_code' => 'err_code', + 'err_msg' => 'err_msg', + 'sub_err' => 'sub_err', + 'sub_msg' => 'sub_msg', + 'sign_type' => 'sign_type' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'time' => 'setTime', + 'openid' => 'setOpenid', + 'payload' => 'setPayload', + 'sign' => 'setSign', + 'charset' => 'setCharset', + 'description' => 'setDescription', + 'response_id' => 'setResponseId', + 'err_code' => 'setErrCode', + 'err_msg' => 'setErrMsg', + 'sub_err' => 'setSubErr', + 'sub_msg' => 'setSubMsg', + 'sign_type' => 'setSignType' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'time' => 'getTime', + 'openid' => 'getOpenid', + 'payload' => 'getPayload', + 'sign' => 'getSign', + 'charset' => 'getCharset', + 'description' => 'getDescription', + 'response_id' => 'getResponseId', + 'err_code' => 'getErrCode', + 'err_msg' => 'getErrMsg', + 'sub_err' => 'getSubErr', + 'sub_msg' => 'getSubMsg', + 'sign_type' => 'getSignType' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[] $data Associated array of property values + * initializing the model + */ + public function __construct(array $data = null) + { + $this->setIfExists('time', $data ?? [], null); + $this->setIfExists('openid', $data ?? [], null); + $this->setIfExists('payload', $data ?? [], null); + $this->setIfExists('sign', $data ?? [], null); + $this->setIfExists('charset', $data ?? [], null); + $this->setIfExists('description', $data ?? [], null); + $this->setIfExists('response_id', $data ?? [], null); + $this->setIfExists('err_code', $data ?? [], null); + $this->setIfExists('err_msg', $data ?? [], null); + $this->setIfExists('sub_err', $data ?? [], null); + $this->setIfExists('sub_msg', $data ?? [], null); + $this->setIfExists('sign_type', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets time + * + * @return string|null + */ + public function getTime() + { + return $this->container['time']; + } + + /** + * Sets time + * + * @param string|null $time 该消息发生的时间 + * + * @return self + */ + public function setTime($time) + { + if (is_null($time)) { + throw new \InvalidArgumentException('non-nullable time cannot be null'); + } + $this->container['time'] = $time; + + return $this; + } + + /** + * Gets openid + * + * @return string|null + */ + public function getOpenid() + { + return $this->container['openid']; + } + + /** + * Sets openid + * + * @param string|null $openid openid + * + * @return self + */ + public function setOpenid($openid) + { + if (is_null($openid)) { + throw new \InvalidArgumentException('non-nullable openid cannot be null'); + } + $this->container['openid'] = $openid; + + return $this; + } + + /** + * Gets payload + * + * @return \OpenAPI\Client\Model\ChatResult|null + */ + public function getPayload() + { + return $this->container['payload']; + } + + /** + * Sets payload + * + * @param \OpenAPI\Client\Model\ChatResult|null $payload payload + * + * @return self + */ + public function setPayload($payload) + { + if (is_null($payload)) { + throw new \InvalidArgumentException('non-nullable payload cannot be null'); + } + $this->container['payload'] = $payload; + + return $this; + } + + /** + * Gets sign + * + * @return string|null + */ + public function getSign() + { + return $this->container['sign']; + } + + /** + * Sets sign + * + * @param string|null $sign sign + * + * @return self + */ + public function setSign($sign) + { + if (is_null($sign)) { + throw new \InvalidArgumentException('non-nullable sign cannot be null'); + } + $this->container['sign'] = $sign; + + return $this; + } + + /** + * Gets charset + * + * @return string|null + */ + public function getCharset() + { + return $this->container['charset']; + } + + /** + * Sets charset + * + * @param string|null $charset charset + * + * @return self + */ + public function setCharset($charset) + { + if (is_null($charset)) { + throw new \InvalidArgumentException('non-nullable charset cannot be null'); + } + $this->container['charset'] = $charset; + + return $this; + } + + /** + * Gets description + * + * @return string|null + */ + public function getDescription() + { + return $this->container['description']; + } + + /** + * Sets description + * + * @param string|null $description description + * + * @return self + */ + public function setDescription($description) + { + if (is_null($description)) { + throw new \InvalidArgumentException('non-nullable description cannot be null'); + } + $this->container['description'] = $description; + + return $this; + } + + /** + * Gets response_id + * + * @return string|null + */ + public function getResponseId() + { + return $this->container['response_id']; + } + + /** + * Sets response_id + * + * @param string|null $response_id 消息的唯一ID + * + * @return self + */ + public function setResponseId($response_id) + { + if (is_null($response_id)) { + throw new \InvalidArgumentException('non-nullable response_id cannot be null'); + } + $this->container['response_id'] = $response_id; + + return $this; + } + + /** + * Gets err_code + * + * @return int|null + */ + public function getErrCode() + { + return $this->container['err_code']; + } + + /** + * Sets err_code + * + * @param int|null $err_code 该消息的错误码 + * + * @return self + */ + public function setErrCode($err_code) + { + if (is_null($err_code)) { + throw new \InvalidArgumentException('non-nullable err_code cannot be null'); + } + $this->container['err_code'] = $err_code; + + return $this; + } + + /** + * Gets err_msg + * + * @return string|null + */ + public function getErrMsg() + { + return $this->container['err_msg']; + } + + /** + * Sets err_msg + * + * @param string|null $err_msg 错误消息描述 + * + * @return self + */ + public function setErrMsg($err_msg) + { + if (is_null($err_msg)) { + throw new \InvalidArgumentException('non-nullable err_msg cannot be null'); + } + $this->container['err_msg'] = $err_msg; + + return $this; + } + + /** + * Gets sub_err + * + * @return int|null + */ + public function getSubErr() + { + return $this->container['sub_err']; + } + + /** + * Sets sub_err + * + * @param int|null $sub_err 子错误的具体的代号 + * + * @return self + */ + public function setSubErr($sub_err) + { + if (is_null($sub_err)) { + throw new \InvalidArgumentException('non-nullable sub_err cannot be null'); + } + $this->container['sub_err'] = $sub_err; + + return $this; + } + + /** + * Gets sub_msg + * + * @return string|null + */ + public function getSubMsg() + { + return $this->container['sub_msg']; + } + + /** + * Sets sub_msg + * + * @param string|null $sub_msg 子错误对应的提示消息 + * + * @return self + */ + public function setSubMsg($sub_msg) + { + if (is_null($sub_msg)) { + throw new \InvalidArgumentException('non-nullable sub_msg cannot be null'); + } + $this->container['sub_msg'] = $sub_msg; + + return $this; + } + + /** + * Gets sign_type + * + * @return string|null + */ + public function getSignType() + { + return $this->container['sign_type']; + } + + /** + * Sets sign_type + * + * @param string|null $sign_type sign_type + * + * @return self + */ + public function setSignType($sign_type) + { + if (is_null($sign_type)) { + throw new \InvalidArgumentException('non-nullable sign_type cannot be null'); + } + $this->container['sign_type'] = $sign_type; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/lib/Model/SpeechResult.php b/lib/Model/SpeechResult.php new file mode 100644 index 0000000..82e0de3 --- /dev/null +++ b/lib/Model/SpeechResult.php @@ -0,0 +1,512 @@ + + */ +class SpeechResult implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'SpeechResult'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'text' => 'string', + 'begin_time' => 'int', + 'end_time' => 'int', + 'sentence_end' => 'bool' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'text' => null, + 'begin_time' => 'int64', + 'end_time' => 'int64', + 'sentence_end' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'text' => false, + 'begin_time' => false, + 'end_time' => false, + 'sentence_end' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'text' => 'text', + 'begin_time' => 'beginTime', + 'end_time' => 'endTime', + 'sentence_end' => 'sentenceEnd' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'text' => 'setText', + 'begin_time' => 'setBeginTime', + 'end_time' => 'setEndTime', + 'sentence_end' => 'setSentenceEnd' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'text' => 'getText', + 'begin_time' => 'getBeginTime', + 'end_time' => 'getEndTime', + 'sentence_end' => 'getSentenceEnd' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[] $data Associated array of property values + * initializing the model + */ + public function __construct(array $data = null) + { + $this->setIfExists('text', $data ?? [], null); + $this->setIfExists('begin_time', $data ?? [], null); + $this->setIfExists('end_time', $data ?? [], null); + $this->setIfExists('sentence_end', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets text + * + * @return string|null + */ + public function getText() + { + return $this->container['text']; + } + + /** + * Sets text + * + * @param string|null $text 语音识别的内容 + * + * @return self + */ + public function setText($text) + { + if (is_null($text)) { + throw new \InvalidArgumentException('non-nullable text cannot be null'); + } + $this->container['text'] = $text; + + return $this; + } + + /** + * Gets begin_time + * + * @return int|null + */ + public function getBeginTime() + { + return $this->container['begin_time']; + } + + /** + * Sets begin_time + * + * @param int|null $begin_time 开始时间 + * + * @return self + */ + public function setBeginTime($begin_time) + { + if (is_null($begin_time)) { + throw new \InvalidArgumentException('non-nullable begin_time cannot be null'); + } + $this->container['begin_time'] = $begin_time; + + return $this; + } + + /** + * Gets end_time + * + * @return int|null + */ + public function getEndTime() + { + return $this->container['end_time']; + } + + /** + * Sets end_time + * + * @param int|null $end_time 结束时间 + * + * @return self + */ + public function setEndTime($end_time) + { + if (is_null($end_time)) { + throw new \InvalidArgumentException('non-nullable end_time cannot be null'); + } + $this->container['end_time'] = $end_time; + + return $this; + } + + /** + * Gets sentence_end + * + * @return bool|null + */ + public function getSentenceEnd() + { + return $this->container['sentence_end']; + } + + /** + * Sets sentence_end + * + * @param bool|null $sentence_end 句子是否结尾 + * + * @return self + */ + public function setSentenceEnd($sentence_end) + { + if (is_null($sentence_end)) { + throw new \InvalidArgumentException('non-nullable sentence_end cannot be null'); + } + $this->container['sentence_end'] = $sentence_end; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/lib/Model/StreamModule.php b/lib/Model/StreamModule.php new file mode 100644 index 0000000..bf38d0f --- /dev/null +++ b/lib/Model/StreamModule.php @@ -0,0 +1,444 @@ + + */ +class StreamModule implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'StreamModule'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'speech_to_text' => 'string', + 'synthesis' => 'string' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'speech_to_text' => null, + 'synthesis' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'speech_to_text' => false, + 'synthesis' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'speech_to_text' => 'speechToText', + 'synthesis' => 'synthesis' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'speech_to_text' => 'setSpeechToText', + 'synthesis' => 'setSynthesis' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'speech_to_text' => 'getSpeechToText', + 'synthesis' => 'getSynthesis' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[] $data Associated array of property values + * initializing the model + */ + public function __construct(array $data = null) + { + $this->setIfExists('speech_to_text', $data ?? [], null); + $this->setIfExists('synthesis', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets speech_to_text + * + * @return string|null + */ + public function getSpeechToText() + { + return $this->container['speech_to_text']; + } + + /** + * Sets speech_to_text + * + * @param string|null $speech_to_text 语音转文字 + * + * @return self + */ + public function setSpeechToText($speech_to_text) + { + if (is_null($speech_to_text)) { + throw new \InvalidArgumentException('non-nullable speech_to_text cannot be null'); + } + $this->container['speech_to_text'] = $speech_to_text; + + return $this; + } + + /** + * Gets synthesis + * + * @return string|null + */ + public function getSynthesis() + { + return $this->container['synthesis']; + } + + /** + * Sets synthesis + * + * @param string|null $synthesis 语音合成 + * + * @return self + */ + public function setSynthesis($synthesis) + { + if (is_null($synthesis)) { + throw new \InvalidArgumentException('non-nullable synthesis cannot be null'); + } + $this->container['synthesis'] = $synthesis; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/lib/Model/SynthesizerParam.php b/lib/Model/SynthesizerParam.php new file mode 100644 index 0000000..50ea94e --- /dev/null +++ b/lib/Model/SynthesizerParam.php @@ -0,0 +1,487 @@ + + */ +class SynthesizerParam implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'SynthesizerParam'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'text' => 'string', + 'model' => 'string', + 'format' => 'string' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'text' => null, + 'model' => null, + 'format' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'text' => false, + 'model' => false, + 'format' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'text' => 'text', + 'model' => 'model', + 'format' => 'format' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'text' => 'setText', + 'model' => 'setModel', + 'format' => 'setFormat' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'text' => 'getText', + 'model' => 'getModel', + 'format' => 'getFormat' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[] $data Associated array of property values + * initializing the model + */ + public function __construct(array $data = null) + { + $this->setIfExists('text', $data ?? [], null); + $this->setIfExists('model', $data ?? [], null); + $this->setIfExists('format', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + if ($this->container['text'] === null) { + $invalidProperties[] = "'text' can't be null"; + } + if ($this->container['model'] === null) { + $invalidProperties[] = "'model' can't be null"; + } + if ($this->container['format'] === null) { + $invalidProperties[] = "'format' can't be null"; + } + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets text + * + * @return string + */ + public function getText() + { + return $this->container['text']; + } + + /** + * Sets text + * + * @param string $text 需要转换为语音的文本 + * + * @return self + */ + public function setText($text) + { + if (is_null($text)) { + throw new \InvalidArgumentException('non-nullable text cannot be null'); + } + $this->container['text'] = $text; + + return $this; + } + + /** + * Gets model + * + * @return string + */ + public function getModel() + { + return $this->container['model']; + } + + /** + * Sets model + * + * @param string $model 使用的语音合成模型 + * + * @return self + */ + public function setModel($model) + { + if (is_null($model)) { + throw new \InvalidArgumentException('non-nullable model cannot be null'); + } + $this->container['model'] = $model; + + return $this; + } + + /** + * Gets format + * + * @return string + */ + public function getFormat() + { + return $this->container['format']; + } + + /** + * Sets format + * + * @param string $format 生成的音频格式 + * + * @return self + */ + public function setFormat($format) + { + if (is_null($format)) { + throw new \InvalidArgumentException('non-nullable format cannot be null'); + } + $this->container['format'] = $format; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/lib/NotifyHandler.php b/lib/NotifyHandler.php new file mode 100644 index 0000000..d48795a --- /dev/null +++ b/lib/NotifyHandler.php @@ -0,0 +1,10 @@ +format('Y-m-d') : $data->format(self::$dateTimeFormat); + } + + if (is_array($data)) { + foreach ($data as $property => $value) { + $data[$property] = self::sanitizeForSerialization($value); + } + return $data; + } + + if (is_object($data)) { + $values = []; + if ($data instanceof ModelInterface) { + $formats = $data::openAPIFormats(); + foreach ($data::openAPITypes() as $property => $openAPIType) { + $getter = $data::getters()[$property]; + $value = $data->$getter(); + if ($value !== null && !in_array($openAPIType, ['\DateTime', '\SplFileObject', 'array', 'bool', 'boolean', 'byte', 'float', 'int', 'integer', 'mixed', 'number', 'object', 'string', 'void'], true)) { + $callable = [$openAPIType, 'getAllowableEnumValues']; + if (is_callable($callable)) { + /** array $callable */ + $allowedEnumTypes = $callable(); + if (!in_array($value, $allowedEnumTypes, true)) { + $imploded = implode("', '", $allowedEnumTypes); + throw new \InvalidArgumentException("Invalid value for enum '$openAPIType', must be one of: '$imploded'"); + } + } + } + if (($data::isNullable($property) && $data->isNullableSetToNull($property)) || $value !== null) { + $values[$data::attributeMap()[$property]] = self::sanitizeForSerialization($value, $openAPIType, $formats[$property]); + } + } + } else { + foreach($data as $property => $value) { + $values[$property] = self::sanitizeForSerialization($value); + } + } + return (object)$values; + } else { + return (string)$data; + } + } + + /** + * Sanitize filename by removing path. + * e.g. ../../sun.gif becomes sun.gif + * + * @param string $filename filename to be sanitized + * + * @return string the sanitized filename + */ + public static function sanitizeFilename($filename) + { + if (preg_match("/.*[\/\\\\](.*)$/", $filename, $match)) { + return $match[1]; + } else { + return $filename; + } + } + + /** + * Shorter timestamp microseconds to 6 digits length. + * + * @param string $timestamp Original timestamp + * + * @return string the shorten timestamp + */ + public static function sanitizeTimestamp($timestamp) + { + if (!is_string($timestamp)) return $timestamp; + + return preg_replace('/(:\d{2}.\d{6})\d*/', '$1', $timestamp); + } + + /** + * Take value and turn it into a string suitable for inclusion in + * the path, by url-encoding. + * + * @param string $value a string which will be part of the path + * + * @return string the serialized object + */ + public static function toPathValue($value) + { + return rawurlencode(self::toString($value)); + } + + /** + * Checks if a value is empty, based on its OpenAPI type. + * + * @param mixed $value + * @param string $openApiType + * + * @return bool true if $value is empty + */ + private static function isEmptyValue($value, string $openApiType): bool + { + # If empty() returns false, it is not empty regardless of its type. + if (!empty($value)) { + return false; + } + + # Null is always empty, as we cannot send a real "null" value in a query parameter. + if ($value === null) { + return true; + } + + switch ($openApiType) { + # For numeric values, false and '' are considered empty. + # This comparison is safe for floating point values, since the previous call to empty() will + # filter out values that don't match 0. + case 'int': + case 'integer': + return $value !== 0; + + case 'number': + case 'float': + return $value !== 0 && $value !== 0.0; + + # For boolean values, '' is considered empty + case 'bool': + case 'boolean': + return !in_array($value, [false, 0], true); + + # For all the other types, any value at this point can be considered empty. + default: + return true; + } + } + + /** + * Take query parameter properties and turn it into an array suitable for + * native http_build_query or GuzzleHttp\Psr7\Query::build. + * + * @param mixed $value Parameter value + * @param string $paramName Parameter name + * @param string $openApiType OpenAPIType eg. array or object + * @param string $style Parameter serialization style + * @param bool $explode Parameter explode option + * @param bool $required Whether query param is required or not + * + * @return array + */ + public static function toQueryValue( + $value, + string $paramName, + string $openApiType = 'string', + string $style = 'form', + bool $explode = true, + bool $required = true + ): array { + + # Check if we should omit this parameter from the query. This should only happen when: + # - Parameter is NOT required; AND + # - its value is set to a value that is equivalent to "empty", depending on its OpenAPI type. For + # example, 0 as "int" or "boolean" is NOT an empty value. + if (self::isEmptyValue($value, $openApiType)) { + if ($required) { + return ["{$paramName}" => '']; + } else { + return []; + } + } + + # Handle DateTime objects in query + if($openApiType === "\\DateTime" && $value instanceof \DateTime) { + return ["{$paramName}" => $value->format(self::$dateTimeFormat)]; + } + + $query = []; + $value = (in_array($openApiType, ['object', 'array'], true)) ? (array)$value : $value; + + // since \GuzzleHttp\Psr7\Query::build fails with nested arrays + // need to flatten array first + $flattenArray = function ($arr, $name, &$result = []) use (&$flattenArray, $style, $explode) { + if (!is_array($arr)) return $arr; + + foreach ($arr as $k => $v) { + $prop = ($style === 'deepObject') ? $prop = "{$name}[{$k}]" : $k; + + if (is_array($v)) { + $flattenArray($v, $prop, $result); + } else { + if ($style !== 'deepObject' && !$explode) { + // push key itself + $result[] = $prop; + } + $result[$prop] = $v; + } + } + return $result; + }; + + $value = $flattenArray($value, $paramName); + + if ($openApiType === 'object' && ($style === 'deepObject' || $explode)) { + return $value; + } + + if ('boolean' === $openApiType && is_bool($value)) { + $value = self::convertBoolToQueryStringFormat($value); + } + + // handle style in serializeCollection + $query[$paramName] = ($explode) ? $value : self::serializeCollection((array)$value, $style); + + return $query; + } + + /** + * Convert boolean value to format for query string. + * + * @param bool $value Boolean value + * + * @return int|string Boolean value in format + */ + public static function convertBoolToQueryStringFormat(bool $value) + { + if (Configuration::BOOLEAN_FORMAT_STRING == Configuration::getDefaultConfiguration()->getBooleanFormatForQueryString()) { + return $value ? 'true' : 'false'; + } + + return (int) $value; + } + + /** + * Take value and turn it into a string suitable for inclusion in + * the header. If it's a string, pass through unchanged + * If it's a datetime object, format it in ISO8601 + * + * @param string $value a string which will be part of the header + * + * @return string the header string + */ + public static function toHeaderValue($value) + { + $callable = [$value, 'toHeaderValue']; + if (is_callable($callable)) { + return $callable(); + } + + return self::toString($value); + } + + /** + * Take value and turn it into a string suitable for inclusion in + * the http body (form parameter). If it's a string, pass through unchanged + * If it's a datetime object, format it in ISO8601 + * + * @param string|\SplFileObject $value the value of the form parameter + * + * @return string the form string + */ + public static function toFormValue($value) + { + if ($value instanceof \SplFileObject) { + return $value->getRealPath(); + } else { + return self::toString($value); + } + } + + /** + * Take value and turn it into a string suitable for inclusion in + * the parameter. If it's a string, pass through unchanged + * If it's a datetime object, format it in ISO8601 + * If it's a boolean, convert it to "true" or "false". + * + * @param string|bool|\DateTime $value the value of the parameter + * + * @return string the header string + */ + public static function toString($value) + { + if ($value instanceof \DateTime) { // datetime in ISO8601 format + return $value->format(self::$dateTimeFormat); + } elseif (is_bool($value)) { + return $value ? 'true' : 'false'; + } else { + return (string) $value; + } + } + + /** + * Serialize an array to a string. + * + * @param array $collection collection to serialize to a string + * @param string $style the format use for serialization (csv, + * ssv, tsv, pipes, multi) + * @param bool $allowCollectionFormatMulti allow collection format to be a multidimensional array + * + * @return string + */ + public static function serializeCollection(array $collection, $style, $allowCollectionFormatMulti = false) + { + if ($allowCollectionFormatMulti && ('multi' === $style)) { + // http_build_query() almost does the job for us. We just + // need to fix the result of multidimensional arrays. + return preg_replace('/%5B[0-9]+%5D=/', '=', http_build_query($collection, '', '&')); + } + switch ($style) { + case 'pipeDelimited': + case 'pipes': + return implode('|', $collection); + + case 'tsv': + return implode("\t", $collection); + + case 'spaceDelimited': + case 'ssv': + return implode(' ', $collection); + + case 'simple': + case 'csv': + // Deliberate fall through. CSV is default format. + default: + return implode(',', $collection); + } + } + + /** + * Deserialize a JSON string into an object + * + * @param mixed $data object or primitive to be deserialized + * @param string $class class name is passed as a string + * @param string[] $httpHeaders HTTP headers + * @param string $discriminator discriminator if polymorphism is used + * + * @return object|array|null a single or an array of $class instances + */ + public static function deserialize($data, $class, $httpHeaders = null) + { + if (null === $data) { + return null; + } + + if (strcasecmp(substr($class, -2), '[]') === 0) { + $data = is_string($data) ? json_decode($data) : $data; + + if (!is_array($data)) { + throw new \InvalidArgumentException("Invalid array '$class'"); + } + + $subClass = substr($class, 0, -2); + $values = []; + foreach ($data as $key => $value) { + $values[] = self::deserialize($value, $subClass, null); + } + return $values; + } + + if (preg_match('/^(array<|map\[)/', $class)) { // for associative array e.g. array + $data = is_string($data) ? json_decode($data) : $data; + settype($data, 'array'); + $inner = substr($class, 4, -1); + $deserialized = []; + if (strrpos($inner, ",") !== false) { + $subClass_array = explode(',', $inner, 2); + $subClass = $subClass_array[1]; + foreach ($data as $key => $value) { + $deserialized[$key] = self::deserialize($value, $subClass, null); + } + } + return $deserialized; + } + + if ($class === 'object') { + settype($data, 'array'); + return $data; + } elseif ($class === 'mixed') { + settype($data, gettype($data)); + return $data; + } + + if ($class === '\DateTime') { + // Some APIs return an invalid, empty string as a + // date-time property. DateTime::__construct() will return + // the current time for empty input which is probably not + // what is meant. The invalid empty string is probably to + // be interpreted as a missing field/value. Let's handle + // this graceful. + if (!empty($data)) { + try { + return new \DateTime($data); + } catch (\Exception $exception) { + // Some APIs return a date-time with too high nanosecond + // precision for php's DateTime to handle. + // With provided regexp 6 digits of microseconds saved + return new \DateTime(self::sanitizeTimestamp($data)); + } + } else { + return null; + } + } + + if ($class === '\SplFileObject') { + $data = Utils::streamFor($data); + + /** @var \Psr\Http\Message\StreamInterface $data */ + + // determine file name + if ( + is_array($httpHeaders) + && array_key_exists('Content-Disposition', $httpHeaders) + && preg_match('/inline; filename=[\'"]?([^\'"\s]+)[\'"]?$/i', $httpHeaders['Content-Disposition'], $match) + ) { + $filename = Configuration::getDefaultConfiguration()->getTempFolderPath() . DIRECTORY_SEPARATOR . self::sanitizeFilename($match[1]); + } else { + $filename = tempnam(Configuration::getDefaultConfiguration()->getTempFolderPath(), ''); + } + + $file = fopen($filename, 'w'); + while ($chunk = $data->read(200)) { + fwrite($file, $chunk); + } + fclose($file); + + return new \SplFileObject($filename, 'r'); + } + + /** @psalm-suppress ParadoxicalCondition */ + if (in_array($class, ['\DateTime', '\SplFileObject', 'array', 'bool', 'boolean', 'byte', 'float', 'int', 'integer', 'mixed', 'number', 'object', 'string', 'void'], true)) { + settype($data, $class); + return $data; + } + + + if (method_exists($class, 'getAllowableEnumValues')) { + if (!in_array($data, $class::getAllowableEnumValues(), true)) { + $imploded = implode("', '", $class::getAllowableEnumValues()); + throw new \InvalidArgumentException("Invalid value for enum '$class', must be one of: '$imploded'"); + } + return $data; + } else { + $data = is_string($data) ? json_decode($data) : $data; + + if (is_array($data)) { + $data = (object)$data; + } + + // If a discriminator is defined and points to a valid subclass, use it. + $discriminator = $class::DISCRIMINATOR; + if (!empty($discriminator) && isset($data->{$discriminator}) && is_string($data->{$discriminator})) { + $subclass = '\OpenAPI\Client\Model\\' . $data->{$discriminator}; + if (is_subclass_of($subclass, $class)) { + $class = $subclass; + } + } + + /** @var ModelInterface $instance */ + $instance = new $class(); + foreach ($instance::openAPITypes() as $property => $type) { + $propertySetter = $instance::setters()[$property]; + + if (!isset($propertySetter)) { + continue; + } + + if (!isset($data->{$instance::attributeMap()[$property]})) { + if ($instance::isNullable($property)) { + $instance->$propertySetter(null); + } + + continue; + } + + if (isset($data->{$instance::attributeMap()[$property]})) { + $propertyValue = $data->{$instance::attributeMap()[$property]}; + $instance->$propertySetter(self::deserialize($propertyValue, $type, null)); + } + } + return $instance; + } + } + + /** + * Native `http_build_query` wrapper. + * @see https://www.php.net/manual/en/function.http-build-query + * + * @param array|object $data May be an array or object containing properties. + * @param string $numeric_prefix If numeric indices are used in the base array and this parameter is provided, it will be prepended to the numeric index for elements in the base array only. + * @param string|null $arg_separator arg_separator.output is used to separate arguments but may be overridden by specifying this parameter. + * @param int $encoding_type Encoding type. By default, PHP_QUERY_RFC1738. + * + * @return string + */ + public static function buildQuery( + $data, + string $numeric_prefix = '', + ?string $arg_separator = null, + int $encoding_type = \PHP_QUERY_RFC3986 + ): string { + return \GuzzleHttp\Psr7\Query::build($data, $encoding_type); + } +} diff --git a/lib/StreamApiClient.php b/lib/StreamApiClient.php new file mode 100644 index 0000000..84ce787 --- /dev/null +++ b/lib/StreamApiClient.php @@ -0,0 +1,103 @@ +config = $config; + $this->apiClient = new ApiClient($config); + } + + /** + * 语音转文字 + * @param string $binaryData 16k采样率的音频二进制数据 + * @param StreamResponseHandler $handler 流响应处理器 + * @return void + * @throws ApiException + */ + public function speechToText(string $binaryData, StreamResponseHandler $handler): void + { + $path = "/ws/speechToText"; + $host = $this->config->getHost(); + $host = str_replace(["http", "https"], ["ws", "wss"], $host) . $path; + $signParams = [ + 'payload' => null, + 'path' => $path, + 'charset' => $this->config->getCharset(), + 'time' => date($this->config->getDataFormat(), time()), + 'sign_type' => $this->config->getSignType(), + 'access_token' => null, + 'app_id' => $this->config->getAppid(), + 'sign' => '' + ]; + $signParams['sign'] = $this->apiClient->requestSign($signParams); + Loop::run(function () use ($host, $signParams, $binaryData, $handler) { + $handshake = (new Handshake($host))->withHeaders($signParams); + $connection = yield connect($handshake); + yield $connection->sendBinary($binaryData); + while ($message = yield $connection->receive()) { + $payload = yield $message->buffer(); + $content = json_decode($payload); + $result = ObjectSerializer::deserialize($content, '\OpenAPI\Client\Model\SpeechResult', []); + if ($result instanceof SpeechResult) { + $handler->speechToTextHandle($result); + } + } + }); + } + /** + * 语音合成 + * @param SynthesizerParam $synthesizerParam 语音合成参数 + * @param StreamResponseHandler $handler 流响应处理器 + * @return void + * @throws ApiException + */ + public function synthesizer(SynthesizerParam $synthesizerParam, StreamResponseHandler $handler): void + { + $path = "/ws/synthesis"; + $host = $this->config->getHost(); + $host = str_replace(["http", "https"], ["ws", "wss"], $host) . $path; + $signParams = [ + 'payload' => null, + 'path' => $path, + 'charset' => $this->config->getCharset(), + 'time' => date($this->config->getDataFormat(), time()), + 'sign_type' => $this->config->getSignType(), + 'access_token' => null, + 'app_id' => $this->config->getAppid(), + 'sign' => '' + ]; + $signParams['sign'] = $this->apiClient->requestSign($signParams); + Loop::run(function () use ($host, $signParams, $synthesizerParam, $handler) { + $handshake = (new Handshake($host))->withHeaders($signParams); + $connection = yield connect($handshake); + yield $connection->send(json_encode($synthesizerParam)); + while ($message = yield $connection->receive()) { + $payload = yield $message->buffer(); + $handler->synthesizerHandle($payload); + } + }); + } + +} diff --git a/lib/StreamResponseHandler.php b/lib/StreamResponseHandler.php new file mode 100644 index 0000000..7b9ff74 --- /dev/null +++ b/lib/StreamResponseHandler.php @@ -0,0 +1,29 @@ + + + + + ./lib/Api + ./lib/Model + + + + + ./test/Api + ./test/Model + + + + + + diff --git a/test/Api/AiApiTest.php b/test/Api/AiApiTest.php new file mode 100644 index 0000000..5b3f454 --- /dev/null +++ b/test/Api/AiApiTest.php @@ -0,0 +1,85 @@ +markTestIncomplete('Not implemented'); + } +} diff --git a/test/Model/ChatParamTest.php b/test/Model/ChatParamTest.php new file mode 100644 index 0000000..d6d3e23 --- /dev/null +++ b/test/Model/ChatParamTest.php @@ -0,0 +1,90 @@ +markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "message" + */ + public function testPropertyMessage() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } +} diff --git a/test/Model/ChatResultTest.php b/test/Model/ChatResultTest.php new file mode 100644 index 0000000..2b8ed4e --- /dev/null +++ b/test/Model/ChatResultTest.php @@ -0,0 +1,90 @@ +markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "content" + */ + public function testPropertyContent() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } +} diff --git a/test/Model/ResponseOpenAPIChatResultTest.php b/test/Model/ResponseOpenAPIChatResultTest.php new file mode 100644 index 0000000..dc53275 --- /dev/null +++ b/test/Model/ResponseOpenAPIChatResultTest.php @@ -0,0 +1,189 @@ +markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "time" + */ + public function testPropertyTime() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "openid" + */ + public function testPropertyOpenid() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "payload" + */ + public function testPropertyPayload() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "sign" + */ + public function testPropertySign() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "charset" + */ + public function testPropertyCharset() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "description" + */ + public function testPropertyDescription() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "response_id" + */ + public function testPropertyResponseId() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "err_code" + */ + public function testPropertyErrCode() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "err_msg" + */ + public function testPropertyErrMsg() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "sub_err" + */ + public function testPropertySubErr() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "sub_msg" + */ + public function testPropertySubMsg() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "sign_type" + */ + public function testPropertySignType() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } +} diff --git a/test/Model/SpeechResultTest.php b/test/Model/SpeechResultTest.php new file mode 100644 index 0000000..b4b1a23 --- /dev/null +++ b/test/Model/SpeechResultTest.php @@ -0,0 +1,117 @@ +markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "text" + */ + public function testPropertyText() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "begin_time" + */ + public function testPropertyBeginTime() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "end_time" + */ + public function testPropertyEndTime() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "sentence_end" + */ + public function testPropertySentenceEnd() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } +} diff --git a/test/Model/StreamModuleTest.php b/test/Model/StreamModuleTest.php new file mode 100644 index 0000000..55ecc91 --- /dev/null +++ b/test/Model/StreamModuleTest.php @@ -0,0 +1,99 @@ +markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "speech_to_text" + */ + public function testPropertySpeechToText() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "synthesis" + */ + public function testPropertySynthesis() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } +} diff --git a/test/Model/SynthesizerParamTest.php b/test/Model/SynthesizerParamTest.php new file mode 100644 index 0000000..efd7e2c --- /dev/null +++ b/test/Model/SynthesizerParamTest.php @@ -0,0 +1,108 @@ +markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "text" + */ + public function testPropertyText() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "model" + */ + public function testPropertyModel() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } + + /** + * Test attribute "format" + */ + public function testPropertyFormat() + { + // TODO: implement + $this->markTestIncomplete('Not implemented'); + } +}