测试PHP中变量是否存在的最佳方法;isset()已明显损坏


187

isset()文档

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的答案array_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(),根据其文档,类属性的工作方式与之类似array_key_exists(),因为它可以正确地区分未设置和被设置为NULL。)


您无法检查-但是为什么需要?
过多的php

12
NULL在PHP中具有非常特殊的含义,它与是否设置变量是一个完全独立的概念。
chazomaticus

33
原因无效和不存在的区分。例如,您要建立一个对象来表示数据库表中的一行。对于该行中的每一列,您都创建一个私有变量,该私有变量只能通过对象的getter方法访问。假设列值为空。现在,该getter方法如何知道表中是否没有这样的列,或者该对象那里是否只有空值?幸运的是,就我而言,私有变量实际上是私有数组中的一个条目,因此我可以使用array_key_exists,但这是一个真正的问题。
内森·朗

1
是的,它已从新版本的PHP中删除。不幸的是,并非所有的PHP部署都支持它。同样,似乎无意义的语义细节也使我们争论是在谈论数组元素还是变量。无论您认为代码应遵循什么标准,了解如何解决PHP语言的不一致都是很有用的。
chazomaticus

2
@chazomaticus但是变量和数组元素本质上是不同的 ; 仅仅因为您可以对它们执行某些相同的操作,并不意味着它们可以或应该100%可互换。这里没有“ PHP语言中的不一致”,只是您不会偶然喜欢/理解的某些东西。至于register_globals,我仍在努力思考一种情况,即使这种情况也需要这样的区别,因为从HTTP请求中注册的任何内容始终都是字符串,而不是null
IMSoP 2013年

Answers:


97

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

array_key_exists('v', $GLOBALS) 

3
啊哈!现在你在说话!例如,对于类属性,您将如何处理?
chazomaticus

22
作为一种变体,如果检查也需要对局部范围变量起作用,则可以执行$defined_vars = get_defined_vars();,然后通过进行测试array_key_exists('v', $defined_vars);
Henrik Opel 2010年

1
对我来说,这看起来有些丑陋,但是在您实际上正在检查数组元素的情况下,它就更有意义了:isset($foo[$bar])变成了array_key_exists($bar, $foo)
2014年

property_exists除此之外,似乎很有希望:> property_exists()函数无法检测使用__get magic方法可以魔术访问的属性。
alexw

@alexw通过__get“创建”的变量确实不存在。__get是用作不存在变量的后备的任意代码,无论是否存储了任何相关数据,它都可以返回所需的内容。
Brilliand

46

尝试概述各种讨论和答案:

这个问题没有一个单一的答案可以替代所有isset可以使用的方式。有些用例是由其他功能解决的,而另一些则不能经受审查,或者具有超出代码标准的可疑价值。其他用例远没有被“破坏”或“不一致”,而是证明了为什么isset的反应null而是是逻辑行为。

实际用例(包含解决方案)

1.阵列键

数组可以像对待的变量集合,与unsetisset对待他们,好像他们是。但是,由于可以对它们进行迭代,计数等,因此缺失值与值为null

在这种情况下,答案是使用array_key_exists()代替isset()

由于这需要将数组作为函数参数进行检查,因此如果数组本身不存在,PHP仍然会发出“ notices”。在某些情况下,可以有效地辩称每个维度都应该首先初始化,因此该通知正在起作用。在其他情况下,“递归” array_key_exists函数会依次检查数组的每个维度,从而避免了这种情况,但基本上与相同@array_key_exists。它也与null值的处理有关。

2.对象属性

在传统的“面向对象程序设计”理论中,封装和多态性是对象的关键属性。在基于类的OOP实现像PHP的,封装的属性声明为类定义的一部分,并给予访问级别(publicprotected,或private)。

但是,PHP还允许您动态地向对象添加属性,就像对数组的键一样,有些人stdClass在类似的方法中使用无类对象(从技术上讲,是内置实例,没有方法或私有功能)。关联数组的方式。这导致函数可能想知道某个特定属性是否已添加到提供给它的对象的情况。

与数组键一样,该语言中也包含一种用于检查对象属性的解决方案,这种语言称为property_exists

不合理的用例,并进行讨论

3. register_globals和其他污染全局名称空间

