检查PHP中可选参数的类型

时间:2013-02-27 03:26:25

标签: php

我正在检查PHP中的可选参数类型:

/**
 * Get players in the team using limit and
 * offset.
 *
 *
 * @param TeamInterface $participant
 * @param int $limit
 * @param int $offset
 * @throws \InvalidArgumentException
 * @return Players of a team
 */
public function getPlayers(TeamInterface $team, $limit = null, $offset = null)
{
    if (func_num_args() === 2 && !is_int($limit) ){
        throw new \InvalidArgumentException(sprintf('"Limit" should be of int type, "%s" given respectively.', gettype($limit)));
    }
    if (func_num_args() === 3 && (!is_int($limit) || !is_int($offset))){
        throw new \InvalidArgumentException(sprintf('"Limit" and "Offset" should be of int type, "%s" and "%s" given respectively.', gettype($limit), gettype($offset)));
    }
//.....

}

这有效,但有两个主要问题:

1 /如果我需要检查相同int类型的4/5可选参数的类型,则代码变得不必要地长。任何想法如何使这段代码更易于维护? (也许只使用一个if语句来检查同一类型的$limit$offset

2 / getPlayers($team, 2, null)抛出异常。知道函数实际上可以在这里处理null值吗?

5 个答案:

答案 0 :(得分:2)

你可以使用args数组进行for循环。类似的东西:

$args = func_get_args();
for ($i = 1; $i < 4; $i++) {
    if ($args[$i] !== null and !is_int($args[$i])) {
        throw ...
    }
}

当然,根据您需要检查的参数数量调整for条件。

或者...

$args = func_get_args();
// skip first
array_shift($args);
foreach ($args as $arg) {
    if ($arg !== null and !is_int($arg)) {
        throw ...
    }
}

答案 1 :(得分:1)

对于1)我会单独检查每个变量并为每个变量抛出异常:

   if (!is_int($limit)){
        //Throw
    }
    if (!is_int($offset))){
        //Throw
    }

这仍然需要每个变量的if语句,但是不那么冗长。

对于2)如果允许空值,您可以将检查更改为:

if ($offset && !is_int($offset))){
    //Throw
}

最后,我不建议您查看func_num_args()。在您的示例代码中,使用太多参数调用函数将绕过验证。

答案 2 :(得分:1)

我个人更喜欢每个函数只有一个参数(除非函数非常简单),例如函数可以取$request,并返回一个数据树$response。它使循环和扩展更容易一些:

function dostuff( $request ) {

   $team   = @$request['team'];
   $limit  = @$request['limit'];
   $offset = @$request['offset'];

   // ...
   return $response;
}

然后,为了验证,您可以在函数顶部编写一组规则,如

   // define validation rules
   $rules = array( 'required' => array('team'),      
                   'depends' => array('offset' => 'limit'),      
                   'types' => array('offset' => 'int', 'limit' => 'int' ),
            );

在一次通话中集中所有错误检查:

   // can throw exception
   argcheck( array( 'request' => $request, 'rules' => $rules ) );

这可能需要优化,但一般方法有助于在增加函数的复杂性时包含膨胀。

答案 3 :(得分:1)

PHP还没有标量的类型提示。

重新设计

当您开始在函数中使用大量可选参数时,您会产生代码味道。有些东西是错的,有一个物体在等待出现。

将所有可选参数构建为Object并在其上设置验证方法。

我认为你想要一个GameParameters对象,并且有一个验证方法。

getPlayers($gameParameters) {
}

将参数验证移至该对象,您可以将其构建到每个setter中,或者具有全面的validate()函数。

组合问题

就检查的爆炸性而言,我会构建一个错误数组,如果有错误则扔掉它。这可以在重新设计或不重新设计的情况下完成。

if ($limit != null && !is_int($limit){
  #add to the errors array      
}

if ($offset != null && !is_int($offset){
  #add to the errors array      
}

if (errors) {
  throw new \InvalidArgumentException(sprintf('"Limit" and "Offset" should be of int type, "%s" and "%s" given respectively.', gettype($limit), gettype($offset)));
}

答案 4 :(得分:0)

使用开关编码特定功能。

switch(gettype($limit)) {
    case "integer":
        //do other processing
    break;
}

你不能让你的代码容易受到攻击。至于克服漏洞的安全解决方案。创建一个这样的安全列表。

public function getPlayers(TeamInterface $team, $limit = null, $offset = null) {
    $safelist = array("var1" => "TeamInterface", "var2" => "integer", "var3" => "integer");
    $args = function_get_args();
    $status = true;
    foreach($args as $key => $var) {
        if(gettype($var)!=$safelist["var".$key]) {
             $status = false;
             break;
        }
    }
    if(!$status) break;

    //...........
}