如何避免isset()和empty()

我有几个较旧的应用程序在E_NOTICE错误级别上运行时会抛出大量的“xyz is undefined”和“undefined offset”消息,因为variables的存在没有使用isset()和consorts显式检查。

我正在考虑通过它们来使它们与E_NOTICE兼容,因为有关缺lessvariables或偏移量的通知可能是救生员,可能会有一些微小的性能改进,并且总体而言是更简洁的方式。

但是,我不喜欢对我的代码造成数百个isset() empty()array_key_exists() 。 它变得臃肿,变得不太可读,没有获得任何价值或意义方面的东西。

我怎样才能构build我的代码没有多余的variables检查,同时也是E_NOTICE兼容?

对于那些有兴趣的人,我已经将这个主题扩展为一个小文章,它提供了一个更好的结构化的forms下面的信息: PHP的权威指南isset和空


恕我直言,你应该考虑不只是使应用程序“E_NOTICE兼容”,但重组整个事情。 在您的代码中有数百个点经常尝试使用不存在的variables听起来像是一个相当糟糕的程序。 尝试访问不存在的variables永远不会发生,其他语言在编译时就会遇到这种情况。 PHP允许你这样做并不意味着你应该这样做。

这些警告是帮助你,而不是惹恼你。 如果您收到警告“您正在尝试使用不存在的东西!” ,你的反应应该是“糟糕,我不好,让我尽快解决。” 你还有什么其他的方法来区分“可以正确工作的variables”以及可能导致严重错误的错误代码 ? 这也是为什么您总是始终将错误报告转换为11的原因,并始终阻止您的代码,直到没有发出任何NOTICE 。 closures错误报告仅适用于生产环境,以避免信息泄漏,即使面对错误的代码也能提供更好的用户体验。


详细说明:

你总是需要在你的代码的某个地方放置或empty ,减less它们发生的唯一方法是正确地初始化你的variables。 根据情况有不同的方法来做到这一点:

函数参数:

 function foo ($bar, $baz = null) { ... } 

无需检查$bar$baz是否在函数内部设置,因为您只需设置它们,所有您需要担心的是如果它们的值计算结果为truefalse (或其他)。

任何地方的正则变

 $foo = null; $bar = $baz = 'default value'; 

在您要使用它们的代码块的顶部初始化您的variables。 这解决了!isset问题,确保你的variables总是有一个已知的默认值,给读者一个下面的代码将工作的概念,从而也作为一种自我文档。

arrays:

 $defaults = array('foo' => false, 'bar' => true, 'baz' => 'default value'); $values = array_merge($defaults, $incoming_array); 

和上面一样,你正在使用默认值初始化数组,并用实际值覆盖它们。

在剩余的情况下,让我们来说一个模板,你输出的值可能是也可能不是由控制器设置,你只需要检查:

 <table> <?php if (!empty($foo) && is_array($foo)) : ?> <?php foreach ($foo as $bar) : ?> <tr>...</tr> <?php endforeach; ?> <?php else : ?> <tr><td>No Foo!</td></tr> <?php endif; ?> </table> 

如果你经常使用array_key_exists ,你应该评估你使用的是什么。 唯一有所作为的是这里:

 $array = array('key' => null); isset($array['key']); // false array_key_exists('key', $array); // true 

如上所述,如果你正确地初始化你的variables,你不需要检查密钥是否存在,因为你知道它是否存在。 如果你从外部数据源获得数组,这个值很可能不是null而是''0'0'false或类似的东西,也就是你可以用isset或者empty值来计算的值,这取决于你的意图。 如果您经常将数组键设置为null并且希望它的含义不是false ,即如果在上面的示例中issetarray_key_exists的不同结果对您的程序逻辑有所不同,那么您应该问自己为什么。 variables的存在不应该是重要的,只有它的价值才是重要的。 如果键是true / false标志,则使用truefalse ,而不是null 。 唯一的例外是第三方库希望null意味着什么,但是由于null在PHP中很难被检测到,所以我还没有find任何这样的库。

只要写一个函数。 就像是:

 function get_string($array, $index, $default = null) { if (isset($array[$index]) && strlen($value = trim($array[$index])) > 0) { return get_magic_quotes_gpc() ? stripslashes($value) : $value; } else { return $default; } } 

你可以使用它

 $username = get_string($_POST, 'username'); 

get_number()get_boolean()get_array()等一样简单的东西也一样。

我相信解决这个问题的最好方法之一是通过一个类访问GET和POST(COOKIE,SESSION等)数组的值。

为每个数组创build一个类并声明__get__set方法( 重载 )。 __get接受一个参数,它将是一个值的名称。 该方法应该使用isset()empty()检查相应全局数组中的值,如果存在则返回值,否则返回null (或其他默认值)。

之后,您可以以这种方式自信地访问数组值: $POST->username ,如果需要,可以不使用任何isset() s或empty() 。 如果username不存在于相应的全局数组中,则返回null ,因此不会生成警告或通知。