register_globals功能将变量添加到了全局范围,该变量的名称由HTTP请求的各个方面(GET和POST参数以及cookie)确定。这可能导致错误的代码和不安全的代码,这就是为什么自2000年8月发布的PHP 4.2开始默认禁用它于2012年3月发布的PHP 5.4中将其完全删除的原因。但是,可能某些系统仍在启用或仿真此功能的情况下运行。也可以使用global关键字或$GLOBALS数组以其他方式“污染”全局名称空间。

首先,由于GET,POST和cookie值将始终为字符串(仍从返回),因此register_globals其自身不太可能意外地产生null变量,并且会话中的变量应完全在程序员的控制之下。''trueisset

其次,如果变量null覆盖了先前的某些初始化,则仅会污染该值。null仅当其他地方的代码在两种状态之间进行区分时,“覆盖”未初始化的变量才是有问题的,因此,就其本身而言,这种可能性是反对进行这种区分的理由。

4. get_defined_varscompact

PHP中很少使用的一些函数(例如get_defined_varscompact)允许您将变量名视为数组中的键。对于全局变量,超全局数组$GLOBALS允许类似的访问,并且更为常见。如果未在相关范围内定义变量,则这些访问方法的行为将有所不同。

一旦决定使用这些机制之一将一组变量视为数组,就可以对它进行所有操作,就如同对任何普通数组一样。因此,请参见1。

仅用于预测这些函数将如何运行的功能(例如“在返回的数组中是否存在键'foo' get_defined_vars?”)是多余的,因为您可以简单地运行该函数并找出没有不良影响的函数。

4a。变量($$foo

尽管与将一组变量转换为关联数组的函数不太相同,但是大多数情况下使用“变量变量”(“分配给基于该另一个变量命名的变量”)可以并且应该改为使用关联数组。

从根本上说,变量名是程序员赋予值的标签。如果要在运行时确定它,它实际上不是标签,而是某些键值存储中的键。实际上,通过不使用数组,您将失去计数,迭代等功能。在键值存储区的“外部”有一个变量也变得不可能,因为它可能被覆盖$$foo

一旦更改为使用关联数组,该代码将适用于解决方案1。$foo->$property_name可以使用解决方案2 解决间接对象属性访问(例如)。

5. isset比打字容易得多array_key_exists

我不确定这是否真的相关,但是是的,PHP的函数名称有时会很冗长且不一致。显然,史前版本的PHP使用函数名的长度作为哈希键,因此Rasmus故意像htmlspecialchars这样编造函数名,以使它们具有不寻常的字符数。

不过,至少我们不写Java,是吗?;)

6.未初始化的变量具有类型

关于变量基础手册页包含以下语句:

未初始化变量的类型默认值取决于使用它们的上下文

我不确定Zend引擎中是否有“未初始化但已知类型”的概念,或者这是否在该语句中读得太多。

显而易见的是,这对它们的行为没有实际的影响,因为该页面上针对未初始化变量描述的行为与值为的变量的行为相同null。举一个例子,在代码中,$a和都$b将以整数结尾42

unset($a);
$a += 42;

$b = null;
$b += 42;

(第一个将引起有关未声明变量的通知,以尝试使您编写更好的代码,但对代码的实际运行方式没有任何影响。)

99.检测功能是否已运行

(最后一个保留下来,因为它比其他的要长得多。也许以后再编辑它……)

考虑以下代码:

$test_value = 'hello';
foreach ( $list_of_things as $thing ) {
    if ( some_test($thing, $test_value) ) {
        $result = some_function($thing);
    }
}
if ( isset($result) ) {
    echo 'The test passed at least once!';
}

如果some_function能返回null,有一种可能性,即echo不会甚至达到了,虽然some_test返回true。程序员的意图是检测$result从未设置的时间,但是PHP不允许他们这样做。

但是,此方法还有其他问题,如果添加外循环,这些问题将变得很明显:

foreach ( $list_of_tests as $test_value ) {
    // something's missing here...
    foreach ( $list_of_things as $thing ) {
        if ( some_test($thing, $test_value) ) {
            $result = some_function($thing);
        }
    }
    if ( isset($result) ) {
        echo 'The test passed at least once!';
    }
}

由于$result从未显式初始化,因此在第一个测试通过时它将采用一个值,从而无法确定后续测试是否通过。当变量未正确初始化时,这实际上是一个极为常见的错误。

