【问题标题】:Best way to test for a variable's existence in PHP; isset() is clearly broken在 PHP 中测试变量是否存在的最佳方法; isset() 明显坏了
【发布时间】:2010-09-29 21:42:24
【问题描述】:

来自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.)

【问题讨论】:

  • 您无法检查 - 但为什么需要?
  • NULL 在 PHP 中有一个非常特殊的含义,它与是否设置变量是完全不同的概念。
  • 个理由来区分 null 和不存在。例如,您正在构建一个对象来表示数据库表中的一行。对于行中的每一列,您创建一个私有变量,只能通过对象的 getter 方法访问。假设列值为空。现在那个 getter 方法是如何知道表中是否没有这样的列,或者这个对象是否只有一个空值呢?幸运的是,就我而言,私有变量实际上是私有数组中的一个条目,所以我可以使用 array_key_exists,但这是一个真正的问题。
  • 它已从新版本的 PHP 中删除,是的。不幸的是,它并没有从 PHP 的每个部署中消失。此外,争论我们是在谈论数组元素还是变量似乎是一个毫无意义的语义细节。无论您认为代码应该遵循什么标准,了解如何解决 PHP 语言中的不一致问题都会很有用。
  • @chazomaticus 但是变量和数组元素是根本不同的东西;仅仅因为你可以用它们做一些相同的事情并不意味着它们是或应该是 100% 可互换的。这里没有“PHP 语言的不一致”,只是您碰巧不喜欢/不理解的东西。至于register_globals,我仍然在努力思考一种情况,即使这样也需要这样的区别,因为从 HTTP 请求中注册的任何内容都将始终是一个字符串,而不是 null

标签: php variables isset


【解决方案1】:

如果您要检查的变量在全局范围内,您可以这样做:

array_key_exists('v', $GLOBALS) 

【讨论】:

  • 啊哈!现在你在说话!你会如何为类属性做这些?
  • 作为一种变体,如果检查也需要对局部范围变量起作用,则可以执行$defined_vars = get_defined_vars(); 然后通过array_key_exists('v', $defined_vars); 进行测试。
  • 这对我来说看起来有点难看,但如果你实际上是在检查一个数组元素,它就更有意义了:isset($foo[$bar]) 变为 array_key_exists($bar, $foo)
  • @alexw 通过 __get “创建”的变量确实不存在。 __get 是用作不存在变量的后备的任意代码,无论是否存储了任何相关数据,它都可以返回它想要的任何内容。
【解决方案2】:

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

这个问题没有单一的答案可以替代isset 的所有使用方式。 一些用例由其他功能解决,而另一些则经不起推敲,或者除了代码高尔夫之外,还有可疑的价值。远非“破碎”或“不一致”,其他用例说明了为什么issetnull 的反应是合乎逻辑的行为。

真实用例(带有解决方案)

1。数组键

可以将数组视为变量的集合,unsetisset 将它们视为变量的集合。但是,由于它们可以被迭代、计数等,缺失值与值为null 的值不同。

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

由于这是将要检查的数组作为函数参数,因此如果数组本身不存在,PHP 仍会发出“通知”。在某些情况下,可以有效地争论每个维度应该首先被初始化,所以通知正在完成它的工作。对于其他情况,依次检查数组的每个维度的“递归”array_key_exists 函数可以避免这种情况,但基本上与@array_key_exists 相同。它也与null 值的处理有些相切。

2。对象属性

在传统的“面向对象编程”理论中,封装和多态是对象的关键属性;在像 PHP 这样的基于类的 OOP 实现中,封装的属性被声明为类定义的一部分,并被赋予访问级别(publicprotectedprivate)。

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

与数组键一样,语言中包含一个检查对象属性的解决方案,称为property_exists

不合理的用例,有讨论

3。 register_globals,以及其他对全局命名空间的污染

register_globals 功能将变量添加到全局范围,其名称由 HTTP 请求的各个方面(GET 和 POST 参数以及 cookie)确定。这可能会导致错误和不安全的代码,这就是为什么它在PHP 4.2, released Aug 2000 之后默认被禁用并在PHP 5.4, released Mar 2012 中完全删除。但是,某些系统可能在启用或模拟此功能的情况下仍在运行。也可以使用global 关键字或$GLOBALS 数组以其他方式“污染”全局命名空间。

首先,register_globals 本身不太可能意外生成 null 变量,因为 GET、POST 和 cookie 值将始终是字符串('' 仍然从 isset 返回 true),并且会话中的变量应该完全在程序员的控制之下。

其次,只有当这覆盖了一些先前的初始化时,才会对值为 null 的变量造成污染。仅当其他地方的代码区分这两种状态时,使用null“覆盖”未初始化的变量才会有问题,因此这种可能性本身就是一个论点反对做出这样的区分。

