PHP接口参数校验怎么做

wen PHP项目 1

本文目录导读:

PHP接口参数校验怎么做

  1. 基础手动校验(简单场景)
  2. 使用Validation类库(推荐)
  3. 使用Symfony Validator(企业级)
  4. 通用校验基类(自定义封装)
  5. 针对RESTful API的完整示例
  6. 使用PHP 8 Attributes(现代PHP)
  7. 最佳实践建议

在PHP中做接口参数校验,我推荐以下几种方案:

基础手动校验(简单场景)

<?php
class UserController {
    public function createUser() {
        // 获取参数
        $data = json_decode(file_get_contents('php://input'), true);
        // 基础校验
        $errors = [];
        if (empty($data['username'])) {
            $errors[] = '用户名不能为空';
        } elseif (mb_strlen($data['username']) < 3 || mb_strlen($data['username']) > 20) {
            $errors[] = '用户名长度必须在3-20个字符之间';
        }
        if (empty($data['email'])) {
            $errors[] = '邮箱不能为空';
        } elseif (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
            $errors[] = '邮箱格式不正确';
        }
        if (!empty($errors)) {
            http_response_code(400);
            echo json_encode(['errors' => $errors]);
            return;
        }
        // 处理业务逻辑...
    }
}

使用Validation类库(推荐)

安装PHP Validation库

composer require respect/validation

使用示例

<?php
use Respect\Validation\Validator as v;
class ApiValidator {
    public static function validateCreateUser($data) {
        $validator = v::key('username', v::stringType()->length(3, 20))
            ->key('email', v::email())
            ->key('age', v::intType()->between(18, 100))
            ->key('gender', v::stringType()->in(['male', 'female']), false);
        try {
            $validator->assert($data);
            return ['valid' => true];
        } catch (\Respect\Validation\Exceptions\ValidationException $e) {
            return [
                'valid' => false,
                'errors' => $e->getMessages()
            ];
        }
    }
}

使用Symfony Validator(企业级)

<?php
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;
class CreateUserRequest {
    /**
     * @Assert\NotBlank()
     * @Assert\Length(min=3, max=20)
     */
    public $username;
    /**
     * @Assert\NotBlank()
     * @Assert\Email()
     */
    public $email;
    /**
     * @Assert\Type(type="integer")
     * @Assert\Range(min=18, max=100)
     */
    public $age;
}
// 使用
$validator = Validation::createValidatorBuilder()
    ->enableAnnotationMapping()
    ->getValidator();
$request = new CreateUserRequest();
$request->username = $_POST['username'] ?? '';
$request->email = $_POST['email'] ?? '';
$request->age = $_POST['age'] ?? '';
$errors = $validator->validate($request);
if (count($errors) > 0) {
    foreach ($errors as $error) {
        echo $error->getPropertyPath() . ': ' . $error->getMessage() . "\n";
    }
}

通用校验基类(自定义封装)

<?php
abstract class BaseApiController {
    protected function validate(array $rules, array $data) {
        $errors = [];
        foreach ($rules as $field => $validations) {
            $value = $data[$field] ?? null;
            foreach ($validations as $rule => $params) {
                $method = 'validate' . ucfirst($rule);
                if (method_exists($this, $method)) {
                    $result = $this->$method($value, $params);
                    if ($result !== true) {
                        $errors[$field][] = $result;
                    }
                }
            }
        }
        if (!empty($errors)) {
            $this->response(['errors' => $errors], 400);
        }
        return true;
    }
    protected function validateRequired($value) {
        return !empty($value) ? true : '该字段为必填项';
    }
    protected function validateEmail($value) {
        return filter_var($value, FILTER_VALIDATE_EMAIL) !== false 
            ? true : '邮箱格式不正确';
    }
    protected function validateMinLength($value, $min) {
        return strlen($value) >= $min ? true : "长度不能小于{$min}";
    }
    // 更多校验方法...
    protected function response($data, $status = 200) {
        http_response_code($status);
        echo json_encode($data);
        exit;
    }
}
// 使用
class OrderController extends BaseApiController {
    public function createOrder() {
        $params = [
            'user_id' => 123,
            'amount' => 100.50,
            'email' => 'test@example.com'
        ];
        $rules = [
            'user_id' => ['required', 'integer'],
            'amount' => ['required', 'numeric', 'min:0.01'],
            'email' => ['email']
        ];
        $this->validate($rules, $params);
        // 业务逻辑...
    }
}

