在PHP中测试变量存在的最佳方法; isset()显然已被打破

时间:2009-01-06 20:46:51

标签: php variables isset

来自isset() docs

isset() will return FALSE if testing a variable that has been set to NULL.

基本上,isset()不检查变量是否设置,但是否设置为NULL以外的任何变量。

鉴于此,实际检查变量是否存在的最佳方法是什么?我试过像:

if(isset($v) || @is_null($v))

@是必要的,以避免在未设置$v时出现警告但is_null()isset()存在类似问题:它返回TRUE关于未设定的变量!它似乎也是:

@($v === NULL)

的工作原理与@is_null($v)完全相同,所以也是如此。

我们如何在PHP中可靠地检查变量是否存在?


编辑:PHP中未设置的变量与设置为NULL的变量之间明显存在差异:

<?php
$a = array('b' => NULL);
var_dump($a);

PHP显示$a['b']存在,且值NULL。如果你添加:

var_dump(isset($a['b']));
var_dump(isset($a['c']));

你可以看到我用isset()函数谈论的歧义。以下是这三个var_dump()s的输出结果:

array(1) {
  ["b"]=>
  NULL
}
bool(false)
bool(false)

进一步编辑:两件事。

一,用例。将数组转换为SQL UPDATE语句的数据,其中数组的键是表的列,数组的值是要应用于每列的值。任何表的列都可以保存NULL值,通过在数组中传递NULL值来表示。您需要一种区分不存在的数组键,并将数组的值设置为NULL的方法;这是不更新列的值和将列的值更新为NULL之间的区别。

其次,对于我的上述用例和任何全局变量,Zoredache's answerarray_key_exists()正常工作:

<?php
$a = NULL;
var_dump(array_key_exists('a', $GLOBALS));
var_dump(array_key_exists('b', $GLOBALS));

输出:

bool(true)
bool(false)

由于几乎可以在任何地方正确处理,我可以看到在不存在的变量与设置为NULL的变量之间存在任何歧义,我正在调用array_key_exists()官方PHP中最简单的方法是真正检查变量的存在

(只有我可以想到的其他情况是类属性,其中有property_exists(),根据its docs,它与array_key_exists()的工作方式类似,因为它可以正确区分正在设置并设置为NULL。)

16 个答案:

答案 0 :(得分:95)

如果要检查的变量位于全局范围内,则可以执行以下操作:

array_key_exists('v', $GLOBALS) 

答案 1 :(得分:20)

有时我会在尝试找出在给定情况下使用哪种比较操作时有点失落。 isset()仅适用于未初始化或显式空值。传递/分配null是确保逻辑比较按预期工作的好方法。

但是,考虑到这一点有点困难,所以这里有一个简单的矩阵,比较不同操作如何评估不同的值:

|           | ===null | is_null | isset | empty | if/else | ternary | count>0 |
| -----     | -----   | -----   | ----- | ----- | -----   | -----   | -----   |
| $a;       | true    | true    |       | true  |         |         |         |
| null      | true    | true    |       | true  |         |         |         |
| []        |         |         | true  | true  |         |         |         |
| 0         |         |         | true  | true  |         |         | true    |
| ""        |         |         | true  | true  |         |         | true    |
| 1         |         |         | true  |       | true    | true    | true    |
| -1        |         |         | true  |       | true    | true    | true    |
| " "       |         |         | true  |       | true    | true    | true    |
| "str"     |         |         | true  |       | true    | true    | true    |
| [0,1]     |         |         | true  |       | true    | true    | true    |
| new Class |         |         | true  |       | true    | true    | true    |

为了适应表格,我稍微压缩了标签:

  • $a;引用已声明但未分配的变量
  • 第一列中的所有其他内容都指向指定的值,例如:
    • $a = null;
    • $a = [];
    • $a = 0;
    • ...
  • 列指比较操作,如:
    • $a === null
    • isset($a)
    • empty($a)
    • $a ? true : false
    • ...

所有结果都是布尔值,打印true并省略false

你可以自己运行测试,检查这个要点:
https://gist.github.com/mfdj/8165967

答案 2 :(得分:17)

您可以使用紧凑语言构造来测试是否存在null变量。不存在的变量不会在结果中出现,而空值将显示。

$x = null;
$y = 'y';

$r = compact('x', 'y', 'z');
print_r($r);

// Output:
// Array ( 
//  [x] => 
//  [y] => y 
// ) 

就你的例子而言:

if (compact('v')) {
   // True if $v exists, even when null. 
   // False on var $v; without assignment and when $v does not exist.
}

当然,对于全局范围内的变量,您也可以使用array_key_exists()。

