Compare commits

...

16 Commits

Author SHA1 Message Date
ch4o5
2ee20984ce chore(release): 0.2.11 2023-04-03 10:36:37 +00:00
李东云
859a1a1f2a feat(i18n): 引入了 symfony 的 Accept-Language 机制 2023-04-03 18:36:07 +08:00
李东云
78b56a6aa9 build(composer): 更新依赖 2023-04-03 18:35:34 +08:00
李东云
c991c31260 build(composer): 更新依赖
限定了 hyperf/crontab 的版本,避开有问题的版本
2023-03-30 17:50:18 +08:00
ch4o5
1e4158639f chore(release): 0.2.10 2023-03-21 02:56:28 +00:00
李东云
70ad3078b1 build(composer): 更新依赖 2023-03-21 10:55:48 +08:00
李东云
afec642454 build(composer): 只留下了腾讯源,更新依赖 2023-03-20 11:27:50 +08:00
李东云
b2debf9c13 build(composer): 只留下了腾讯源,更新依赖 2023-03-20 10:11:27 +08:00
ch4o5
d7c902d697 chore(release): 0.2.9 2023-03-15 01:54:35 +00:00
李东云
de96e288c3 feat: 增加了inArray、hasExtends方法 2023-03-14 18:29:05 +08:00
李东云
7c1efe1a1f feat: 添加了 http 请求服务 2023-03-13 16:14:22 +08:00
李东云
e33c115938 feat: 增加默认api风格参数 2023-03-13 13:32:48 +08:00
李东云
b8c3d6d068 feat(api): 增加了可以使用中间件定制是否 restful 的逻辑 2023-03-13 11:00:16 +08:00
李东云
2d2148f3ec feat: 实现了通过中间件设置是否 restful 的逻辑 2023-03-10 18:29:41 +08:00
李东云
2d2a535fd2 fix: 修复了一处大小写错误 2023-03-08 17:37:27 +08:00
李东云
a618af5ab3 feat: 增加了对 session 中间件配置 cookie 方式的补充 2023-03-08 17:30:16 +08:00
20 changed files with 1195 additions and 387 deletions

15
.idea/HDK-Core.iml generated
View File

@@ -64,9 +64,6 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/http-message" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/exception-handler" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/macroable" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/resource" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/paginator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/engine" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/pool" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/event" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/validation" />
@@ -85,7 +82,6 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/manifest" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-timer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-text-template" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-invoker" />
@@ -135,14 +131,21 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/nunomaduro/collision" />
<excludeFolder url="file://$MODULE_DIR$/vendor/pestphp/pest" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/stopwatch" />
<excludeFolder url="file://$MODULE_DIR$/vendor/spatie/test-time" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpstan/phpstan" />
<excludeFolder url="file://$MODULE_DIR$/vendor/pestphp/pest-plugin" />
<excludeFolder url="file://$MODULE_DIR$/vendor/friendsofphp/php-cs-fixer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/spatie/pest-plugin-test-time" />
<excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/deprecations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/engine" />
<excludeFolder url="file://$MODULE_DIR$/vendor/spatie/pest-plugin-test-time" />
<excludeFolder url="file://$MODULE_DIR$/vendor/spatie/test-time" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/coordinator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/engine-contract" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/resource" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/paginator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/guzzle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

4
.idea/php.xml generated
View File

@@ -158,6 +158,10 @@
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan" />
<path value="$PROJECT_DIR$/vendor/doctrine/deprecations" />
<path value="$PROJECT_DIR$/vendor/hyperf/coordinator" />
<path value="$PROJECT_DIR$/vendor/hyperf/guzzle" />
<path value="$PROJECT_DIR$/vendor/hyperf/engine-contract" />
<path value="$PROJECT_DIR$/vendor/hyperf/crontab" />
<path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.0" />

View File