要解决此问题,我们需要在我评论有遗漏的地方做一些事情。最明显的解决方案是将其设置$resultsome_function永远不会返回的“最终值” 。如果是null,则其余代码将正常工作。如果没有自然的候选值作为终值,因为some_function它的返回类型极其无法预测(本身可能是一个不好的信号),则可以使用其他布尔值(例如)$found代替。

思想实验一:very_null常数

从理论上讲,PHP可以提供一个特殊的常量-以及null-用作此处的终端值。假定从函数返回此值是非法的,或者将其强制转换为null,并且将其作为函数参数传递可能也是如此。这会使这个非常特殊的情况稍微简单一些,但是一旦您决定重新构造代码(例如,将内部循环放入一个单独的函数中),它将变得毫无用处。如果常量可以在函数之间传递,则不能保证some_function不会返回该常量,因此它将不再用作通用终端值。

在这种情况下,用于检测未初始化变量的参数归结为该特殊常数的参数:如果将替换为unset($result),并且与区别对待,则将$result = null引入一个“值”,因为$result该值不能被传递,只能被由特定的内置功能检测。

思想实验二:作业计数器

关于最后一个if问题的另一种思考方式是“是否有任务分配给$result?” 与其将其视为的特殊值,不如将其$result视为关于变量的“元数据” ,有点像Perl的“变量污点”。因此,而不是isset你可以叫它has_been_assigned_to,而非unsetreset_assignment_state

但是,如果是这样,为什么要停在布尔值上呢?如果您想知道测试通过了多少次怎么办?您可以简单地将元数据扩展为整数并具有get_assignment_countand reset_assignment_count...

显然,添加这样的功能将在语言的复杂性和性能之间进行权衡,因此需要仔细权衡其预期的有用性。与very_null常数一样,它仅在非常狭窄的情况下才有用,并且同样会抵抗重构。

希望明确的问题是,为什么PHP运行时引擎应该事先假设您想跟踪这些事情,而不是让您使用常规代码来明确地做到这一点。


关于类和属性,可悲的是,当属性为数组时,property_exists()不起作用,例如:Class {public $ property = array()}。引发错误。
安德鲁(Andrew)

1
@Andrew似乎对我来说还不错:3v4l.org/TnAY5想要提供完整的示例吗?
IMSoP 2015年

是的,似乎工作正常,我的设置有问题。抱歉,您的误报警是:)
Andrew

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


也许超出了这个问题的范围,但是您可能想要添加"0"到表格中,以确保empty操作的完整性和清晰度
Rik Schaaf

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()。

顺便说一句,我个人会避免像瘟疫这样的情况,即不存在的变量和具有空值的变量之间存在语义差异。PHP和大多数其他语言只是认为不存在。


3
PHP没有,但是我不会说大多数其他语言都没有。如果尚未声明变量,则大多数声明变量的语言都将引发错误,但是您可以将其设置为NULL。语义上NULL应表示“没有资源”,但未定义变量是程序员错误。
米勒

1
@MMiller当然可以,但是在“无资源”的情况下编写遵循一条路径的代码,而在“程序员错误”的情况下编写遵循另一条路径的代码是很荒谬的。如果要在调试期间检测未声明的变量,请使用静态分析工具,就像查找任何语言的潜在错误一样。
IMSoP 2014年

@MMiller,酷,你怎么想到这个。
Pacerier,2015年

1
@MMiller但是它不能作为反驳,因为答案中的语句明确地涉及“变量不存在”,而您的反例涉及对象属性/哈希键不存在。这些情况之间的区别不仅仅是偶然的。
IMSoP 2015年

1
@MMiller-的确是一个更好的例子。然而,经过20多年的严格语言编程,在这里我需要区分的情况undefinednull是如此罕见,我不会错过它。恕我直言,主要用途undefined是“非严格语言中的程序员错误”。用严格的语言来说,如果我需要一个不同的状态client did not state a value,那么我将声明一个适合于这种情况的值,并对其进行测试。最坏的情况是,必须添加一个单独的标志变量。但是这样做很少总要应付两个不同的非价值状态要好!
ToolmakerSteve

15

逻辑解释NULL

我想所有这些的明显答案是...不要将变量初始化为NULL,将它们初始化为与要成为的变量相关的东西。

