我有一个类似的Web服务:
GET http://localhost/services/sum?a=1&b=2
这将直接解决(忽略授权等细节(定义如下的函数调用:
class Services {
public function sum(int $a, int $b) {
return $a + $b;
}
}
现在,如果他们的用户调用GET http://localhost/services/sum?a=abc&b=2
,这是一个PHP类型的错误。在调用sum
函数之前;类型检查";并报告错误。在这种情况下,响应将类似于
"errors" {
"a": {
"type_mismatch": {
"expected": "int",
"received": "string",
}
}
}
为此,我编写了以下函数:
function buildArguments(array $arguments, $service)
{
$reflectionMethod = new ReflectionFunction($service);
$reflectionParameters = $reflectionMethod->getParameters();
$missingArguments = [];
$typeMismatch = [];
foreach ($reflectionParameters as $reflectionParameter) {
$name = $reflectionParameter->getName();
if (!array_key_exists($name, $arguments) && !$reflectionParameter->isOptional()) {
$missingArguments[] = $reflectionParameter->getName();
} else if ((is_null($arguments[$name] ?? null) && !$reflectionParameter->getType()->allowsNull()) ||
!($reflectionParameter->getType()->getName() == gettype($arguments[$name]))) {
$typeMismatch[$name] = [
'received' => gettype($arguments[$name]),
'expected' => $reflectionParameter->getType()->getName()
];
}
}
$errors = [];
if (!empty($missingArguments)) {
$errors['missing_argument'] = $missingArguments;
}
if (!empty($typeMismatch)) {
$errors['type_mismatch'] = $typeMismatch;
}
if (empty($errors)) {
return true;
} else {
var_dump($errors);
return false;
}
}
它适用于字符串:
function concat(string $a, string $b) {
return $a . $b;
}
buildArguments(['a' => 'x', 'b' => 'y'], 'concat'); //ok!
buildArguments(['a' => 'x'], 'concat'); // missing_argument: b
buildArguments(['a' => 1, 'b' => 'y'], 'concat'); //type mismatch: a (expected integer, got string)
对于int:,它立即崩溃
function sum(int $a, int $b): int
{
return $a + $b;
}
buildArguments(['a' => 1, 'b' => 2], 'sum');
//type mismatch! expected "int" received "integer"
我只需要它来处理简单的结构:int、字符串、非类型化数组,不需要检查对象、继承、接口等等。我可以添加一个";如果int,则integer";但我有一种感觉,会有很多关于null和optionals的争论。有没有一种聪明的方法可以做到这一点?
TypeError在这方面没有提供任何帮助,只是一条字符串消息,也许我可以"手动";调用PHP调用的抛出TypeError的任何过程?
正如Stevish在一条评论中指出的那样,一旦将当前方法应用于URL而不是硬编码的测试值,则它将不起作用,因为$_GET['a']
将永远只是一个字符串(或未设置(。
相反,您需要查看期望的类型,并选择适当的验证函数。不幸的是,PHP没有一个很好的内置函数用于";是整数ish字符串";因此,您可能需要使用正则表达式(ctype_digit()
很接近,但不允许使用负数,因为'-'不是数字(。
为了避免太多的意大利面条,我会把验证代码从循环中分解出来,并有这样的东西:
$reflectionMethod = new ReflectionFunction($service);
$reflectionParameters = $reflectionMethod->getParameters();
$errors = [];
foreach ($reflectionParameters as $reflectionParameter) {
$name = $reflectionParameter->getName();
$errors[] = $this->validateParameter(
$name,
$reflectionParameter->getType(),
$arguments[$name] ?? null
);
}
然后
private function validateParameter(string $name, ReflectionType $expectedType, ?string $input): ?string {
if ( $input === null )
if ( !$expectedType->allowsNull() ) {
return "Missing required $name";
}
else {
// null allowed, no error
return null;
}
}
switch ( $expectedType->getName() ) {
case 'string':
// everything's a string
// unless you want to assume empty string is always an error?
return null;
break;
case 'integer':
if ( ! preg_match('/^-?[0-9]+$/', $input) ) {
return "Bad integer for $name";
}
break;
// Other cases ...
}
}