PHP项目请求参数如何统一校验

wen PHP项目 30

本文目录导读:

PHP项目请求参数如何统一校验

  1. 基础方案:使用验证器类
  2. 进阶方案:使用中间件 + 请求类
  3. 高级方案:使用Respect/Validation
  4. 最终方案:使用Symfony Validator组件
  5. 完整架构示例
  6. 推荐方案

在PHP项目中统一进行请求参数校验,通常有几种方案,我推荐几种从简单到完善的实现方式:

基础方案:使用验证器类

创建一个统一的验证器基类:

<?php
class Validator
{
    protected array $errors = [];
    public function validate(array $data, array $rules): bool
    {
        $this->errors = [];
        foreach ($rules as $field => $ruleSet) {
            $value = $data[$field] ?? null;
            $this->validateField($field, $value, $ruleSet);
        }
        return empty($this->errors);
    }
    protected function validateField(string $field, $value, array $rules): void
    {
        foreach ($rules as $rule) {
            $params = [];
            if (is_string($rule) && str_contains($rule, ':')) {
                [$rule, $paramStr] = explode(':', $rule, 2);
                $params = explode(',', $paramStr);
            }
            $method = 'rule' . ucfirst($rule);
            if (method_exists($this, $method)) {
                if (!$this->$method($field, $value, $params)) {
                    $this->errors[$field][] = $this->getErrorMessage($field, $rule, $params);
                    break;
                }
            }
        }
    }
    protected function ruleRequired(string $field, $value, array $params): bool
    {
        return $value !== null && $value !== '';
    }
    protected function ruleEmail(string $field, $value, array $params): bool
    {
        return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
    }
    protected function ruleMin(string $field, $value, array $params): bool
    {
        $minLength = (int) $params[0];
        return strlen($value) >= $minLength;
    }
    protected function ruleMax(string $field, $value, array $params): bool
    {
        $maxLength = (int) $params[0];
        return strlen($value) <= $maxLength;
    }
    protected function ruleNumeric(string $field, $value, array $params): bool
    {
        return is_numeric($value);
    }
    protected function ruleInteger(string $field, $value, array $params): bool
    {
        return filter_var($value, FILTER_VALIDATE_INT) !== false;
    }
    protected function ruleIn(string $field, $value, array $params): bool
    {
        return in_array($value, $params);
    }
    protected function getErrorMessage(string $field, string $rule, array $params): string
    {
        $messages = [
            'required' => "{$field} 字段是必填的",
            'email' => "{$field} 格式不正确",
            'min' => "{$field} 最少需要 {$params[0]} 个字符",
            'max' => "{$field} 最多允许 {$params[0]} 个字符",
            'numeric' => "{$field} 必须是数字",
            'integer' => "{$field} 必须是整数",
            'in' => "{$field} 必须是以下值: " . implode(', ', $params),
        ];
        return $messages[$rule] ?? "{$field} 验证失败";
    }
    public function getErrors(): array
    {
        return $this->errors;
    }
}
// 具体的业务验证器
class UserValidator extends Validator
{
    public function validateCreate(array $data): bool
    {
        $rules = [
            'username' => ['required', 'min:3', 'max:20'],
            'email' => ['required', 'email'],
            'age' => ['required', 'integer', 'min:1', 'max:150'],
            'role' => ['required', 'in:admin,user,guest']
        ];
        return $this->validate($data, $rules);
    }
}

进阶方案:使用中间件 + 请求类

创建请求验证中间件:

<?php
// middleware/ValidationMiddleware.php
class ValidationMiddleware
{
    public function handle($request, $next)
    {
        $routeName = $request->getRouteName();
        $validatorClass = $this->getValidatorForRoute($routeName);
        if ($validatorClass) {
            $validator = new $validatorClass();
            if (!$validator->validate($request->all())) {
                // 统一返回格式
                return response()->json([
                    'code' => 422,
                    'message' => '参数验证失败',
                    'errors' => $validator->getErrors()
                ], 422);
            }
        }
        return $next($request);
    }
    private function getValidatorForRoute($routeName): ?string
    {
        $validators = [
            'user.create' => UserCreateRequest::class,
            'user.update' => UserUpdateRequest::class,
            'order.create' => OrderCreateRequest::class,
        ];
        return $validators[$routeName] ?? null;
    }
}

创建专用请求类:

<?php
// requests/FormRequest.php
abstract class FormRequest
{
    protected array $data;
    protected array $errors = [];
    protected bool $validated = false;
    public function __construct(array $data)
    {
        $this->data = $data;
    }
    abstract public function rules(): array;
    abstract public function messages(): array;
    public function validate(): bool
    {
        $this->errors = [];
        foreach ($this->rules() as $field => $rules) {
            $value = $this->data[$field] ?? null;
            $this->validateField($field, $value, $rules);
        }
        $this->validated = empty($this->errors);
        return $this->validated;
    }
    protected function validateField($field, $value, $rules): void
    {
        // 使用上面创建的Validator类进行验证
        $validator = new Validator();
        foreach ($rules as $rule) {
            // 验证逻辑...
        }
    }
    public function validated(): array
    {
        if (!$this->validated) {
            $this->validate();
        }
        return $this->validated ? array_intersect_key($this->data, $this->rules()) : [];
    }
    public function errors(): array
    {
        return $this->errors;
    }
    public function fails(): bool
    {
        return !$this->validate();
    }
}

高级方案:使用Respect/Validation

使用成熟的验证库:Respect/Validation

<?php
use Respect\Validation\Validator as v;
class RequestValidator
{
    public static function validate(array $data, array $rules): array
    {
        $errors = [];
        foreach ($rules as $field => $validators) {
            $value = $data[$field] ?? null;
            foreach ($validators as $validator) {
                try {
                    $validator->setName($field)->assert($value);
                } catch (\Respect\Validation\Exceptions\ValidationException $e) {
                    $errors[$field][] = $e->getMessage();
                }
            }
        }
        return $errors;
    }
}
// 使用示例
$rules = [
    'username' => [
        v::alnum()->noWhitespace()->length(3, 20),
        v::notEmpty()
    ],
    'email' => [
        v::email()
    ],
    'age' => [
        v::intVal()->between(18, 100)
    ],
    'password' => [
        v::length(8, 32),
        v::regex('/[A-Za-z]/'),
        v::regex('/[0-9]/')
    ]
];
$errors = RequestValidator::validate($_POST, $rules);
if (!empty($errors)) {
    // 处理错误
}

最终方案:使用Symfony Validator组件

对于大型项目,推荐使用Symfony Validator:

<?php
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;
class UserRequest
{
    #[Assert\NotBlank]
    #[Assert\Length(min: 3, max: 20)]
    public string $username;
    #[Assert\NotBlank]
    #[Assert\Email]
    public string $email;
    #[Assert\NotBlank]
    #[Assert\Type('integer')]
    #[Assert\Range(min: 18, max: 150)]
    public int $age;
    #[Assert\NotBlank]
    #[Assert\Choice(['admin', 'user', 'guest'])]
    public string $role;
}
// 验证
$validator = Validation::createValidatorBuilder()
    ->enableAttributeMapping()
    ->getValidator();
$request = new UserRequest();
$request->username = $_POST['username'] ?? '';
$request->email = $_POST['email'] ?? '';
$request->age = (int)($_POST['age'] ?? 0);
$request->role = $_POST['role'] ?? '';
$violations = $validator->validate($request);
if (count($violations) > 0) {
    $errors = [];
    foreach ($violations as $violation) {
        $errors[$violation->getPropertyPath()][] = $violation->getMessage();
    }
    // 返回错误响应
    response()->json(['errors' => $errors], 422);
}

完整架构示例

<?php
// 1. 创建验证器工厂
class ValidatorFactory
{
    public static function make(string $type, array $data): FormRequest
    {
        $map = [
            'user_create' => UserCreateRequest::class,
            'user_update' => UserUpdateRequest::class,
            'order_create' => OrderCreateRequest::class,
        ];
        $class = $map[$type] ?? null;
        if (!$class) {
            throw new \InvalidArgumentException("Validator type '{$type}' not found");
        }
        return new $class($data);
    }
}
// 2. 在控制器中使用
class UserController
{
    public function store(Request $request)
    {
        // 统一的验证入口
        $validator = ValidatorFactory::make('user_create', $request->all());
        if ($validator->fails()) {
            return response()->json([
                'code' => 422,
                'message' => 'Validation failed',
                'errors' => $validator->errors()
            ], 422);
        }
        // 只获取验证通过的数据
        $validatedData = $validator->validated();
        // 业务逻辑...
        $user = User::create($validatedData);
        return response()->json([
            'code' => 200,
            'message' => '创建成功',
            'data' => $user
        ]);
    }
}
// 3. 全局注册验证中间件
// 在框架的全局中间件中注册
$app->addMiddleware(new ValidationMiddleware());

推荐方案

对于不同规模的项目,我的建议是:

  • 小型项目:使用方案1,自己实现简单的验证器
  • 中型项目:使用方案2,使用中间件+请求类
  • 大型项目:使用方案4,集成Symfony Validator组件

最重要的是:

  1. 统一入口:所有参数验证都在同一个地方处理
  2. 统一返回格式:验证失败的响应格式保持一致
  3. 验证规则标准化:使用统一的验证规则定义方式
  4. 错误信息可配置:错误提示信息应该支持多语言和自定义

抱歉,评论功能暂时关闭!