@@ -1,4 +1,43 @@
# 版本更新日志
### [0.2.11](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.2.10...v0.2.11) (2023-04-03)
### 📦‍ Build System | 打包构建
* **composer:** 更新依赖 ([78b56a6](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/78b56a6aa9038743be7df9a17787b94771e226c3))
* **composer:** 更新依赖 ([c991c31](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/c991c312609e7501c7adad28ec8229efad717db0))
### ✨ Features | 新功能
* **i18n:** 引入了 symfony 的 Accept-Language 机制 ([859a1a1](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/859a1a1f2aa683f9f026652952d1a03ba5d7be02))
### [0.2.10](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.2.9...v0.2.10) (2023-03-21)
### 📦‍ Build System | 打包构建
* **composer:** 只留下了腾讯源,更新依赖 ([afec642](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/afec6424545daa3b3f2a4b91e97e359b4a840a19))
* **composer:** 只留下了腾讯源,更新依赖 ([b2debf9](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/b2debf9c135554a15639c299f183c43f3682545b))
* **composer:** 更新依赖 ([70ad307](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/70ad3078b1ddeeeb0179dd44770e959ecf072fed))
### [0.2.9](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.2.8...v0.2.9) (2023-03-15)
### 🐛 Bug Fixes | Bug 修复
* 修复了一处大小写错误 ([2d2a535](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/2d2a535fd2c1ae13195f13498864540b503edd1e))
### ✨ Features | 新功能
* 增加了inArray、hasExtends方法 ([de96e28](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/de96e288c3429c58deed15ea667a5e15a34fb81f))
* 增加了对 session 中间件配置 cookie 方式的补充 ([a618af5](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/a618af5ab353f478dd4bcce18a7b84f611fd0c7a))
* 增加默认api风格参数 ([e33c115](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/e33c1159388efa3590e81f484c01a66d21f8b574))
* 实现了通过中间件设置是否 restful 的逻辑 ([2d2148f](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/2d2148f3ecabef09801a225632430bf103dbf218))
* 添加了 http 请求服务 ([7c1efe1](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/7c1efe1a1f18f082ed7b7b6383112034982e2893))
* **api:** 增加了可以使用中间件定制是否 restful 的逻辑 ([b8c3d6d](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/b8c3d6d06810fb920df200a4d75746b49e2fbd07))
### [0.2.8](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.2.7...v0.2.8) (2023-02-03)

View File

@@ -1 +1 @@
0.2.8
0.2.11

View File

@@ -35,6 +35,7 @@
"lmc/http-constants": "^1.2",
"myclabs/php-enum": "^1.8",
"roave/dont": "^1.5",
"symfony/http-foundation": "^6.0",
"symfony/polyfill-php81": "^1.26",
"teapot/status-code": "^1.1"
},
@@ -43,6 +44,8 @@
"firebase/php-jwt": "^6.1",
"friendsofphp/php-cs-fixer": "^3.13",
"guzzlehttp/guzzle": "^7.5",
"hyperf/crontab": "<=3.0.9 || >3.0.13 <3.1",
"hyperf/guzzle": "^3.0",
"hyperf/session": "3.0.*",
"hyperf/validation": "3.0.*",
"pestphp/pest": "^1.22",
@@ -57,7 +60,7 @@
"firebase/php-jwt": "JWT 鉴权必需",
"hyperf/session": "Session 鉴权必需",
"symfony/mailer": "用于发送电子邮件",
"guzzlehttp/guzzle": "需要发起 http 请求时必需",
"hyperf/guzzle": "需要发起 http 请求时必需",
"alibabacloud/dysmsapi-20170525": "阿里云短信服务必需"
},
"config": {
@@ -98,17 +101,9 @@
"url": "https://satis.luxcreo.cn/"
},
"packagist": {
"type": "composer",
"url": "https://mirrors.aliyun.com/composer/"
},
"packagist-tx": {
"type": "composer",
"url": "https://mirrors.cloud.tencent.com/composer/"
},
"packagist-hw": {
"type": "composer",
"url": "https://repo.huaweicloud.com/repository/php/"
}
},
"version": "0.2.8"
"version": "0.2.11"
}

948
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,13 @@
parameters:
level: 6
reportUnmatchedIgnoredErrors: false
checkGenericClassInNonGenericObjectType: false
paths:
- publish
- src
- tests
- testsi
ignoreErrors:
- '#Constant BASE_PATH not found#'
- '#Property [a-zA-Z0-9\\_]+::\$[a-zA-Z0-9]+ is never written, only read.#'
- '#Property [a-zA-Z0-9\\_]+::\$[a-zA-Z0-9]+ is never written, only read\.#'
- '#Method [a-zA-Z0-9\\_]+::[a-zA-Z0-9]+\(\) is unused\.#'
- '#Method [a-zA-Z0-9\\_]+::[a-zA-Z0-9]+\(\) has parameter \$response with no value type specified in iterable type array\.#'