4。 get_defined_varscompact

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

一旦您决定使用其中一种机制将一组变量视为一个数组,您就可以对它执行所有与对任何普通数组相同的操作。因此,请参见 1。

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

4a。变量变量($$foo

虽然与将一组变量转换为关联数组的函数不太一样,但大多数使用"variable variables"(“分配给基于此其他变量的变量”)的情况可以并且应该更改为使用关联代替数组。

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

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

5。 issetarray_key_exists 更容易输入

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

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

6。未初始化的变量有一个类型

manual page on variable basics 包含以下声明:

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

我不确定 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,则即使some_test 返回true,也可能无法到达echo。程序员的意图是检测 $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 从未显式初始化,所以它会在第一个测试通过时获取一个值,因此无法判断后续测试是否通过。 当变量未正确初始化时,这实际上是一个极其常见的错误。

要解决此问题,我们需要在我评论说缺少某些内容的那一行做一些事情。最明显的解决方案是将$result 设置为some_function 永远无法返回的“终端值”;如果这是null,那么其余代码将正常工作。如果由于some_function 具有极其不可预测的返回类型(这本身可能是一个不好的迹象)而没有自然的终端值候选者,那么一个额外的布尔值,例如$found,可以改用。

思想实验一:very_null常量

理论上,PHP 可以提供一个特殊的常量 - 以及 null - 在这里用作终端值;据推测,从函数返回 this 是非法的,或者它会被强制转换为null,同样的情况也可能适用于将它作为函数参数传入。这将使这个非常具体的情况稍微简单一些,但是一旦您决定重构代码 - 例如,将内部循环放入单独的函数中 - 它就会变得无用。如果常量可以在函数之间传递,则不能保证some_function 不会返回它,因此它不再用作通用终结值。

在这种情况下,用于检测未初始化变量的参数归结为该特殊常量的参数:如果您将注释替换为 unset($result),并将其处理方式与 $result = null 不同,则您正在为 @ 引入一个“值” 987654395@不能传,只能通过特定的内置函数检测到。

思想实验二:赋值计数器

另一种思考最后一个if 所问问题的方式是“有什么分配给$result 的吗?”与其将其视为 $result 的特殊值,不如将其视为变量的“元数据”关于,有点像 Perl 的“变量污染”。因此,您可以将其称为has_been_assigned_to,而不是isset,而不是unsetreset_assignment_state

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

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

希望显而易见的问题是,为什么 PHP 运行时引擎应该提前假设您想要跟踪这些事情,而不是让您使用普通代码明确地进行。

【讨论】:

  • 关于类和属性,遗憾的是 property_exists() 在属性为数组时不起作用,例如:Class{ public $property = array() }。引发错误。
  • @Andrew 对我来说似乎工作正常:3v4l.org/TnAY5 愿意提供一个完整的例子吗?
  • 是的,它似乎工作正常,我的设置有问题。抱歉误报:)
【解决方案3】:

有时我在试图找出在给定情况下使用哪个比较操作时会有点迷失。 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 操作的完整性和清晰度
【解决方案4】:

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

$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 和大多数其他语言只是认为没有。

【讨论】:

  • PHP 没有,但我不会说 大多数 其他语言没有。如果没有声明变量,大多数声明变量的语言都会抛出错误,但您可以将它们设置为NULL。从语义上讲,NULL 应该表示“没有资源”,但不定义变量是程序员错误。
  • @MMiller 当然,但是在“无资源”的情况下编写遵循一条路径而在“程序员错误”的情况下遵循不同路径的代码是相当荒谬的。如果您想在调试期间检测未声明的变量,请使用静态分析工具,就像在任何语言中查找潜在错误一样。
  • @MMiller,酷,你是怎么想到这个的。
  • @MMiller 但它不能作为反驳,因为答案中的陈述明确地是关于“一个不存在的变量”,而你的反例是关于 an object property /哈希键不存在。这些案例之间的区别不仅仅是偶然的。
  • @MMiller - 这确实是一个更好的例子。尽管如此,在使用严格的语言进行了 20 多年的编程之后,我需要区分 undefinednull 的情况非常罕见,我不会错过它。恕我直言,undefined 的主要用途是“非严格语言中的程序员错误”。用严格的语言来说,如果我需要 client did not state a value 的不同状态,那么我声明一个适合这种情况的值,并对其进行测试。最坏的情况,必须添加一个单独的标志变量。但这样做很少比必须总是应对两种不同的非价值状态更好!
【解决方案5】:

解释NULL,逻辑思考

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

正确对待NULL