正确对待NULL

NULL应该被视为“不存在的值”,这是NULL的含义。该变量不能归类为PHP的现有变量,因为尚未告知它要成为哪种类型的实体。它可能还不存在,所以PHP只是说:“好吧,那不是因为没有意义,NULL是我这样说的方式”。

争论

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

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

NULL永远不会回答任何问题,它不会告诉我们是或否,也不会告诉我们时间,也不会告诉我们提交了空白字符串。这是理解NULL的基本逻辑。

摘要

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

随意引用这个。它超出了我的逻辑头脑:)


5
好答案。如此多次,我看到人们对他们讨厌语言的这种或那种特性感到愤慨。但是他们似乎以为“如果不按我的方式做,那就坏了。” 是的,有错误的设计决策。但是也有非常有心的开发者!
curtisdf 2012年

23
未设置的变量和variable === null之间存在巨大差异。一个不存在,另一个的值为null。null表示无值的说法就是不正确的。Null是类型为null的VALUE。它是完全有效的值,并且没有理由让php将其视为不存在的值,可悲的是。如果不存在的变量为null,并且每个现有变量都不为null,那么将null赋给变量将取消它的设置,那就可以了。但是在很多情况下,函数返回null作为实际值。然后我们被搞砸了,因为没有流血的方法来测试它。
2013年

2
我知道我们“不应该”检查php中变量的存在,地狱,甚至没有任何真正的方法来检查它。我不会编写依赖于它的代码,因为在php中是不可能的。那是php的局限性。非设置变量和null变量之间显然有区别,但是php没有提供区分这些变量的方法。然而,许多元功能在内部依赖于它:读取不存在的var会产生通知,isset($a['x'])如果x为null 则会告诉您false ,但是它将显示在count($a).. compact它将对所有设置的变量都起作用,包括nulls,等等。
enrey 2013年

3
这个答案在一个主要方面存在缺陷:在OO编程中,null是表示“无对象”的逻辑选择。例如,在异常情况下,当一个函数可以返回一个对象或没有对象时,null是显而易见的选择。从技术上讲,在PHP中,可以使用false或在布尔上下文中被认为是false的任何其他值,但是这样会使您失去一些语义上的纯度。因此,空是一个完全合理的值初始化一个变量最终应持有的对象,因为它有关什么它打算成为。
chazomaticus

3
只要PHP为未定义的变量引发错误,而不是为null引发错误,则存在差异。如果null和undefined确实是同一概念,则PHP应该将默认的undefined / undeclared变量假定为null,并且永远不会抛出错误,但是没人会这样,因为这是开发的噩梦。在值语义的上下文中,null和undefined可能并没有真正的不同,但是在编写清晰且可调试的代码时,它们却有很大不同。
克里斯·米德尔顿

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");
    }
}

4

除了greatbigmassive讨论NULL的含义之外,请考虑“变量的存在”的实际含义。

在许多语言中,必须在使用每个变量之前显式声明每个变量。这可以确定其类型,但更重要的是,它声明了它的作用域。变量在其作用域中的任何地方“存在”,而在其范围之外的任何地方都存在-是整个函数还是单个“块”。

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

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

但是,如果您使用的变量没有先“初始化”,自动代码质量检测器通常会向您发出警告。首先,这有助于检测错别字,例如分配给;$thingId但要从中读取$thing_id;但是其次,它迫使您考虑该变量含义的范围,就像声明一样。

任何关心变量是否“存在”的代码都属于该变量范围的一部分 -不管它是否已初始化,作为程序员,您都已在代码的那个位置给出了标签的含义。由于您正在使用它,因此它在某种意义上必须“存在”,并且如果存在,则它必须具有隐式值;在PHP中,该隐式值为null

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

事实证明,PHP有一种更好的表示键值存储的方式,称为关联数组。尽管可以将数组的值视为变量,但是您也可以整体上对数组执行操作。如果您有关联数组,可以使用来测试它是否包含键array_key_exists()

您还可以以类似的方式使用对象,动态设置属性,在这种情况下,可以property_exists()以完全相同的方式使用。当然,如果你定义一个类时,你可以声明其性能已经 -你甚至可以选择publicprivateprotected范围。

