1

我正在检查 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/ 如果我需要检查相同类型的 4/5 可选参数的int类型,代码会变得不必要的长。任何想法如何使这段代码更易于维护?(也许只使用一个if语句来检查两者的相同类型$limit$offset

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

4

5 回答 5

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 ...
    }
}
于 2013-02-27T03:40:39.233 回答
1

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

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

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

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

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

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

于 2013-02-27T03:41:53.060 回答
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 ) );

这可能需要优化,但是当您增加函数的复杂性时,一般方法有助于控制膨胀。

于 2013-02-27T03:46:22.007 回答
1

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

重新设计

当您开始在函数中使用大量可选参数时,您会产生代码异味。出了点问题,有一个对象等待出现。

将所有可选参数构建为 Object 并在其上使用 validate 方法。

我认为您想要一个 GameParameters 对象并在其上有一个 validate 方法。

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)));
}
于 2013-02-27T03:47:26.523 回答
0

使用 switch 对特定功能进行编码。

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;

    //...........
}
于 2013-02-27T03:34:54.967 回答