建议避免使用复杂的条件表达式,因为它们难以阅读和理解。但是,如果表达式只使用一种类型的逻辑运算符来连接条件,那就很容易理解了。
例如:
if ($a && $b || $c && ! $d) {
echo("Yes!");
}
您能一眼看出if 分支(echo())中的代码何时会执行吗?很可能没有。该表达式包含所有逻辑运算的混合,如果不绘制$a、$b、$c 和$d 的所有可能值的表,则很难评估其最终值。
另一方面,表达式:
if ($a && $b && $c && $d) {
echo("Hooray!");
}
更容易掌握。
当所有$a、$b、$c 和$d 同时为TRUE 时,就会执行echo() 语句。
重构
在您的情况下,情况比上面介绍的第一个更复杂。它包含所有逻辑运算符和括号,子表达式很长。
为了保持可读性,我会将复杂条件提取到返回布尔值(谓词)的(私有或受保护)方法中。
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;
}
}
并替换if 语句中的表达式:
if ($this->isValidData($data)) {
}
在接下来的步骤中,我会将复杂的条件表达式拆分为更小的条件,逐个测试它们,并尽可能早地使函数 isValidData() 返回 (FALSE)。
步骤 1
因为表达式很复杂,而且片段相对较长,所以很难按原样处理。这就是为什么在第一步中我会将条件提取到名称较短的局部变量中($a、$b a.s.o. 从上面):
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;
}
}
现在一切看起来都清楚了,我们甚至发现了一个错误。太好了!
第二步
让我们尝试解开(现在更容易阅读)条件:
if ($a && $b && (($c && ! $d) || ($e && ! $f))) {
return TRUE;
} else {
return FALSE;
}
如果我们一次进行一项测试并在结果明显后立即返回(TRUE 或FALSE),可能会更容易理解。
当$a 和$b 以及包含表达式其余部分的括号同时为TRUE 时,该函数返回TRUE。
if 语句可以这样重写:
if ($a) {
if ($b) {
if (($c && ! $d) || ($e && ! $f))) {
return TRUE;
}
}
}
return FALSE;
通过反转$a 和$b 的条件,我们可以提前返回FALSE:
if (! $a) {
return FALSE;
}
if (! $b) {
return FALSE;
}
if (($c && ! $d) || ($e && ! $f))) {
return TRUE;
}
return FALSE;
现在的代码如下所示:
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;
}
}
请注意,因为从第二个开始的所有赋值都包含公共子表达式$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']);
第三步
让我们注意在表达式$c && ! $d:
array_key_exists('salesRegion', $company) && ! empty($company['salesRegion'])
对array_key_exists() 的调用并不是真正需要的。我猜它的目的是避免在访问$company['salesRegion'] 并且$company 不包含密钥'salesRegion' 时触发通知。 empty() PHP 结构会在数组中不存在键且不会触发通知时愉快地返回 TRUE。
$e && ! $f 也是如此。这使我们可以完全删除$c 和$e。现在的情况是这样的:
if (! $d || ! $f) {
return TRUE;
}
或者
if (! ($d && $f)) {
return TRUE;
}
通过否定条件:
if ($d && $f) {
return FALSE;
} else {
return TRUE;
}
现在完整的函数如下所示:
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;
}
}
最后一步
我们现在可以消除局部变量$a、$b、$d 和$f,并在我们知道它们不是NULL 之后移动$form 和$company 的初始化(每个后对应的调用array_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;
}
结论
代码比以前长了几行,但是:
- 条件更易于阅读和理解,因为它们是一个接一个地计算的,而不是在一个大而复杂的表达式中;
- 代码被封装在一个函数中;
if ($this->isValidData($data)) 条件的读者现在一目了然发生了什么,而无需阅读和理解它是如何发生的;如果它有效,请不要担心;
- 验证代码可以独立于数据有效与否时发生的情况进行测试;
- 如果需要和适当,可以通过调用具有不同数据的方法(可能通过不同的表单提交)来重用代码。