Files
2026-08-31 11:33:59 +08:00

316 lines
8.7 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace core\base;
use core\exception\handler\ValidationException;
use Illuminate\Validation\Validator;
use think\helper\Str;
use Webman\Http\Request;
abstract class BaseValidate
{
/** @var bool 是否实例化时自动验证 */
public bool $authValidate = false;
/** @var array 验证规则 */
protected array $rules = [];
/** @var array 错误消息 */
protected array $message = [];
/** @var array 验证场景(场景名 => 需要验证的字段) */
protected array $scene = [];
/** @var array 当前场景需验证的字段 */
protected array $only = [];
/** @var array 验证通过的有效数据 */
protected array $data = [];
/** @var string|null 当前场景名 */
protected string|null $currentScene;
/** @var string|null 验证失败错误信息 */
protected ?string $error = null;
/** @var bool 是否抛出验证异常(默认是) */
protected bool $failException = true;
/** @var int 业务错误码 */
protected int $code = -1;
/** @var int HTTP响应状态码 */
protected int $statusCode = 200;
/**
* 构造函数:初始化场景与自动验证
*
* @param string|null $scene 验证场景名
*
* @throws ValidationException
*/
public function __construct(?string $scene = null)
{
// 设置场景
if ($scene) {
$this->scene($scene);
}
// 自动验证(若开启)
if ($this->authValidate) {
$this->check();
}
}
/**
* 魔术方法:代理Webman请求对象的方法(如input、method)
*
* @param string $name 方法名
* @param array $arguments 参数
*
* @return mixed
* @throws ValidationException
*/
public function __call(string $name, array $arguments)
{
$request = $this->request();
if (method_exists($request, $name)) {
return $request->{$name}(...$arguments);
}
throw new ValidationException(__('方法名不能为空', ['class' => get_class($request), 'method' => $name]));
}
/**
* 合并自定义错误消息
*
* @param array $message 自定义消息
*
* @return $this
*/
public function setMessage(array $message): self
{
$this->message = array_merge($this->message, $message);
return $this;
}
/**
* 核心验证方法:执行规则校验(保持原功能,优化内部逻辑)
*
* @param array $data 待验证数据(为空则从请求取)
* @param array $rules 自定义规则(为空则用类内规则)
*
* @return bool 验证通过返回true
* @throws ValidationException
* @throws \Exception
*/
public function check(array $data = [], array $rules = []): bool
{
// 1. 初始化场景(若有)
if ($this->currentScene) {
$this->loadSceneRules($this->currentScene);
}
// 2. 获取最终验证规则(支持方法和属性两种方式,属性优先)
if (empty($rules)) {
// 优先使用属性定义(如果属性不为空)
if (!empty($this->rules)) {
$finalRules = $this->rules;
}
// 其次使用方法定义(如果方法返回非空数组)
elseif (method_exists($this, 'rules')) {
$methodRules = $this->rules();
if (!empty($methodRules)) {
$finalRules = $methodRules;
} else {
$finalRules = [];
}
} else {
$finalRules = [];
}
} else {
$finalRules = $rules;
}
// 3. 获取错误消息(优先类内message方法)
if (empty($this->message) && method_exists($this, 'message')) {
$this->message = $this->message();
}
// 4. 处理场景字段过滤(only)
if (!empty($this->only)) {
$sceneRules = [];
$method = strtolower($this->request()->method());
$method = $method === 'post' ? 'post' : 'get';
foreach ($this->only as $field) {
if (isset($finalRules[$field])) {
$sceneRules[$field] = $finalRules[$field];
// 若数据为空,从请求中获取对应字段值
if (empty($data)) {
$data[$field] = $this->request()->{$method}($field);
}
}
}
$finalRules = $sceneRules;
} else {
// 若未指定only,从请求中获取全部数据(若数据为空)
if (empty($data)) {
$data = $this->request()->all();
}
}
// 5. 执行验证
$validator = validator($data, $finalRules, $this->message);
// 6. 处理验证结果
$this->currentScene = null;
if ($validator->fails()) {
return $this->handleValidationFailure($validator);
}
// 验证通过:保存有效数据
$this->data = $validator->validated();
return true;
}
/**
* 加载场景规则
*
* @param string $scene 场景名
*/
protected function loadSceneRules(string $scene): void
{
$this->only = [];
$sceneAction = Str::studly($scene);
// 优先调用sceneXxx方法(如sceneAdd)
if (method_exists($this, $sceneAction)) {
call_user_func([$this, $sceneAction]);
} // 其次读取scene数组配置
elseif (isset($this->scene[$scene])) {
$this->only = $this->scene[$scene];
}
}
/**
* 处理验证失败:修正异常抛出逻辑
*
* @param Validator $validator 验证器实例
*
* @return bool
* @throws \Exception
*/
protected function handleValidationFailure(Validator $validator): bool
{
$errorMsg = $validator->errors()->first();
if ($this->failException) {
// 抛出正确的ValidationException(传递状态码)
throw new ValidationException($errorMsg, [
'statusCode' => $this->statusCode,
'errorCode' => $this->code,
'headers' => [],
]
);
}
// 不抛异常:保存错误信息
$this->error = $errorMsg;
return false;
}
/**
* 执行验证并返回验证通过的数据
*
* @param array $data 待验证数据(为空则从请求取)
* @param array $rules 自定义规则(为空则用类内规则)
* @param array $message 自定义错误消息(为空则用类内消息)
* @param bool $returnAllData 是否返回全部数据(包括未验证字段)
*
* @return array 验证通过的数据
* @throws \Exception
*/
public function validate(array $data = [], array $rules = [], array $message = [], bool $returnAllData = false): array
{
// 保存原始消息以便验证后恢复
$originalMessage = $this->message;
try {
if (!empty($message)) {
$this->setMessage($message);
}
// 执行验证
$this->check($data, $rules);
// 根据参数决定返回验证数据还是全部数据
if ($returnAllData && !empty($data)) {
return array_merge($data, $this->data);
}
return $this->data;
} finally {
$this->message = $originalMessage;
}
}
/**
* 获取验证通过的有效数据
*
* @return array
*/
public function getData(): array
{
return $this->data;
}
/**
* 获取验证失败错误信息
*
* @return string|null
*/
public function getError(): ?string
{
return $this->error;
}
/**
* 设置当前验证场景
*
* @param string $scene 场景名
*
* @return $this
*/
public function scene(string $scene): self
{
$this->currentScene = $scene;
return $this;
}
/**
* 设置是否抛出验证异常
*
* @param bool $flag 是否抛出
*
* @return $this
*/
public function failException(bool $flag): self
{
$this->failException = $flag;
return $this;
}
/**
* 获取Webman请求对象
*
* @return Request
*/
protected function request(): Request
{
return request();
}
/**
* 定义验证规则(子类重写)
*
* @return array
*/
protected function rules(): array
{
return [];
}
}