NULL应该被视为“不存在的值”,这就是NULL的含义。 该变量不能被归类为 PHP 存在的变量,因为它没有被告知它试图成为什么类型的实体。它也可能不存在,所以 PHP 只是说“好吧,它不存在,因为它没有任何意义,而 NULL 是我的说法”。

论据

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

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

NULL 只是没有答案,它没有告诉我们是或否,它没有告诉我们时间,也没有告诉我们提交了一个空白字符串。这就是理解NULL的基本逻辑。

总结

这不是为了解决问题而创建古怪的函数,它只是改变你的大脑看待 NULL 的方式。如果它是 NULL,假设它没有被设置为任何东西。如果您要预定义变量,请根据您打算使用它们的类型将它们预定义为 0、FALSE 或 ""。

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

【讨论】:

  • 很好的答案。很多次我看到人们抱怨他们多么讨厌一种语言的这个或那个特性。但他们似乎在假设“如果它不按照我的方式做,那么它就坏了。”是的,有糟糕的设计决策。但也有非常封闭的开发者!
  • 未设置变量和变量===null 之间存在巨大差异。一个不存在,另一个的值为 null。 null 表示没有值的论点根本不正确。 Null 是 null 类型的值。这是完全有效的值,php 没有理由将其视为不存在的值,遗憾的是它确实如此。没关系,如果不存在的变量为 null 并且每个现有变量都不为 null 并且将 null 分配给变量会取消设置它。但是在很多情况下,函数返回 null 作为实际值。然后我们就搞砸了,因为没有血腥的方法来测试它。
  • 我知道我们“不应该”检查 php 中的变量是否存在,地狱,甚至没有任何真正的方法来检查它。我不会编写依赖于它的代码,因为这在 php.ini 中是不可能的。这是 php.ini 的限制。未设置变量和空变量之间显然存在差异,但是 php 没有提供区分它们的方法。然而,许多元功能在内部依赖于它:读取不存在的 var 会产生通知,isset($a['x']) 会告诉你如果x 为空,则它会显示在count($a)..compact 将适用于所有设置变量,包括nulls等。
  • 这个答案在一个主要方面存在缺陷:在 OO 编程中,null 是表示“无对象”的逻辑选择。例如,在函数可以返回对象或不返回对象的非异常情况下,null 是显而易见的选择。从技术上讲,在 PHP 中,可以使用 false 或在布尔上下文中被视为 false 的任何其他值,但是您会失去一些语义纯度。因此,null 是一个完全合理的值,可以将变量初始化为最终应该包含一个对象,因为它与它的预期目标相关。
  • 只要 PHP 为未定义的变量抛出错误,而不是为 null,那么就有区别。如果 null 和 undefined 真的是同一个概念,那么 PHP 应该将默认的未定义/未声明变量假定为 null 并且永远不会抛出错误,但没有人想要这样,因为这是一场开发噩梦。 Null 和 undefined 在值语义的上下文中可能并没有真正的不同,但在编写清晰且可调试的代码时它们是非常不同的。
【解决方案6】:

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

