在 if 语句中使用大量的 and 条件好吗?

use*_*303 3 php arrays if-statement

我有一个类似这样的代码,我只是想知道在单个 if 语句中使用大量“and”“or”是否是个好习惯

if (array_key_exists(self::SUB_FORM_CONTACT, $data) &&
            array_key_exists('company', $data[self::SUB_FORM_CONTACT]) &&
            ((array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion'])) ||
                (array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company'][''])))
        ) {

        }
Run Code Online (Sandbox Code Playgroud)

或者有没有更好的方法来做到这一点?

axi*_*iac 5

建议避免复杂的条件表达式,因为它们难以阅读和理解。但是,如果表达式仅使用一种类型的逻辑运算符来连接条件,则可以很容易理解。

例如:

if ($a && $b || $c && ! $d) {
    echo("Yes!");
}
Run Code Online (Sandbox Code Playgroud)

你能一眼看出if分支( echo())中的代码什么时候执行吗?很可能不会。该表达式包含所有逻辑运算的混合,如果不绘制$a$b$c和的所有可能值的表,则很难评估其最终值$d

另一方面,表达式:

if ($a && $b && $c && $d) {
    echo("Hooray!");
}
Run Code Online (Sandbox Code Playgroud)

更容易掌握。当、 、和全部同时出现时,
该语句执行。echo()$a$b$c$dTRUE

重构

就您而言,情况比上面介绍的第一个情况更复杂。它包含所有逻辑运算符和括号,并且子表达式相当长。

为了保持其可读性,我会将复杂的条件提取到返回布尔值(谓词)的(​​私有或受保护)方法中。

private function isValidData(array $data)
{
    if (array_key_exists(self::SUB_FORM_CONTACT, $data) &&
        array_key_exists('company', $data[self::SUB_FORM_CONTACT]) &&
        ((array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion'])) ||
            (array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company'][''])))
    ) {
        return TRUE;
    } else {
        return FALSE;
    }
}
Run Code Online (Sandbox Code Playgroud)

并替换语句中的表达式if

if ($this->isValidData($data)) {
}
Run Code Online (Sandbox Code Playgroud)

在接下来的步骤中,我会将复杂的条件表达式拆分为较小的条件,逐一测试它们,并在可能的情况下使函数尽早isValidData()返回 ( FALSE)。

步骤1

由于表达复杂且片段相对较长,因此很难按原样使用它。这就是为什么,在第一步中,我将条件提取到具有较短名称的局部变量中($a$b如上):

private function isValidData(array $data)
{
    $a = array_key_exists(self::SUB_FORM_CONTACT, $data);
    $b = array_key_exists('company', $data[self::SUB_FORM_CONTACT]);
    $c = array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']);
    $d = empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion']);
    $e = array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']);
    $f = empty($data[self::SUB_FORM_CONTACT]['company']['']);
    // this should probably read 'serviceRegion' -------^

    if ($a && $b && (($c && ! $d) || ($e && ! $f))) {
        return TRUE;
    } else {
        return FALSE;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在一切看起来都很清楚了,我们甚至发现了一个错误。伟大的!

第2步

让我们尝试理清这个(现在更容易阅读)条件:

if ($a && $b && (($c && ! $d) || ($e && ! $f))) {
    return TRUE;
} else {
    return FALSE;
}
Run Code Online (Sandbox Code Playgroud)

如果我们一次进行一个测试并在结果明显后立即返回(TRUE或),可能会变得更容易理解。当和以及包含表达式其余部分的括号同时出现时,FALSE该函数返回。TRUE$a$bTRUE

if语句可以这样重写:

if ($a) {
    if ($b) {
        if (($c && ! $d) || ($e && ! $f))) {
            return TRUE;
        }
    }
}

return FALSE;
Run Code Online (Sandbox Code Playgroud)

通过反转 和 的条件$a$b我们可以FALSE提前返回:

if (! $a) {
    return FALSE;
}
if (! $b) {
    return FALSE;
}

if (($c && ! $d) || ($e && ! $f))) {
    return TRUE;
}
return FALSE;
Run Code Online (Sandbox Code Playgroud)

代码现在看起来像这样:

private function isValidData(array $data)
{
    $a = array_key_exists(self::SUB_FORM_CONTACT, $data);
    $form = $data[self::SUB_FORM_CONTACT];

    $b = array_key_exists('company', $form);

    $c = array_key_exists('salesRegion', $form['company']);
    $d = empty($form['company']['salesRegion']);

    $e = array_key_exists('serviceRegion', $form['company']);
    $f = empty($form['company']['serviceRegion']);


    if (! $a) {
        return FALSE;
    }
    if (! $b) {
        return FALSE;
    }

    if (($c && ! $d) || ($e && ! $f))) {
        return TRUE;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,因为从第二个开始的所有赋值都包含公共子表达式,所以$data[self::SUB_FORM_CONTACT]我们已将其提取到局部变量中。同样的情况也适用于最后 4 个赋值(提取公共子表达式$form['company']

// ...
$b = array_key_exists('company', $form);
$company = $form['company'];

$c = array_key_exists('salesRegion', $company);
$d = empty($company['salesRegion']);

$e = array_key_exists('serviceRegion', $company);
$f = empty($company['serviceRegion']);
Run Code Online (Sandbox Code Playgroud)

步骤3

让我们注意表达式中的内容$c && ! $d

array_key_exists('salesRegion', $company) && ! empty($company['salesRegion'])
Run Code Online (Sandbox Code Playgroud)

调用 toarray_key_exists()并不是真正需要的。我猜它的目的是避免在$company['salesRegion']访问且不$company包含密钥时触发通知'salesRegion'。当数组中不存在该键并且不会触发通知时,PHPempty()构造将愉快地返回。TRUE

同样的情况也发生在$e && ! $f. 这使我们能够完全删除$c$e。现在的情况是这样的:

if (! $d || ! $f) {
    return TRUE;
}
Run Code Online (Sandbox Code Playgroud)

或者

if (! ($d && $f)) {
    return TRUE;
}
Run Code Online (Sandbox Code Playgroud)

通过否定条件:

if ($d && $f) {
    return FALSE;
} else {
    return TRUE;
}
Run Code Online (Sandbox Code Playgroud)

完整的函数现在如下所示:

private function isValidData(array $data)
{
    $a = array_key_exists(self::SUB_FORM_CONTACT, $data);
    $form = $data[self::SUB_FORM_CONTACT];

    $b = array_key_exists('company', $form);
    $company = $form['company'];

    $d = empty($company['salesRegion']);
    $f = empty($company['serviceRegion']);


    if (! $a) {
        return FALSE;
    }
    if (! $b) {
        return FALSE;
    }

    if ($d && $f) {
        return FALSE;
    } else {
        return TRUE;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一步

现在,我们可以消除局部变量$a、和$b,并在知道它们不是之后移动和的初始化(每个变量都在对 的相应调用之后):$d$f$form$companyNULLarray_key_exists()

该函数的最终形式是这样的:

/**
 * Verify if the provided data is valid.
 *
 * @param array $data the data to validate
 * @return boolean TRUE if the data is valid, FALSE if some field is missing or empty
 */
private function isValidData(array $data)
{
    // $data must contain the contact form information
    if (! (array_key_exists(self::SUB_FORM_CONTACT, $data))) {
        return FALSE;
    }

    // The contact form information exists in $data
    $form = $data[self::SUB_FORM_CONTACT];
    // The form must contain company information
    if (! (array_key_exists('company', $form))) {
        return FALSE;
    }

    // The company information exists in the contact form
    $company = $form['company'];
    // The company must contains information about salesRegion or serviceRegion or both
    if (empty($company['salesRegion']) && empty($company['serviceRegion'])) {
        return FALSE;
    }

    // The data is valid
    return TRUE;
}
Run Code Online (Sandbox Code Playgroud)

结论

该代码比以前长了几行,但是:

  • 这些条件更容易阅读和理解,因为它们是一个接一个地计算的,而不是在一个大的复杂表达式中;
  • 代码封装在函数中;现在,该条件的读者if ($this->isValidData($data))一眼就能明白那里发生了什么,而无需阅读和理解它是如何发生的;如果它有效,那就不用担心;
  • 验证代码的测试可以独立于数据有效或无效时发生的情况;
  • 如果需要并且适当的话,可以通过使用不同的数据(可能通过不同的表单提交)调用方法来重用代码。