我不介意使用array_key_exists() ,事实上我更喜欢使用这个特定的函数,而不是依靠黑客函数,这可能会改变他们在将来的行为, emptyisset (通过避免易感性 )。


不过,我使用一个简单的函数,在这个和其他一些处理数组索引的情况下:

 function Value($array, $key, $default = false) { if (is_array($array) === true) { settype($key, 'array'); foreach ($key as $value) { if (array_key_exists($value, $array) === false) { return $default; } $array = $array[$value]; } return $array; } return $default; } 

假设你有以下数组:

 $arr1 = array ( 'xyz' => 'value' ); $arr2 = array ( 'x' => array ( 'y' => array ( 'z' => 'value', ), ), ); 

你如何从数组中获得“价值”? 简单:

 Value($arr1, 'xyz', 'returns this if the index does not exist'); Value($arr2, array('x', 'y', 'z'), 'returns this if the index does not exist'); 

我们已经覆盖了单维和multidimensional array,我们还可以做什么?


以下面的一段代码为例:

 $url = 'https://stackoverflow.com/questions/1960509'; $domain = parse_url($url); if (is_array($domain) === true) { if (array_key_exists('host', $domain) === true) { $domain = $domain['host']; } else { $domain = 'N/A'; } } else { $domain = 'N/A'; } 

很无聊不是吗? 这是使用Value()函数的另一种方法:

 $url = 'https://stackoverflow.com/questions/1960509'; $domain = Value(parse_url($url), 'host', 'N/A'); 

作为一个附加的例子, 使用RealIP()函数进行testing:

 $ip = Value($_SERVER, 'HTTP_CLIENT_IP', Value($_SERVER, 'HTTP_X_FORWARDED_FOR', Value($_SERVER, 'REMOTE_ADDR'))); 

整洁,嗯? ;)

我在你身边 但是PHPdevise者犯了更多的错误。 没有定义任何价值阅读的自定义function,没有办法绕过它。

我使用这些function

 function load(&$var) { return isset($var) ? $var : null; } function POST($var) { return isset($_POST[$var]) ? $_POST[$var] : null; } 

例子

 $y = load($x); // null, no notice // this attitude is both readable and comfortable if($login=POST("login")) // really =, not == if($pass=POST("pass")) if($login=="Admin" && $pass==...) { // login and pass are not empty, login is "Admin" and pass is ... $authorized = true; ... } 

做一个函数,如果没有设置,返回false ,如果指定,则返回false 。 如果有效,则返回variables。 您可以添加更多的选项,如下面的代码所示:

 <?php function isset_globals($method, $name, $option = "") { if (isset($method[$name])) { // Check if such a variable if ($option === "empty" && empty($method[$name])) { return false; } // Check if empty if ($option === "stringLength" && strlen($method[$name])) { return strlen($method[$name]); } // Check length of string -- used when checking length of textareas return ($method[$name]); } else { return false; } } if (!isset_globals("$_post", "input_name", "empty")) { echo "invalid"; } else { /* You are safe to access the variable without worrying about errors! */ echo "you uploaded: " . $_POST["input_name"]; } ?> 

我不确定你的可读性定义是什么,但是正确使用empty(),isset()和try / throw / catch块对于整个过程是非常重要的。 如果您的E_NOTICE来自$ _GET或$ _POST,那么应该对它们进行空(right)检查以及数据应该通过的所有其他安全检查。 如果它来自外部提要或库,则应该将其封装在try / catch中。 如果它来自数据库,则应检查$ db_num_rows()或它的等价物。 如果它来自内部variables,则应正确初始化。 通常,这些types的通知来自将一个新variables赋值给返回一个在失败时返回FALSE的函数的函数,这些函数应该包装在一个testing中,如果失败,可以将该variables分配给一个可接受的默认值代码可以处理,或抛出代码可以处理的exception。 这些东西使代码更长,添加额外的块,并添加额外的testing,但我不同意你的看法,我认为他们肯定会增加额外的价值。

软件不会神奇的运行,如果你正在期待一些缺失的东西,你需要正确处理它。 如果你忽略它,你可能在你的应用程序中创build安全漏洞。 在静态语言访问一个非定义的variables是不可能的,它不会简单地编译或崩溃你的应用程序,如果它为空。 进一步使您的应用程序无法维护,而当意想不到的事情发生时,您将发疯。 语言的严格性是必须的,而PHP在devise上在很多方面都是错误的。 如果你不知道,它会使你成为一个糟糕的程序员。

那么使用@操作符怎么样? 例如:

 if(@$foo) { /* do something */ } 

你可能会说这是不好的,因为你不能控制$ foo里面发生了什么(如果它是一个函数调用,例如包含一个PHP错误),但是如果你只使用这个技术的variables,这相当于:

 if(isset($foo) && $foo) { /* ... */ }