虽然有一个 尚未初始化(或已明确声明unset())的变量(相对于数组键或对象属性)与值为的变量之间技术差异null,但是任何将其视为有意义的代码使用变量的方式不被使用。


1
非常好的观点,尽管不能完全回答问题。
chazomaticus

1
对于明确的问题“我们应该如何可靠地检查PHP中变量的存在?” 我的回答是“你不是,这就是原因”。这个答案和greatbigmassive的答案也都回答了隐式问题“为什么会isset()那样行?”。
IMSoP 2013年

“如果从当前没有条目的变量中读取,PHP会认为该变量的值为NULL。” 这是错误的。未定义的变量就是未定义的。当您尝试访问它时,它可能返回null,但这是不相关的。
雨果·辛克

@HugoZink与什么无关?对未定义变量的进行的任何测试都会告诉您该值为null。对于您而言,在您看到它之前是否存在该值是哲学家的一个问题,但是就任何可观察到的行为而言,该值是一致的null
IMSoP

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

2

我不同意您对NULL的推理,并且说您需要更改有关NULL的思维方式只是很奇怪。

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

如果您正在检查从数据库返回的值,并且其中一列具有NULL值,那该怎么办,即使该值为NULL,您仍然想知道它是否存在...在这里不信任isset()。

同样

$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-它只是愚蠢的设计


您的示例不是检查变量是否存在,而是检查数组键。存在一个解决方案,形式为array_key_exists。永远不要在运行时不知道是否存在实际变量的情况下。
IMSoP 2013年

@chazomaticus好吧,您永远不要处于register_globals处于打开状态的情况,因此我支持该声明。
IMSoP 2013年

哦,我同意。尽管如此,并不是每个人都可以控制其代码的部署位置。无论情况是“应该”是什么,拥有针对每种情况的信息都是很有用的。
chazomaticus

@chazomaticus如果您的问题是register_globals,那您的答案就不会变成isset()PHP手册提到“通常首先初始化变量通常是一种好的编程习惯”,它可以register_globals在设计时而不是运行时解决。还有一个FAQ条目,提供了unregister_globals()在运行时处理它的功能。
IMSoP 2013年

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显然具有区分空变量和未定义变量的内部能力。在我看来,应该有一个内置函数来检查这一点。

我认为可接受的答案在大多数情况下是好的,但是如果我要实现它,我将为此编写一个包装器。如之前在此答案中提到的,我必须同意,我实际上没有遇到过出现问题的情况。我似乎几乎总是会遇到这样一种情况:我的变量要么被设置和定义,要么没有(未定义,未设置,空,空白等)。并不是说将来不会发生这种情况,但是由于这似乎是一个非常独特的问题,我对PHP开发人员没有费心将其放入也不感到惊讶。


对未定义变量的警告向程序员暗示它们在代码中做错了什么。在外部调试中(对于语言而言,该工具具有语言以外的工具),永远不需要程序检测这种状态,因为程序员应该始终知道他们声明的变量。
IMSoP

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) ) { /* ... */ }如果需要,可以确定该变量是否存在,即使它为null。

完整的isset文档所读内容不只是最初粘贴的内容。是的,它为先前设置但现在为空的变量返回false,但如果尚未设置变量(曾经)以及已标记为未设置的任何变量,则返回false。还应注意,NULL字节(“ \ 0”)不会被视为null,并将返回true。

确定是否设置了变量。

如果使用unset()取消设置了变量,则将不再设置该变量。如果测试已设置为NULL的变量,则isset()将返回FALSE。另请注意,NULL字节(\ 0)不等于PHP NULL常量。


他取消了该链接的文档。阅读您提供的链接上的描述部分的第一句话,第二段。这正是他在上面引用的内容。
Zoredache

对于简单的脚本来说,这不是一个坏习惯,但是在复杂的(例如大型OO)项目中,它变得不可行。而且,正如我上面所说的,is_null()对于未设置的变量返回TRUE,因此,除了避免PHP警告外,实际上没有理由按照您的意思进行操作。
chazomaticus

1
对于设计良好的“大型OO”项目,为什么这根本是个问题?为什么在方法主体中有$ foo可能在首次使用之前没有设置过?
Beau Simensen 09年

1

尝试使用

unset($v)