B.t.w。我个人会避免像瘟疫这样的情况,即不存在的变量和具有空值的变量之间存在语义差异。 PHP和大多数其他语言都不认为有。

答案 3 :(得分:15)

解释NULL,逻辑思考

我猜这一切的明显答案是...... 不要将变量初始化为NULL,将它们作为与其预期相关的内容进行初始化。

正确处理NULL

NULL应被视为“非存在值”,这是NULL的含义。 该变量不能被归类为PHP的现有变量,因为它还没有被告知它试图成为什么类型的实体。它可能也不存在,所以PHP只是说“好吧,它不是因为无论如何都没有意义,而NULL是我这样说的方式”。

参数

我们现在争论吧。 “但是NULL就像说0或FALSE或''。

错误,0-FALSE-''仍被归类为空值,但它们被指定为某种类型的值或问题的预定答案。 FALSE 是肯定或否定的答案,''是有人提交的标题的答案, 0 是数量或时间的答案它们被设置为某种类型的答案/结果,使它们在设置时有效。

NULL只是没有回答什么,它没有告诉我们是或否它并没有告诉我们时间它并没有告诉我们一个空白的字符串被提交。这是理解NULL的基本逻辑。

摘要

这不是为了解决问题而创建古怪的函数,它只是改变了你的大脑看待NULL的方式。如果它为NULL,则假设它未设置为任何值。如果您预先定义变量,则根据您打算使用的类型将它们预先定义为0,FALSE或“”。

随意引用此内容。 它不在我的逻辑头脑中:)

答案 4 :(得分:9)

可以通过property_exists

检查对象属性是否存在

单元测试示例:

function testPropertiesExist()
{
    $sl =& $this->system_log;
    $props = array('log_id',
                   'type',
                   'message',
                   'username',
                   'ip_address',
                   'date_added');

    foreach($props as $prop) {
        $this->assertTrue(property_exists($sl, $prop),
                           "Property <{$prop}> exists");
    }
}

答案 5 :(得分:4)

作为greatbigmassive's discussion of what NULL means的补充,请考虑“变量的存在”实际意味着什么。

在许多语言中,您必须在使用之前明确声明每个变量;这可能决定了它的类型,但更重要的是它声明了它的范围。变量“存在”在其范围内的任何地方,并且在它之外的任何地方 - 无论是整个函数还是单个“块”。

在其范围内,变量为您(程序员)选择的标签赋予一些含义。在其范围之外,该标签毫无意义(无论您在不同范围内使用相同的标签,基本上都无关紧要)。

在PHP中,变量不需要声明 - 它们会在您需要时立即生效。当您第一次写入变量时,PHP会在内存中为该变量分配一个条目。如果从当前没有条目的变量中读取,PHP会认为该变量的值为NULL

但是,如果您使用变量而不首先“初始化”它,自动代码质量检测器通常会发出警告。首先,这有助于检测拼写错误,例如分配到$thingId但是从$thing_id读取;但其次,它迫使你考虑变量具有意义的范围,就像声明一样。

任何关心变量“是否存在”的代码都是该变量范围的一部分 - 无论它是否已经初始化,作为程序员你已经给出了该标签的含义代码。既然你正在使用它,它在某种意义上必须“存在”,如果存在,它必须具有隐含值;在PHP中,隐含值为null

由于PHP的工作方式,可以编写处理现有变量名称空间的代码,而不是作为您赋予其意义的标签范围,而是作为某种键值存储。例如,您可以运行如下代码:$var = $_GET['var_name']; $$var = $_GET['var_value'];仅仅因为你可以,并不意味着这是一个好主意。

事实证明,PHP有一种更好的方法来表示键值存储,称为关联数组。虽然数组的值可以像变量一样处理,但您也可以对数组执行整体操作。 如果您有关联数组,则可以使用array_key_exists()测试它是否包含密钥。

您也可以以类似的方式使用对象,动态设置属性,在这种情况下,您可以以完全相同的方式使用property_exists()。当然,如果您定义了一个类,您可以声明它具有哪些属性 - 您甚至可以在publicprivateprotected范围之间进行选择。

虽然尚未初始化的变量(或数组键或对象属性)之间存在技术差异(或已明确unset())一个值为null的人,任何认为差异有意义的的代码都是以一种他们不打算使用的方式使用变量。

答案 6 :(得分:3)

isset检查变量是否已设置,如果是,则是否为NULL。后一部分(在我看来)不属于本职能范围。没有合适的解决方法来确定变量是否为NULL ,因为它未设置,或者因为显式设置为NULL

以下是一种可能的解决方案:

$e1 = error_get_last();
$isNULL = is_null(@$x);
$e2 = error_get_last();
$isNOTSET = $e1 != $e2;
echo sprintf("isNOTSET: %d, isNULL: %d", $isNOTSET, $isNULL);

