本文目录导读:

在 PHP 中实现回调校验(Callback Validation)通常有以下几种常见场景和方法:
函数回调校验
检查回调是否有效
<?php
// 检查是否是有效的回调
function validateCallback($callback) {
if (!is_callable($callback)) {
throw new InvalidArgumentException('Invalid callback provided');
}
return true;
}
// 使用示例
$callbacks = [
'myFunction', // 函数名字符串
['MyClass', 'method'], // 类方法数组
[$object, 'method'], // 对象方法数组
function ($data) { // 匿名函数
return $data;
},
'MyClass::staticMethod' // 静态方法字符串
];
foreach ($callbacks as $callback) {
try {
validateCallback($callback);
echo "Valid callback\n";
} catch (Exception $e) {
echo "Invalid callback: " . $e->getMessage() . "\n";
}
}
回调参数校验
<?php
class CallbackValidator {
/**
* 校验回调函数返回的数据
*/
public static function validateResult($callback, $data) {
if (!is_callable($callback)) {
throw new InvalidArgumentException('Callback is not callable');
}
$result = call_user_func($callback, $data);
// 验证返回结果
if ($result === false) {
throw new RuntimeException('Callback returned false');
}
return $result;
}
/**
* 带参数数量的回调校验
*/
public static function validateWithArgs($callback, $expectedArgs = 0) {
if (!is_callable($callback)) {
throw new InvalidArgumentException('Callback is not callable');
}
// 获取回调的参数数量(仅对函数有效)
if (is_string($callback) && function_exists($callback)) {
$reflection = new ReflectionFunction($callback);
$actualArgs = $reflection->getNumberOfParameters();
if ($actualArgs < $expectedArgs) {
throw new InvalidArgumentException(
"Callback expects {$actualArgs} arguments, but {$expectedArgs} required"
);
}
}
return true;
}
}
数组回调的高级校验
<?php
function validateArrayCallback($array, $callback) {
if (!is_callable($callback)) {
return false;
}
$results = array_map($callback, $array);
// 检查全部通过
foreach ($results as $key => $result) {
if (!$result) {
return [
'valid' => false,
'failed_key' => $key,
'message' => "Validation failed for key: {$key}"
];
}
}
return ['valid' => true];
}
// 使用示例
$data = [
'email' => 'user@example.com',
'age' => 25,
'name' => 'John Doe'
];
$validationRules = [
'email' => function ($value) {
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
},
'age' => function ($value) {
return is_numeric($value) && $value >= 18;
},
'name' => function ($value) {
return is_string($value) && strlen($value) > 2;
}
];
foreach ($data as $key => $value) {
$result = validateArrayCallback([$value], $validationRules[$key]);
echo "Field '{$key}': " . ($result['valid'] ? 'Passed' : 'Failed') . "\n";
}
类型校验的回调
<?php
class TypeValidator {
/**
* 创建类型校验回调
*/
public static function makeTypeValidator($type) {
return function ($value) use ($type) {
switch ($type) {
case 'string':
return is_string($value);
case 'integer':
case 'int':
return is_int($value);
case 'float':
case 'double':
return is_float($value) || is_double($value);
case 'array':
return is_array($value);
case 'boolean':
case 'bool':
return is_bool($value);
case 'null':
return is_null($value);
case 'numeric':
return is_numeric($value);
default:
return false;
}
};
}
/**
* 校验多个字段
*/
public static function validateFields($data, $rules) {
$errors = [];
foreach ($rules as $field => $validator) {
if (!isset($data[$field])) {
$errors[$field] = "Field '{$field}' is missing";
continue;
}
if (is_callable($validator) && !$validator($data[$field])) {
$errors[$field] = "Field '{$field}' failed validation";
}
}
return empty($errors) ? true : $errors;
}
}
// 使用示例
$data = [
'username' => 'john_doe',
'email' => 'invalid-email',
'age' => 'twenty-five',
'active' => true
];
$rules = [
'username' => TypeValidator::makeTypeValidator('string'),
'email' => function ($value) {
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
},
'age' => TypeValidator::makeTypeValidator('integer'),
'active' => TypeValidator::makeTypeValidator('boolean')
];
$result = TypeValidator::validateFields($data, $rules);
if ($result === true) {
echo "All validation passed!\n";
} else {
echo "Validation errors:\n";
foreach ($result as $field => $error) {
echo " - {$error}\n";
}
}
异步回调校验(Promise 模式)
<?php
class AsyncCallbackValidator {
public static function validateAsync($callback) {
if (!is_callable($callback)) {
return Promise::reject(new InvalidArgumentException('Invalid callback'));
}
return Promise::resolve()
->then(function () use ($callback) {
$result = call_user_func($callback);
if (!$result) {
throw new RuntimeException('Validation failed');
}
return $result;
});
}
}
最佳实践建议
<?php
/**
* 完整的回调校验框架
*/
class SecureCallbackValidator {
private $allowedNamespaces = ['App\\Validators\\'];
/**
* 安全的回调校验
*/
public function validate($callback, $options = []) {
// 1. 基本可调用性检查
if (!is_callable($callback)) {
throw new InvalidArgumentException('Callback is not callable');
}
// 2. 字符串回调安全检查(防止代码注入)
if (is_string($callback)) {
$this->checkStringCallback($callback);
}
// 3. 反射检查
$reflection = $this->getReflection($callback);
if (!$reflection->isUserDefined()) {
throw new SecurityException('Cannot use internal PHP functions as callback');
}
// 4. 参数数量验证
if (isset($options['expected_params'])) {
$this->checkParameterCount($reflection, $options['expected_params']);
}
return true;
}
private function checkStringCallback($callback) {
// 防止任意代码执行
if (strpos($callback, '::') !== false || strpos($callback, '->') !== false) {
throw new SecurityException('Unsafe callback string format');
}
}
private function getReflection($callback) {
if (is_array($callback)) {
return new ReflectionMethod($callback[0], $callback[1]);
}
if (is_string($callback)) {
return new ReflectionFunction($callback);
}
return new ReflectionFunction($callback);
}
private function checkParameterCount($reflection, $expected) {
$actual = $reflection->getNumberOfParameters();
if ($actual !== $expected) {
throw new InvalidArgumentException(
"Callback expects {$actual} parameters, {$expected} given"
);
}
}
}
实际应用示例
<?php
// 表单验证框架
class FormValidator {
private $rules;
public function addRule($field, $validator) {
$this->validateCallback($validator);
$this->rules[$field][] = $validator;
return $this;
}
public function validate($data) {
$errors = [];
foreach ($this->rules as $field => $validators) {
foreach ($validators as $validator) {
$result = call_user_func($validator, $data[$field] ?? null);
if (is_string($result)) {
// 返回错误消息
$errors[$field] = $result;
break;
} elseif ($result === false) {
$errors[$field] = "{$field} is invalid";
break;
}
}
}
return empty($errors) ? true : $errors;
}
private function validateCallback($callback) {
if (!is_callable($callback)) {
throw new InvalidArgumentException('Invalid callback');
}
}
}
// 使用
$validator = new FormValidator();
$validator
->addRule('username', function ($value) {
return preg_match('/^[a-zA-Z0-9_]{3,20}$/', $value) ?: 'Invalid username format';
})
->addRule('email', function ($value) {
return filter_var($value, FILTER_VALIDATE_EMAIL) ? 'Invalid email' : true;
});
$data = [
'username' => 'john',
'email' => 'invalid-email'
];
$result = $validator->validate($data);
if (is_array($result)) {
print_r($result);
}
这些方法覆盖了 PHP 回调校验的主要场景,根据你的具体需求,可以选择合适的方法来实现回调校验,关键是要确保:
- 回调确实可调用
- 回调的参数和返回值符合预期
- 考虑安全性,防止恶意代码执行