我有一个像这样的代码,我只想知道在单个if语句中使用大量'和'或'是好习惯
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'][''])))
) {
}
或者有更好的方法吗?
答案 0 :(得分:2)
PHP短路if语句。因此,如果您有多个if子句。
if ($condition1 && $condition2 && $condition3 && $condition4)
然后,只要第一个表达式为假,PHP就会停止评估表达式。
要优化代码,请对表达式进行排序,以便首先评估最可能的失败代码。
参考文献: http://php.net/manual/en/language.operators.logical.php
Does PHP have short-circuit evaluation?
Is there a short-circuit OR in PHP that returns the left-most value?
答案 1 :(得分:2)
建议避免使用复杂的条件表达式,因为它们难以阅读和理解。但是,如果表达式仅使用一种类型的逻辑运算符来连接条件,则可以很容易地理解它。
例如:
if ($a && $b || $c && ! $d) {
echo("Yes!");
}
乍一看,if
分支(echo()
)中的代码何时执行?很可能没有。表达式包含所有逻辑运算的混合,如果不绘制$a
,$b
,$c
和$d
的所有可能值的表格,则很难评估其最终值
另一方面,表达式:
if ($a && $b && $c && $d) {
echo("Hooray!");
}
更容易掌握
当echo()
,$a
,$b
和$c
同时$d
时,TRUE
语句会执行。
在您的情况下,条件比上面提到的第一个更复杂。它包含所有逻辑运算符和括号,子表达式很长。
为了保持可读性,我会将复杂条件提取到(私有或受保护)方法中,该方法返回一个布尔值(谓词)。
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
)。
因为表达式很复杂且片段相对较长,所以很难按原样使用它。这就是为什么,在第一步我将条件提取到具有较短名称的局部变量($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
),则可能会更容易理解。
该函数在TRUE
和$a
时返回$b
,并且包含表达式其余部分的括号在同一时间全部为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
,然后移动$form
和$company
的初始化我们知道它们不是NULL
(每个人在对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))
条件的读者现在一眼就能理解会发生什么,而不需要阅读和理解它是如何发生的;如果有效则不要担心; 答案 2 :(得分:0)
就可读性而言,您应该正确格式化代码,一切都应该没问题(给定示例中的格式化正常)。在调试方面,在定义哪个语句失败时可能存在一些问题,但仍然 - 使用正确的格式化,您可以只注释掉几行easyli并检查结果。在表现的情况下,我认为它甚至更好。我的意思是你无论如何都必须以某种方式检查所有这些条件。如果你把它们全部放在不同的ifs中,它们会一个接一个地被检查(好的,你可以写它以便不是所有的都被检查,但我认为它只是不必要地延长你的代码)。如果您正确撰写陈述,则不必检查所有陈述。例如,如果第一个条件为假,则有大量&&
,甚至不检查下一个条件。你应该在开始时放置更少的可能性,更轻量级(在性能方面)条件,并在最后留下沉重的,可能的。如果你想保持你的主要代码干净,你也可以把它包装在一个函数中。
答案 3 :(得分:0)
这是一种糟糕的性能解决方案。但是,一般来说,我只是在处理表单时并不关心它,如果它是地球上最有效的东西 - 它不是内核代码。我刚刚解决了一个类似的问题,就是抛出192演出的ram .....在此基础上它可以节省数百到数千个开发人员的时间,这比ram要贵很多。
$hasdata = array_key_exists(self::SUB_FORM_CONTACT, $data);
$hasCompany = array_key_exists('company', $data[self::SUB_FORM_CONTACT]);
$hasSalesRegion = array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company'])
$hasSalesRegion = $hasSalesRegion && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion']);
$hasServiceRegion = array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company'])
$hasServiceRegion = $hasServiceRegion && !empty($data[self::SUB_FORM_CONTACT]['company']);
$evalResult = $hasdata && $hasCompany && ($hasSalesRegion || $hasServiceRegion);
if($evalResult)
{
// do your thing
}
$hasdata = array_key_exists(self::SUB_FORM_CONTACT, $data);
$hasCompany = array_key_exists('company', $data[self::SUB_FORM_CONTACT]);
$hasSalesRegion = array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company'])
$hasSalesRegion = $hasSalesRegion && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion']);
$hasServiceRegion = array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company'])
$hasServiceRegion = $hasServiceRegion && !empty($data[self::SUB_FORM_CONTACT]['company']);
$evalResult = $hasdata && $hasCompany && ($hasSalesRegion || $hasServiceRegion);
if($evalResult)
{
// do your thing
}
我更希望看到这样的东西,而不是获得短切割的好处,并且难以阅读双重否定。