似乎唯一未设置变量的时间是专门将其取消设置($ v)的时间。听起来您对“存在”的含义不同于PHP的定义。NULL当然是存在的,它是NULL。


我不确定你是什么意思。如果您有一个带有一个元素'a'的数组,则不必为元素'b'在PHP中不存在而取消set()元素'b'的存在,只是它不存在。例如全局变量,您可以将其视为$ GLOBALS数组的元素。
chazomaticus

1
但是我同意确实存在具有NULL值的变量。
chazomaticus

0

我不得不说,在我所有的PHP编程生涯中,我从未遇到过isset()在null变量上返回false 的问题。OTOH,我isset()在空数组条目失败时遇到了问题-但array_key_exists()在这种情况下可以正常工作。

为了进行比较,Icon明确将未使用的变量定义为返回,&null因此您可以在Icon中使用is-null测试来检查未设置的变量。这确实使事情变得容易。另一方面,Visual BASIC对于一个没有值的变量具有多个状态(Null,Empty,Nothing等),并且您经常必须检查多个状态。众所周知,这是错误的来源。


0

根据PHP手册的empty()函数,“确定变量是否被认为是空的。如果变量不存在或变量的值等于FALSE,则将变量视为空。变量不存在。” (我的重点是。)按标题Question所述,意味着empty()函数应被视为“测试PHP中变量是否存在的最佳方法”。

但是,这还不够好,因为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)的调用放入函数内部,然后将变量简单地传递给函数,但这是行不通的。即使您尝试将变量的“按引用传递”功能使用,变量也将定义,然后该功能将永远无法检测到之前未定义的变量。这里介绍的是我想做什么与实际可行之间的折衷。

前面的内容暗示着还有另一种方法可以始终避免遇到“未定义的变量”错误消息。(这里的假设是,防止出现此类消息是为什么要测试以查看变量是否未定义的原因。)

   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初始化为默认值“无错误”。定义了“处理程序功能”。如果$ testvar(我们想知道是否未定义的变量)通过了初步的empty()函数测试,那么我们将进行更彻底的测试。我们调用set_error_handler()函数来使用先前定义的处理函数。然后,我们进行涉及$ testvar的简单身份比较,如果未定义,将触发错误。处理函数捕获错误,并专门进行测试以查看错误原因是否是未定义变量的事实。结果放置在错误信息变量$ er中,由于可以确定是否定义了$ testvar,因此我们稍后可以对其进行测试以执行所需的任何操作。因为我们仅需要处理程序函数用于此有限的目的,所以我们恢复了原始的错误处理函数。“ myHndlr”函数只需要声明一次;可以将其他代码复制到$ testvar或我们要以此方式测试的任何其他变量的适当位置。


1
如果要避免发出尚未声明变量的警告,那么解决方案是修复代码以正确声明它们。您的inst函数基本上就像@错误抑制运算符一样:“我知道我在这里做错了,但是我只是希望该消息消失,而无需以任何方式实际更改代码的操作”。
IMSoP 2014年

另一方面,检测方法很巧妙,但是我仍然坚信,除了回显它们正在捕获的非常警告的信息之外,您永远不要对它们有任何用处。(您可能应该澄清您的输出缓冲版本要求将error_reporting设置为高,并打开display_errors。)
IMSoP 2014年

0

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

error_reporting(E_ALL); // Enables E_NOTICE

但是您将必须修复所有由未定义变量,常量,数组键,类属性等产生的通知。完成此操作后,您将不必担心null和未声明的变量之间的区别,并且不再出现歧义。

启用 通知报告可能并非在所有情况下都是不错的选择,但是有充分的理由启用它:

为什么要修复E_NOTICE错误?

在我的情况下,在没有它的proyect中工作了一年多,但是习惯于声明变量时要小心,因此过渡很快。



-1

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

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

2
empty()不检查变量是否为null,而是检查它是否为false-y,例如""(空字符串),0(0作为整数),0.0(0作为浮点数),"0"(0作为字符串)NULLFALSEarray()(一个空数组)和$var;(一个声明的变量,但没有值)。假设您有一个必填的无线电字段,格式为两个输入,其值分别为01。如果您empty()用于验证,并且用户选择了0一个,则您会无意中出错,“必填字段不能为空”。请参阅手册php.net/manual/en/function.empty.php
哈利勒Özgür的
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.