本文目录导读:

在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组件
最重要的是:
- 统一入口:所有参数验证都在同一个地方处理
- 统一返回格式:验证失败的响应格式保持一致
- 验证规则标准化:使用统一的验证规则定义方式
- 错误信息可配置:错误提示信息应该支持多语言和自定义