【问题标题】:Testing optional arguments in PHP在 PHP 中测试可选参数
【发布时间】:2012-01-23 15:27:19
【问题描述】:

我有一些跨类的“setter”方法,为了方便起见,我添加了一个可选参数$previous,它通过引用获取一个参数并用现有值填充它,然后用新值替换它。例如:

public function set_value($key, $value, &$previous = null)
{
    $previous = $this->get_value($key);
    $this->_values[$key] = $value;
    return $this;
}

这很好用;但是在某些情况下,相应的“getter”方法有点过程密集,无条件运行它是一种浪费。我想我可以测试一下:

if(null !== $previous)
{
    $previous = $this->get_value($key);
}

这不起作用,因为通常作为$previous 的参数传递的变量之前没有在它的范围内定义,并且无论如何默认为null。我破解的唯一解决方案是:

public function set_value($key, $value, &$previous = null)
{
    $args = func_get_args();
    if(isset($args[2])
    {
        $previous = $this->get_value($key);
    }
    $this->_values[$key] = $value;
    return $this;
}

或者,单行:

if(array_key_exists(2, func_get_args()))
{
    // ...
}

我不喜欢方法体依赖于参数索引(当它看起来应该是不必要的时候)有没有更简洁的方法来实现我在这里的目标?


我试过了:

if(isset($previous)){}

if(!empty($previous)){}

if(null !== $previous){}

都不行。

到目前为止可能的解决方案:

if(func_num_args() == $num_params){}

if(array_key_exists($param_index, func_get_args())){}

// 5.4
if(isset(func_get_args()[$param_index])){}

// 5.4
if(func_num_args() == (new \ReflectionMethod(__CLASS__, __FUNCTION__))
    ->getNumberOfParameters()){}

@DaveRandom -- 所以,在以下方面:

define('_NOPARAM', '_NOPARAM' . hash('sha4096', microtime()));

function foo($bar = _NOPARAM)
{
    // ...
}

@hoppa -- 用例:

$obj->set_something('some_key', $some_value, $previous) // set
    ->do_something_that_uses_some_key()
    ->set_something('some_key', $previous) // and reset
    ->do_something_that_uses_some_key()
    -> ...

代替:

$previous = $obj->get_something('some_key'); // get
$obj->set_something('some_key', $some_value) // set
    ->do_something_that_uses_some_key();
    ->set_something($previous) // and reset
    ->do_something_that_uses_some_key();
    -> ...

【问题讨论】:

  • 您不能将$previous 的默认值定义为FALSE(或“错误”类型的某个值) - 那么您知道它是null 它已通过,但是它不是FALSE(或其他)。此方法也有漏洞(用户可能会传递您的默认值),但我认为这将是一个不错的方法 - 特别是如果您将默认值设为一个长随机字符串,该字符串极不可能出现在传递的变量中.
  • @DaveRandom -- 在某些情况下,先前的值可能是布尔值 false。我选择了null,因为它的语义意图是“缺乏价值”。
  • 查看关于长随机字符串的编辑评论 - 我承认这不是一个漂亮或完美的方法,但它是一个有效的 99.99999% 方法......
  • @DaveRandom -- 我想我可以定义一些任意字符串作为默认值,但是(非常类似于md5 的参数)仍然存在冲突的可能性。我认为这将是微不足道的,我只是忽略了一些东西。
  • 另一个变量$fetch_previous 并明确测试$fetch_previous == true 怎么样?返工太多?

标签: php pass-by-reference default-value optional-parameters


【解决方案1】:

摘自上面的 cmets / 讨论:

为了检查参数是否被传递,您有 2 个选项 - 根据值检查参数的值(就像您对 null 所做的那样)或检查参数的数量。

如果您使用第一个选项,则没有无法从函数外部传递的值,因此总会有误报的机会(现在 null 发生的情况相同)。 DaveRandom 的带有随机字符串的示例在大多数情况下应该足够了,但我认为它是矫枉过正的。

我认为第二个选项是最干净的(快速、可读等)。作为对 func_get_args 的小改进,我将使用 func_num_args - 这样您将检查传递参数的数量,而不是参数索引。

【讨论】:

  • 感谢@draevor -- 检查我在“我已经尝试过/可能的解决方案”下的编辑,最后一个(一旦 5.4 上街 ) 对于特征中的类不可知的可重用性非常有效,因为 $previous 参数始终是最后一个。就像我之前说的那样,你的答案肯定是最好的候选人,我会让这个坐一会儿,如果没有其他结果,我会标记你的。
【解决方案2】:

可能不是您想要解决问题的方式(以某种方式测试可选参数),但这就是我将如何实现它:

public function set_value($key, $value)
{
    $this->_values[$key] = $value;
    return $this;
}
public function set_get_value($key, $value, &$previous)
{
    $previous = $this->get_value($key);
    $this->_values[$key] = $value;
    return $this;
}

用例示例:

$obj->set_get_something('some_key', $some_value, $previous) // set AND get
    ->do_something_that_uses_some_key()
    ->set_something('some_key', $previous) // and reset
    ->do_something_that_uses_some_key()
    -> ...

为什么要使用其他函数?

这个解决方案有几个优点:

  1. 名称更明确,对其他编码人员的混淆更少
  2. 没有隐藏的副作用
  3. 解决了(未定义的)变量已经有值的问题
  4. 没有调用func_num_args 或其他一些“元”函数的开销

编辑:代码中的错字。

编辑 2:删除了 &$previous set_get_value() 函数的默认值(感谢 draevor)

【讨论】:

  • 不错的选择。一个小的修正 - &$previous 在第二个函数中不需要有默认值,它应该在那里。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-10-16
  • 2021-12-29
  • 2012-10-06
  • 2019-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多