View File

@@ -28,6 +28,9 @@ use Singularity\HDK\Core\Service\UtilsService;
* @author 李东云<dongyun.li@luxcreo.cn>
* Powered by PhpStorm
* Created on 2022/4/29
*
* @deprecated
* @see CommonCoreMiddleware
*/
class ClassicCoreMiddleware extends CoreMiddleware
{

View File

@@ -0,0 +1,32 @@
<?php
/**
* ClassicStyleMiddleware.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/3/10
*/
namespace Singularity\HDK\Core\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Singularity\HDK\Core\Service\ApiStyleService;
class ClassicStyleMiddleware implements MiddlewareInterface
{
public function __construct(protected ApiStyleService $apiStyleService)
{
}
/**
* @inheritDoc
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$this->apiStyleService->set(ApiStyleService::CLASSIC);
return $handler->handle($request);
}
}

View File

@@ -0,0 +1,157 @@
<?php
/**
* ClassicCoreMiddleware.php@hyperf-development-kit
*
* @author 李东云<dongyun.li@luxcreo.cn>
* Powered by PhpStorm
* Created on 2022/4/29
*/
namespace Singularity\HDK\Core\Middleware;
use Hyperf\Contract\Arrayable;
use Hyperf\Contract\Jsonable;
use Hyperf\Contract\LengthAwarePaginatorInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\CoreMiddleware;
use Hyperf\Utils\Codec\Json;
use Lmc\HttpConstants\Header;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Singularity\HDK\Core\Service\ApiStyleService;
use Singularity\HDK\Core\Service\UtilsService;
/**
* Singularity\HDK\Core\Middleware\CommonCoreMiddleware@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/3/10
*/
class CommonCoreMiddleware extends CoreMiddleware
{
#[Inject]
private UtilsService $utilsService;
#[Inject]
private ApiStyleService $apiStyleService;
/**
* @inheritDoc
*/
protected function transferToResponse($response, ServerRequestInterface $request): ResponseInterface
{
$style = $this->apiStyleService->get();
if ($style === ApiStyleService::RESTFUL) {
return $this->transferToRestfulResponse($response, $request);
} else {
return $this->transferToClassicResponse($response, $request);
}
}
/**
* @param array|string|Arrayable|Jsonable|null $response
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
protected function transferToRestfulResponse(Arrayable|Jsonable|array|string|null $response, ServerRequestInterface $request): ResponseInterface
{
// 分页数据
if ($response instanceof LengthAwarePaginatorInterface) {
$fact_response = $this->response()
->withHeader('Per-Page', (string)$response->perPage())
->withHeader('Total', (string)$response->total())
->withHeader('Current-Page', (string)$response->currentPage())
->withHeader('Total-Pages', (string)$response->hasPages());
$fact_response = $this->utilsService->extendLinkToHeader($fact_response, $response->nextPageUrl(), 'next');
$fact_response = $this->utilsService->extendLinkToHeader(
$fact_response,
$response->url($response->lastPage()),
'last'
);
$fact_response = $this->utilsService->extendLinkToHeader($fact_response, $response->url(1), 'first');
return $this->utilsService->extendLinkToHeader(
$fact_response,
$response->previousPageUrl(),
'prev'
);
}
return parent::transferToResponse($response, $request);
}
/**
* @param array|string|Arrayable|Jsonable|null $response
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
protected function transferToClassicResponse(
Arrayable|Jsonable|array|string|null $response,
ServerRequestInterface $request
): ResponseInterface {
$code_name = config('common.response.code_name');
$message_name = config('common.response.message_name');
$data_name = config('common.response.data_name');
// 分页数据
if ($response instanceof LengthAwarePaginatorInterface) {
$paginator = $response;
$fact_response = $this->response()->withHeader('Per-Page', (string)$paginator->perPage())->withHeader(
'Total',
(string)$paginator->total()
)->withHeader('Current-Page', (string)$paginator->currentPage())->withHeader(
'Total-Pages',
(string)$paginator->hasPages()
);
$fact_response = $this->utilsService->extendLinkToHeader($fact_response, $paginator->nextPageUrl(), 'next');
$fact_response = $this->utilsService->extendLinkToHeader(
$fact_response,
$paginator->url($paginator->lastPage()),
'last'
);
$fact_response = $this->utilsService->extendLinkToHeader($fact_response, $paginator->url(1), 'first');
$fact_response = $this->utilsService->extendLinkToHeader(
$fact_response,
$paginator->previousPageUrl(),
'prev'
);
return $fact_response->withAddedHeader(Header::CONTENT_TYPE, 'application/json')->withBody(
new SwooleStream(
Json::encode(
[
$code_name => 200,
$message_name => 'ok',
$data_name => $response->items(),
'meta' => [
'currentPage' => $paginator->currentPage(),
'lastPage' => $paginator->lastPage(),
'perPage' => $paginator->perPage(),
'total' => $paginator->total(),
],
]
)
)
);
}
// 普通数组
if (is_array($response) || $response instanceof Arrayable) {
$response = [$code_name => 200, $message_name => 'ok', $data_name => $response];
return $this->response()->withAddedHeader(Header::CONTENT_TYPE, 'application/json')->withBody(
new SwooleStream(Json::encode($response))
);
}
// 可 Json 化的数据结构
if ($response instanceof Jsonable) {
$response = [$code_name => 200, $message_name => 'ok', $data_name => Json::decode((string)$response)];
return $this->response()->withAddedHeader(Header::CONTENT_TYPE, 'application/json')->withBody(
new SwooleStream(Json::encode($response))
);
}
// 其他默认按字符串处理
return $this->response()->withAddedHeader(Header::CONTENT_TYPE, 'text/plain')->withBody(
new SwooleStream((string)$response)
);
}
}

View File

@@ -7,10 +7,12 @@ namespace Singularity\HDK\Core\Middleware;
use Hyperf\Context\Context;
use Hyperf\Contract\TranslatorInterface;
use Hyperf\Di\Annotation\Inject;
use Lmc\HttpConstants\Header;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Singularity\HDK\Utils\Middleware\InternationalizationMiddleware@HDK
@@ -26,13 +28,16 @@ class InternationalizationMiddleware implements MiddlewareInterface
*/
#[Inject]
private TranslatorInterface $translator;
/**
* @inheritDoc
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = Context::get(ResponseInterface::class);
$language = $request->getHeaderLine('Accept-Language');
$req = (new Request());
$req->headers->set(Header::ACCEPT_LANGUAGE, $request->getHeaderLine(Header::ACCEPT_LANGUAGE));
$language = $req->getPreferredLanguage([config('translation.locale')]);
if (!empty($language)) {
$language = match (strtolower($language)) {
'en', 'en_us', 'en-us', 'en-uk', 'en_uk' => 'en',
@@ -40,9 +45,14 @@ class InternationalizationMiddleware implements MiddlewareInterface
default => config('translation.locale'),
};
$this->translator->setLocale($language);
$response = $response->withAddedHeader('Content-Language', strtr($this->translator->getLocale(), '_', '-'));
Context::set(ResponseInterface::class, $response);
}
$response = Context::get(ResponseInterface::class);
$response = $response->withAddedHeader(
Header::CONTENT_LANGUAGE,
strtr($this->translator->getLocale(), '_', '-')
);
Context::set(ResponseInterface::class, $response);
return $handler->handle($request);
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* ClassicStyleMiddleware.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/3/10
*/
namespace Singularity\HDK\Core\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Singularity\HDK\Core\Service\ApiStyleService;
class RestStyleMiddleware implements MiddlewareInterface
{
public function __construct(protected ApiStyleService $apiStyleService)
{
}
/**
* @inheritDoc
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$this->apiStyleService->set(ApiStyleService::RESTFUL);
return $handler->handle($request);
}
}

View File

@@ -24,6 +24,9 @@ use Singularity\HDK\Core\Service\UtilsService;
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/14
*
* @deprecated
* @see CommonCoreMiddleware
*/
class RestfulCoreMiddleware extends CoreMiddleware
{

View File

@@ -0,0 +1,73 @@
<?php
/**
* SessionMiddleware.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/3/8
*/
namespace Singularity\HDK\Core\Middleware;
use Carbon\Carbon;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\SessionInterface;
use Hyperf\HttpMessage\Cookie\Cookie;
use Hyperf\HttpMessage\Server\Response;
use Hyperf\Session\SessionManager;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
/**
* Singularity\HDK\Core\Middleware\SessionMiddleware@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/3/8
*/
class SessionMiddleware extends \Hyperf\Session\Middleware\SessionMiddleware
{
public function __construct(private SessionManager $sessionManager, private ConfigInterface $config)
{
parent::__construct($this->sessionManager, $this->config);
}
/**
* Add the session cookie to the response·.
*/
private function addCookieToResponse(
ServerRequestInterface $request,
ResponseInterface $response,
SessionInterface $session
): ResponseInterface {
$cookie = new Cookie(
name: $session->getName(),
value: $session->getId(),
expire: $this->getCookieExpirationDate(),
path: $this->config->get('session.options.path', '/'),
domain: $this->config->get('session.options.domain', $request->getUri()->getHost()),
secure: $this->config->get('session.options.secure', strtolower($request->getUri()->getScheme()) === 'https'),
httpOnly: true,
sameSite: $this->config->get('session.options.samesite', Cookie::SAMESITE_LAX)
);
if (!method_exists($response, 'withCookie')) {
return $response->withHeader('Set-Cookie', (string)$cookie);
}
/* @var Response $response */
return $response->withCookie($cookie);
}
/**
* Get the session lifetime in seconds.
*/
private function getCookieExpirationDate(): int
{
if ($this->config->get('session.options.expire_on_close')) {
$expirationDate = 0;
} else {
$expireSeconds = $this->config->get('session.options.cookie_lifetime', 5 * 60 * 60);
$expirationDate = Carbon::now()->addSeconds($expireSeconds)->getTimestamp();
}
return $expirationDate;
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* ApiStyleService.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/3/10
*/
namespace Singularity\HDK\Core\Service;
use Hyperf\Context\Context;
class ApiStyleService
{
public const RESTFUL = 'restful';
public const CLASSIC = 'classic';
public function __construct()
{
Context::set(self::class, config('common.response.restful') ? self::RESTFUL : self::CLASSIC);
}
/**
* @param literal-string $style
* @return $this
*/
public function set(string $style): static
{
Context::set(self::class, $style === self::RESTFUL ? self::RESTFUL : self::CLASSIC);
return $this;
}
public function get(): string
{
return Context::get(self::class) ?? (config('common.response.restful')
? ApiStyleService::RESTFUL
: ApiStyleService::CLASSIC);
}
}

View File

@@ -10,6 +10,10 @@ use Psr\Http\Message\ServerRequestInterface;
*/
class ExtendService
{
public function __construct(private UtilsService $utils)
{
}
/**
* @param ServerRequestInterface|null $request
* @param array<string, string>|null $params
@@ -29,6 +33,7 @@ class ExtendService
return [];
}
/**
* @return array<string, string>
*/
@@ -37,6 +42,12 @@ class ExtendService
return Context::get(self::class) ?? [];
}
public function hasExtends(): bool
{
$extends = Context::get(self::class);
return is_array($extends) && count($extends) > 0;
}
/**
* 判断是否传入了此扩展
*
@@ -44,8 +55,8 @@ class ExtendService
*
* @return bool
*/
public function hasExtends(string $field): bool
public function hasExtend(string $field): bool
{
return Context::has(self::class) && isset(array_flip(Context::get(self::class))[$field]);
return $this->utils->inArray($field, Context::get(self::class));
}
}

View File

@@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
namespace Singularity\HDK\Core\Service;
use Ergebnis\Http\Method;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Utils;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Guzzle\ClientFactory;
use Hyperf\Utils\Codec\Json;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* 发起 Http 请求的类
*/
class HttpRequestService
{
public const TIMEOUT = 20;
/**
* @Inject()
* @var ClientFactory
*/
private ClientFactory $client;
/**
* @var array<literal-string, mixed>
*/
private array $options = [];
/**
* @param string $url
* @param array<string, string|int> $params
* @return ResponseInterface
* @throws GuzzleException
*/
public function requestGet(string $url, array $params = []): ResponseInterface
{
$request = new Request(Method\Rfc\Rfc7231::GET, $url);
return $this->getClient()->send($request, [
'params' => $params,
]);
}
private function getClient(): Client
{
return $this->client->create($this->options);
}
/**
* @param string $url
* @param array<string, mixed> $data
* @param array<string, string|int> $params
* @return ResponseInterface
* @throws GuzzleException
*/
public function requestPost(string $url, array $data = [], array $params = []): ResponseInterface
{
$data = Json::encode($data);
$request = new Request('post', $url, ['Content-Type' => 'application/json'], Utils::streamFor($data));
return $this->getClient()->send($request, [
'params' => $params,
]);
}
/**
* @param string $url
* @param array<string, mixed> $data
* @param array<string, string|int> $params
* @return ResponseInterface
* @throws GuzzleException
*/
public function requestPut(string $url, array $data = [], array $params = []): ResponseInterface
{
$data = Json::encode($data);
$request = new Request('put', $url, ['Content-Type' => 'application/json'], Utils::streamFor($data));
$result = $this->getClient()->send($request, [
'params' => $params,
]);
$json = Json::decode($result->getBody()->getContents());
if ($json['code'] !== 200) {
throw new ServerException($json['message'], $request, $result);
}
return $result;
}
/**
* 定制 options
* @param array<string, mixed> $options
* @return $this
*/
public function setOptions(array $options): HttpRequestService
{
$this->options = array_replace_recursive($this->options, $options);
return $this;
}
/**
* @param ResponseInterface $response
* @param RequestInterface $request
* @return ResponseInterface
*/
private function parseResponse(
ResponseInterface $response,
RequestInterface $request
): ResponseInterface {
if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300) {
return $response;
} else {
throw new BadResponseException($response->getReasonPhrase(), $request, $response);
}
}
}

View File

@@ -328,4 +328,17 @@ class UtilsService
'unit' => $unitToUpper ? strtoupper($unit[$i]) : $unit[$i],
];
}
/**
* 更快的判断数组元素方法
* @param float|int|string $needle
* @param array<int, mixed> $haystack
* @return bool
*/
public function inArray(float|int|string $needle, array $haystack): bool
{
$list = array_flip($haystack);
return isset($list[$needle]);
}
}

View File

@@ -9,15 +9,18 @@
namespace Singularity\HDK\Test\Core\Unit;
use Darabonba\GatewaySpi\Models\InterceptorContext\request;
use Singularity\HDK\Core\Service\ExtendService;
use Singularity\HDK\Core\Service\UtilsService;
$service = new ExtendService();
$service = new ExtendService(new UtilsService());
/** @var ExtendService $service */
$service = make(ExtendService::class, ['utils' => new UtilsService()]);
it('asserts no parameters can be parsed.', function (ExtendService $service, $params) {
$service->parse(
request: null,
params: $params
null,
$params
);
$result = $service->getExtends();
expect($result)->toBeArray()->toHaveCount(0)->toBe([]);
@@ -32,17 +35,22 @@ it('asserts no parameters can be parsed.', function (ExtendService $service, $pa
it('asserts query parameters can be parsed.', function () use ($service) {
$result = $service->parse(
null,
params: [
[
'id' => 5,
'extends' => 'a,b',
]
);
expect($result)->toBeArray()->toHaveCount(2)->toBe(['a', 'b']);
});
it('asserts has extends', function () use ($service) {
expect($service->hasExtends('a'))->toBeTrue()
->and($service->hasExtends('b'))->toBeTrue()
->and($service->hasExtends('c'))->toBeFalse();
expect($service->hasExtends())->toBeTrue();
});
it('asserts has specify extend', function () use ($service) {
expect($service->hasExtend('a'))->toBeTrue()
->and($service->hasExtend('b'))->toBeTrue()
->and($service->hasExtend('c'))->toBeFalse();
})->depends('it asserts query parameters can be parsed.');
it('asserts parsed extends', function () use ($service) {

View File

@@ -60,3 +60,22 @@ test('断言可以根据参数构建 URL', function (string $url, array $params,
'http://username:password@127.0.0.1/git/resp?id=1#/page?a=b&c=d',
]
])->group('pure', 'utils');
test('断言可以判断是否是数组中的元素', function (
$needle,
array $haystack,
bool $expect,
bool $exceptions = false
) use ($utils) {
try {
expect($utils->inArray($needle, $haystack))->toBe($expect);
} catch (Throwable $e) {
if ($exceptions) {
expect(true)->toBeTrue();
}
}
})->with([
[1, ['1', 2, 3], true, false],
['1', ['1', 2, 3], true, false],
[[1, 2, 3], ['1', 2, 3], false, true],
])->group('pure', 'utils');