针对RESTful API的完整示例

<?php
header('Content-Type: application/json; charset=utf-8');
class ApiValidator {
    private $data;
    private $errors = [];
    public function __construct(array $data) {
        $this->data = $data;
    }
    public function check($field, $rules) {
        if (!isset($this->data[$field])) {
            if (in_array('required', $rules)) {
                $this->errors[$field][] = '字段缺失';
            }
            return $this;
        }
        $value = $this->data[$field];
        foreach ($rules as $rule) {
            // 解析带参数的规则,如: min:3
            $parts = explode(':', $rule);
            $ruleName = $parts[0];
            $params = $parts[1] ?? null;
            switch ($ruleName) {
                case 'required':
                    if (empty($value)) {
                        $this->errors[$field][] = '该字段不能为空';
                    }
                    break;
                case 'email':
                    if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
                        $this->errors[$field][] = '邮箱格式不正确';
                    }
                    break;
                case 'numeric':
                    if (!is_numeric($value)) {
                        $this->errors[$field][] = '必须为数字';
                    }
                    break;
                case 'int':
                    if (!filter_var($value, FILTER_VALIDATE_INT)) {
                        $this->errors[$field][] = '必须为整数';
                    }
                    break;
                case 'min':
                    if ($value < $params) {
                        $this->errors[$field][] = "不能小于{$params}";
                    }
                    break;
                case 'max':
                    if ($value > $params) {
                        $this->errors[$field][] = "不能大于{$params}";
                    }
                    break;
                case 'string':
                    if (!is_string($value)) {
                        $this->errors[$field][] = '必须为字符串';
                    }
                    break;
            }
        }
        return $this;
    }
    public function validate() {
        if (!empty($this->errors)) {
            http_response_code(422);
            echo json_encode([
                'success' => false,
                'message' => '参数校验失败',
                'errors' => $this->errors
            ]);
            exit;
        }
        return true;
    }
}
// 使用
$data = json_decode(file_get_contents('php://input'), true) ?? $_POST;
$validator = new ApiValidator($data);
$validator
    ->check('username', ['required', 'string', 'min:3', 'max:20'])
    ->check('email', ['required', 'email'])
    ->check('age', ['numeric', 'min:18', 'max:100'])
    ->check('sex', ['string'])
    ->validate();
echo json_encode(['success' => true, 'data' => $data]);

使用PHP 8 Attributes(现代PHP)

<?php
#[Attribute]
class Validate {
    public function __construct(
        public string $rule,
        public mixed $params = null
    ) {}
}
class UserRequest {
    #[Validate('required')]
    #[Validate('minLength', 3)]
    public string $name;
    #[Validate('required')]
    #[Validate('email')]
    public string $email;
    #[Validate('numeric')]
    #[Validate('between', [18, 100])]
    public int $age;
}
// 反射验证
class RequestValidator {
    public static function validate(object $request): array {
        $reflection = new ReflectionClass($request);
        $errors = [];
        foreach ($reflection->getProperties() as $property) {
            $attributes = $property->getAttributes(Validate::class);
            $value = $property->getValue($request);
            foreach ($attributes as $attribute) {
                $validate = $attribute->newInstance();
                // 执行对应的验证逻辑...
            }
        }
        return $errors;
    }
}

最佳实践建议

  1. 分层校验:不要把校验逻辑放在业务逻辑中
  2. 统一错误格式:返回一致的错误结构
  3. 使用正规库:成熟可靠的库比手写更安全
  4. 考虑性能:对于高频接口,简单校验可能更合适
  5. 安全考虑:永远不要信任客户端数据

选择哪个方案取决于你的项目需求、PHP版本和团队熟悉度,对于大多数中小型项目,建议使用方案2(Respect Validation)或方案5(自定义验证器),它们既灵活又易维护。

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