Files
hdk-core/src/Exceptions/Handler/CommonHandler.php
2023-01-28 18:25:44 +08:00

320 lines
10 KiB
PHP

<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Singularity\HDK\Core\Exceptions\Handler;
use Hyperf\Database\Exception\QueryException;
use Hyperf\Database\Model\ModelNotFoundException;
use Hyperf\Di\Annotation\Inject;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\Framework\Logger\StdoutLogger;
use Hyperf\HttpMessage\Exception\BadRequestHttpException;
use Hyperf\HttpMessage\Exception\HttpException;
use Hyperf\HttpMessage\Exception\MethodNotAllowedHttpException;
use Hyperf\HttpMessage\Exception\NotFoundHttpException;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Utils\Codec\Json;
use Hyperf\Validation\ValidationException;
use Lmc\HttpConstants\Header;
use Psr\Http\Message\ResponseInterface;
use RedisException;
use Singularity\HDK\Core\Constants\CommonErrorCode;
use Singularity\HDK\Core\Exceptions\ValidateException;
use Symfony\Component\Mailer\Exception\TransportException;
use Teapot\StatusCode\RFC\RFC7231;
use Throwable;
/**
* 通用异常处理
* Singularity\HDK\Utils\Exceptions\Handler\CommonHandler@hyperf-development-kit
*
* @author 李东云<dongyun.li@luxcreo.cn>
* Powered by PhpStorm
* Created on 2022/4/28
*/
class CommonHandler extends ExceptionHandler
{
/**
* @Inject(required=false)
*
* @var RequestInterface|null
*/
private ?RequestInterface $request;
/**
* @Inject
* @var StdoutLogger
*/
private StdoutLogger $logger;
/**
* {@inheritDoc}
*/
public function handle(
Throwable $throwable,
ResponseInterface $response
): ResponseInterface {
// 阻止异常冒泡
$this->stopPropagation();
$restful = config('common.response.restful');
$code_name = config('common.response.code_name');
$message_name = config('common.response.message_name');
$is_testing = config('app_status') === true;
$error_type = get_class($throwable);
$request_time = date('Y-m-d H:i:s');
if (!empty($this->request)) {
$this->request->url();
$is_debug = $this->request->hasHeader('Postman-Token')
|| str_starts_with($this->request->header('User-Agent'), 'apifox');
$request_data = Json::encode($this->request->getParsedBody());
$request_headers = Json::encode($this->request->getHeaders());
} else {
$is_debug = false;
$request_data = null;
$request_headers = null;
}
// 901 程序语法错误
// 902 SQL 语法错误
if ($throwable instanceof QueryException) {
if ($throwable->getCode() === '42S22') {
$code = CommonErrorCode::PROGRAM_SQL_COLUMN_NOT_FOUND;
} else {
$code = CommonErrorCode::PROGRAM_SQL_ERROR;
}
$data = [
$code_name => $code,
$message_name => CommonErrorCode::getMessage(
$is_testing
? $code
: CommonErrorCode::PROGRAM_SQL_ERROR
),
];
if ($is_testing) {
$data['details'] = [
'sql' => $throwable->getSql(),
'error' => $throwable->getMessage(),
];
}
}
// 101 请求方式错误
if ($throwable instanceof MethodNotAllowedHttpException) {
$message = explode(': ', $throwable->getMessage());
$allow_method = explode(', ', $message[1]);
$code = CommonErrorCode::REQUEST_METHOD_ERROR;
$data = [
$code_name => $code,
$message_name => CommonErrorCode::getMessage($code, [
'methods' => join(', ', $allow_method),
]),
'currentMethod' => empty($this->request) ? null : $this->request->getMethod(),
'allowedMethod' => $allow_method,
];
}
// 验证失败
if ($throwable instanceof BadRequestHttpException) {
$data = [
$code_name => CommonErrorCode::REQUEST_PARAMS_ERROR,
$message_name => $is_testing
? $throwable->getMessage()
: CommonErrorCode::getMessage(CommonErrorCode::SERVER_ERROR),
];
}
if ($throwable instanceof ValidationException) {
$data = $throwable->validator->errors()->first();
if (is_numeric($data)) {
$code = (int)$data;
$data = CommonErrorCode::getMessage($code);
}
$data = [
$code_name => $code ?? CommonErrorCode::REQUEST_PARAMS_ERROR,
$message_name => $data,
];
}
if ($throwable instanceof ValidateException) {
$code = $throwable->getCode();
$message = $throwable->getMessage();
$data = [
$code_name => $code,
$message_name => empty($message) ? CommonErrorCode::getMessage($code, [
'param' => $throwable->getFieldName(),
]) : $message,
];
if ($is_debug) {
$data['currentValue'] = $throwable->getCurrentValue();
$data['availableValue'] = $throwable->getAvailableValue();
}
}
// 路由不存在
if ($throwable instanceof NotFoundHttpException) {
$code = CommonErrorCode::ROUTE_NOT_FOUND;
$data = [
$code_name => $code,
$message_name => CommonErrorCode::getMessage($code),
];
$status_code = 404;
}
// 模型不存在
if ($throwable instanceof ModelNotFoundException) {
$code = empty($throwable->getCode()) ? CommonErrorCode::MODEL_NOT_FOUND : $throwable->getCode();
$message = empty($throwable->getCode()) ? CommonErrorCode::getMessage($code, [
'resource' => '资源',
]) : $throwable->getMessage();
$data = [
$code_name => $code,
$message_name => $message,
];
$status_code = 404;
}
// 300 服务出错
// 303 缓存异常
if ($throwable instanceof RedisException) {
$code = CommonErrorCode::SERVER_CACHE_REDIS_ERROR;
if ($throwable->getMessage() === 'Connection refused') {
$code = CommonErrorCode::SERVER_CACHE_REDIS_REFUSED_ERROR;
}
$data = [
$code_name => $code,
$message_name => CommonErrorCode::getMessage(
$is_testing
? $code
: CommonErrorCode::SERVER_CACHE_REDIS_ERROR
),
];
}
// 306 消息异常
// 30601 邮箱发件异常
if ($throwable instanceof TransportException) {
$code = CommonErrorCode::SERVER_MESSAGE_EMAIL_ERROR;
if (strpos($throwable->getMessage(), '500 Error: bad syntax')) {
$code = CommonErrorCode::SERVER_MESSAGE_EMAIL_NOT_FOUND;
}
$data = [
$code_name => $code,
$message_name => CommonErrorCode::getMessage(
$is_testing
? $code
: CommonErrorCode::SERVER_MESSAGE_EMAIL_ERROR
),
];
}
if (empty($data)) {
// 其他情况
$data = [
$code_name => $is_testing
? ($throwable->getCode() == 0
? CommonErrorCode::SERVER_ERROR
: $throwable->getCode())
: CommonErrorCode::SERVER_ERROR,
$message_name => $is_testing ? $throwable->getMessage() : __(
CommonErrorCode::getMessage(CommonErrorCode::SERVER_ERROR)
),
];
// 其他错误
if ($throwable instanceof HttpException) {
$data = [
$code_name => $throwable->getCode() ?: $throwable->getStatusCode(),
$message_name => $throwable->getMessage(),
];
}
}
$response = $response->withHeader(
Header::CONTENT_TYPE,
'application/json; charset=utf-8'
);
if ($is_debug && $is_testing) {
$data['trace'] = [
'errorType' => $error_type,
'errorTrack' => $throwable->getTrace(),
];
}
$cookies = Json::encode($this->request->getCookieParams());
$this->logger->error(
<<<ERROR_LOG
TYPE: $error_type
[$data[$code_name]] $data[$message_name]
{$throwable->getMessage()}
-------------------------------
REQUEST_TIME: $request_time
-------------------------------
REQUEST_HEADERS:
$request_headers
-------------------------------
REQUEST_COOKIES:
$cookies
-------------------------------
REQUEST_METHOD:
{$this->request->getMethod()}
-------------------------------
REQUEST_URL:
{$this->request->getUri()}
-------------------------------
REQUEST_QUERY:
{$this->request->getQueryString()}
-------------------------------
REQUEST_DATA:
$request_data
-------------------------------
TRACE:
{$throwable->getTraceAsString()}
===============================
ERROR_LOG
);
$data = Json::encode($data);
if ($restful) {
$response = $response->withStatus(
$status_code ??
$throwable->status ??
$throwable->statusCode ??
RFC7231::INTERNAL_SERVER_ERROR
);
}
return $response
->withBody(
new SwooleStream($data)
);
}
/**
* 判断该异常处理器是否要对该异常进行处理.
*/
public function isValid(Throwable $throwable): bool
{
return true;
}
}