// Sample output:
// when $x is not set: isNOTSET: 1, isNULL: 1
// when $x = NULL:     isNOTSET: 0, isNULL: 1
// when $x = false:    isNOTSET: 0, isNULL: 0

其他解决方法是探测get_defined_vars()的输出:

$vars = get_defined_vars();
$isNOTSET = !array_key_exists("x", $vars);
$isNULL = $isNOTSET ? true : is_null($x);
echo sprintf("isNOTSET: %d, isNULL: %d", $isNOTSET, $isNULL);

// Sample output:
// when $x is not set: isNOTSET: 1, isNULL: 1
// when $x = NULL:     isNOTSET: 0, isNULL: 1
// when $x = false:    isNOTSET: 0, isNULL: 0

答案 7 :(得分:2)

我不同意你关于NULL的推理,并且说你需要改变你对NULL的看法很奇怪。

我认为isset()设计不正确,isset()应该告诉你变量是否已设置且不应该关注变量的实际值。

如果您正在检查从数据库返回的值并且其中一列具有NULL值,那么即使值为NULL,您仍然想知道它是否存在... nope dont trust isset()here。

同样

$a = array ('test' => 1, 'hello' => NULL);

var_dump(isset($a['test']));   // TRUE
var_dump(isset($a['foo']));    // FALSE
var_dump(isset($a['hello']));  // FALSE

isset()应该被设计成像这样工作:

if(isset($var) && $var===NULL){....

通过这种方式,我们让程序员检查类型,而不是将它留给isset()来假设它不存在,因为值为NULL - 它只是愚蠢的设计

答案 8 :(得分:2)

我要加快两分钱。这个问题令人困惑的一个原因是因为这个场景似乎返回相同的结果,错误报告

$a = null;
var_dump($a); // NULL
var_dump($b); // NULL

您可以从此结果中假设$a = null与未定义$b之间的区别完全没有。

报道曲柄错误:

NULL

Notice: Undefined variable: b in xxx on line n
NULL

注意:它抛出了一个未定义的变量错误,但var_dump的输出值仍为NULL

PHP显然确实具有区分空变量和未定义变量的内部能力。在我看来,应该有一个内置功能来检查这一点。

我认为接受的答案在大多数情况下是好的,但如果我要实现它,我会为它编写一个包装器。正如前面提到的in this answer,我必须同意我实际上并没有遇到过这个问题的情况。我似乎几乎总是在我的变量被设置和定义的情况下结束,或者它们不是(未定义,未设置,空,空白等)。并不是说这种情况在未来不会发生,但由于这似乎是一个非常独特的问题,我并不感到惊讶,因为PHP开发人员并没有因此而烦恼。 / p>

答案 9 :(得分:1)

如果我运行以下内容:

echo '<?php echo $foo; ?>' | php

我收到错误:

PHP Notice:  Undefined variable: foo in /home/altern8/- on line 1

如果我运行以下内容:

echo '<?php if ( isset($foo) ) { echo $foo; } ?>' | php

我没有收到错误。

如果我有一个应该设置的变量,我通常会做类似以下的事情。

$foo = isset($foo) ? $foo : null;

if ( ! isset($foo) ) $foo = null;

这样,稍后在脚本中,我可以安全地使用$ foo并知道它“已设置”,并且它默认为null。稍后我可以if ( is_null($foo) ) { /* ... */ }如果我需要并且确定该变量存在,即使它是空的。

完整isset documentation读取的内容不仅仅是最初粘贴的内容。是的,对于先前设置但现在为null的变量,它返回false,但如果尚未设置变量(永远),则它也返回false,对于任何已标记为未设置的变量,它也返回false。它还指出NULL字节(“\ 0”)不被视为null并将返回true。

  

确定是否设置了变量。

     

如果未设置变量   unset(),它将不再被设置。   如果测试a,isset()将返回FALSE   已设置为NULL的变量。   另请注意,NULL字节(“\ 0”)是   不等于PHP NULL   恒定。

答案 10 :(得分:1)

尝试使用

unset($v)

似乎唯一没有设置变量的时候是特别取消设置($ v)。听起来“存在”的含义与PHP的定义不同。 NULL肯定存在,它是NULL。

答案 11 :(得分:0)

我必须说,在我多年的PHP编程中,我从未遇到过isset()在null变量上返回false的问题。 OTOH,我遇到了isset()在空数组条目上失败的问题 - 但array_key_exists()在这种情况下正常工作。

对于某些比较,Icon显式将未使用的变量定义为返回&null,因此您在Icon中使用is-null测试来检查未设置的变量。这确实使事情变得更容易。另一方面,Visual BASIC对于没有值(Null,Empty,Nothing,...)的变量有多个状态,并且您经常需要检查多个状态。众所周知这是一个bug的来源。

答案 12 :(得分:0)

根据用于empty()函数的PHP手册,&#34;确定变量是否被认为是空的。如果变量不存在或者其值等于FALSE,则该变量被视为空。如果变量不存在,empty()不会生成警告。&#34; (我的重点。)这意味着empty()函数应该符合标准问题中测试变量在PHP&#34;中存在的最好方法。

但是,这还不够好,因为empty()函数可能被一个确实存在并且设置为NULL的变量所欺骗。

我打断了我之前的回答,提出了更好的建议,因为它比我原来的答案(在此中断之后,用于比较)不那么麻烦。

  function undef($dnc) //do not care what we receive
  { $inf=ob_get_contents();             //get the content of the buffer
    ob_end_clean();                     //stop buffering outputs, and empty the buffer
    if($inf>"")                         //if test associated with the call to this function had an output
    { if(false!==strpos($inf, "Undef"); //if the word "Undefined" was part of the output
        return true;                    //tested variable is undefined
    }
    return false;                       //tested variable is not undefined
  }

两行简单的代码可以使用上面的函数来揭示变量是否未定义:

  ob_start();                           //pass all output messages (including errors) to a buffer
  if(undef($testvar===null))            //in this case the variable being tested is $testvar

您可以使用适当的内容跟随这两行,例如:

    echo("variable is undefined");
  else
    echo("variable exists, holding some value");

我想在函数内部调用ob_start()和($ testvar === null),只需将变量传递给函数,但它不起作用。即使您尝试使用&#34;通过引用&#34;函数的变量,变量BECOMES定义,然后函数永远不会检测到它以前是未定义的。这里介绍的是我想做什么和实际工作之间的妥协。

前面的含义表明还有另一种方法可以始终避免遇到未定义的变量&#34;错误信息。 (这里的假设是,阻止这样的消息是你想要测试以查看变量是否未定义的原因。)

   function inst(&$v) { return; }  //receive any variable passed by reference; instantiates the undefined

在对$ testvar做一些事情之前调用该函数:

   inst($testvar);                //The function doesn't affect any value of any already-existing variable

当然,新实例化的变量值设置为null!

(中断结束)

所以,经过一些研究和实验,这里有一些保证可以工作的东西:

 function myHndlr($en, $es, $ef, $el)
 { global $er;
   $er = (substr($es, 0, 18) == "Undefined variable");
   return;
 }

 $er = false;
 if(empty($testvar))
 { set_error_handler("myHndlr");
   ($testvar === null);
   restore_error_handler();
 }
 if($er)  // will be 1 (true) if the tested variable was not defined.
 { ; //do whatever you think is appropriate to the undefined variable
 }

解释:变量$ er初始化为默认值&#34;无错误&#34;。 A&#34;处理函数&#34;被定义为。如果$ testvar(我们想知道的变量是否未定义)通过初步的empty()函数测试,那么我们进行更彻底的测试。我们调用set_error_handler()函数来使用先前定义的处理函数。然后我们进行一个简单的身份比较,涉及$ testvar,如果未定义将导致错误。处理程序函数捕获错误并专门测试以查看错误的原因是否是未定义变量的事实。结果放在错误信息变量$ er中,我们可以稍后通过测试来做任何我们想要的结果,因为我们知道是否定义了$ testvar。因为我们只需要处理函数用于这个有限的目的,我们恢复原始的错误处理函数。 &#34; myHndlr&#34;函数只需要声明一次;其他代码可以复制到适合的地方,对于$ testvar或我们想要以这种方式测试的任何其他变量。

答案 13 :(得分:0)

我认为唯一完整的解决方案是报告通知

error_reporting(E_ALL); // Enables E_NOTICE

但是你必须修复由未定义变量,常量,数组键,类属性等生成的所有通知。完成后,您不必担心null和未声明的变量之间的区别,并且模糊性消失了。

在所有情况下启用通知报告可能不是一个好的选择,但有充分的理由启用它:

Why should I fix E_NOTICE errors?

在我的案例中,在没有它的情况下工作了一年多的时间,但过去常常要小心声明变量,因此过渡很快。

答案 14 :(得分:0)

知道变量是否在当前范围内定义($GLOBALS不值得信任)的唯一方法是array_key_exists( 'var_name', get_defined_vars() )

答案 15 :(得分:-1)

我更喜欢使用not empty作为检查a)存在的变量是否存在的最佳方法,而b)不是null。

if (!empty($variable)) do_something();