【讨论】:

    【解决方案7】:

    作为对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,任何认为该差异有意义的代码都在以不应该使用的方式使用变量。

    【讨论】:

    • 非常好的观点,虽然不完全是问题的答案。
    • 对于明确的问题“我们应该如何可靠地检查 PHP 中是否存在变量?”我的回答是“你不是,这就是为什么”。这个答案和greatbigmassive 都回答了implicit 问题“为什么isset() 会这样?”。
    • “如果你从一个当前没有条目的变量中读取,PHP 认为该变量的值为 NULL。”这是错误的。未定义的变量只是未定义的。当您尝试访问它时,它可能会返回 null,但这无关紧要。
    • @HugoZink 与什么无关?您对未定义变量的 进行的任何测试都会告诉您该值是 null。在您查看之前该值是否存在是哲学家的问题,但就任何可观察到的行为而言,该值始终为null
    【解决方案8】:

    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
    

    【讨论】:

      【解决方案9】:

      我不同意你关于 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。您永远不应该处于在运行时不知道实际变量是否存在的情况。
      • @chazomaticus 好吧,您永远不应该处于打开 register_globals 的情况,所以我支持该声明。
      • 哦,我同意。尽管如此,并不是每个人都可以控制他们的代码的部署位置。掌握每种情况的信息很有用,无论事情“应该”如何。
      • @chazomaticus 如果您的问题是register_globals,那么您的答案不是更改为isset()The PHP manual 提到“首先初始化变量确实是一种很好的编程习惯”,它在设计时而不是运行时解决了register_globals。还有an FAQ entry 提供unregister_globals() 函数在运行时处理它。
      【解决方案10】:

      我将为此快速添加两美分。这个问题令人困惑的一个原因是因为这个场景似乎返回了相同的结果,错误报告not完全:

      $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 开发人员没有费心把它放进去。

      【讨论】:

      • 未定义变量的警告提示程序员他们在代码中做错了什么。在调试之外(有语言之外的工具),程序永远不需要检测这种状态,因为程序员应该总是知道他们声明了哪些变量。
      【解决方案11】:

      如果我运行以下命令:

      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 比最初粘贴的内容多一点。是的,它为先前设置但现在为空的变量返回 false,但如果尚未设置(曾经)变量以及任何已标记为未设置的变量,它也会返回 false。它还注意到 NULL 字节(“\0”)不被认为是 null 并且将返回 true。

      判断是否设置了变量。

      如果变量已被取消设置 unset(),它将不再被设置。 如果测试 a,isset() 将返回 FALSE 已设置为 NULL 的变量。 另请注意,NULL 字节 ("\0") 是 不等同于 PHP NULL 常数。

      【讨论】:

      • 他从该链接中获取了文档。阅读您提供的链接上描述部分的第一句,第二段这正是他上面引用的内容。
      • 这对于简单的脚本来说不是一个坏习惯,但在复杂的(例如大型 OO)项目中,它变得不可行。另外,正如我上面所说,is_null() 对未设置的变量返回 TRUE,所以除了避免 PHP 警告外,没有理由做你所说的。
      • 对于精心设计的“大型 OO”项目,为什么这会是个问题呢?为什么在第一次使用之前可能没有设置的方法体中有 $foo?
      【解决方案12】:

      尝试使用

      unset($v)
      

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

      【讨论】:

      • 我不确定你的意思。如果您有一个包含一个元素“a”的数组,则不必取消设置()元素“b”,因为元素“b”在 PHP 中不存在,它只是不存在。与例如相同的事情全局变量,您可以将其视为 $GLOBALS 数组的元素。
      • 但我同意实际上确实存在具有 NULL 值的变量。
      【解决方案13】:

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

      为了进行一些比较,Icon 明确定义了一个未使用的变量作为返回 &amp;null,因此您可以使用 Icon 中的 is-null 测试来检查未设置的变量。这确实使事情变得更容易。另一方面,Visual BASIC 对没有值的变量有多种状态(Null、Empty、Nothing,...),您经常需要检查其中的多个状态。众所周知,这是错误的来源。

      【讨论】:

        【解决方案14】:

        根据empty()函数的PHP手册,“确定一个变量是否被认为是空的。如果一个变量不存在或者如果它的值等于FALSE,它就被认为是空的。empty()不会生成一个如果变量不存在,则发出警告。” (我的重点。)这意味着 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 或我们想要以这种方式测试的任何其他变量。

        【讨论】:

        • 如果目的是避免警告您的变量尚未声明,那么解决方案是修复您的代码以正确声明它们。您的 inst 函数基本上就像 @ 错误抑制运算符:“我知道我在这里做错了,但我只是希望该消息消失,而不以任何方式实际更改我的代码操作” .
        • 另一方面,检测方法很巧妙,但我仍然坚信,除了响应它们捕获的警告信息之外,您永远不应该使用它们. (您可能应该澄清您的输出缓冲版本需要将 error_reporting 设置为高并打开 display_errors。)
        【解决方案15】:

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

        error_reporting(E_ALL); // Enables E_NOTICE
        

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

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

        Why should I fix E_NOTICE errors?

        在我的情况下,在没有它的情况下在项目中工作了一年多,但习惯于在声明变量时非常小心,因此转换速度很快。

        【讨论】:

          【解决方案16】:

          了解变量是否在当前范围内定义($GLOBALS 不可信)的唯一方法是array_key_exists( 'var_name', get_defined_vars() )

          【讨论】:

          • 我认为这是很多人之前所说的,还是我错了?
          【解决方案17】:

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

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

          【讨论】:

          • empty() 不检查变量是否为空,它检查它是否为假-y,例如不是""(空字符串)、0(0 作为整数)、0.0(0 作为浮点数)、"0"(0 作为字符串)、NULLFALSE 之一、array()(空数组)和$var;(声明的变量,但没有值)。假设您在一个表单中有一个必填单选字段,其中包含两个输入值01。如果您使用empty() 进行验证并且用户选择0 之一,您会无意中出错“必填字段不能为空”。参见手册php.net/manual/en/function.empty.php
          猜你喜欢
          • 2011-03-23
          • 2010-12-13
          • 2019-02-02
          • 1970-01-01
          • 2012-07-22
          • 2010-10-10
          • 1970-01-01
          相关资源
          最近更新 更多