当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《在PHP中测试变量存在的最佳方法;isset()显然已被打破》经验,为你挑选了6个好方法。

来自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:

 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()工作正常,我上面的用例和任何全局变量:



输出:

bool(true)
bool(false)

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

(只有我可以想到的其他情况是类属性,property_exists()根据它的文档,它的工作方式类似于array_key_exists()它正确地区分未被设置和被设置为NULL.)



1> Zoredache..:

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

array_key_exists('v', $GLOBALS) 


作为一种变体,如果检查也需要对局部范围变量起作用,则可以执行`$ defined_vars = get_defined_vars();`然后通过`array_key_exists('v',$ defined_vars)进行测试;`.
啊哈!现在你在说话!你会怎么做,比如,类属性?

2> IMSoP..:

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

这个问题没有一个单一的答案可以取代所有isset可以使用的方法.一些用例由其他功能解决,而其他用户则无法经受审查,或者在代码高尔夫之外具有可疑价值.其他用例远非"破碎"或"不一致",而是证明了为什么isset反应null是逻辑行为.

真实用例(带解决方案)

1.数组键

数组可以像对待的变量集合,与unsetisset对待他们,好像他们是.但是,由于它们可以被迭代,计数等,因此缺失值与其值不同null.

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

由于这是将数组作为函数参数进行检查,如果数组本身不存在,PHP仍将引发"通知".在某些情况下,可以有效地论证每个维度应该首先进行初始化,因此通知正在完成其工作.对于其他情况,array_key_exists依次检查数组的每个维度的"递归" 函数将避免这种情况,但基本上与此相同@array_key_exists.它与处理null价值观有些相关.

2.对象属性

在传统的"面向对象程序设计"理论中,封装和多态是对象的关键属性; 在基于类的OOP实现像PHP的,封装的属性声明为类定义的一部分,并给予访问级别(public,protected,或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数组以其他方式"污染"全局命名空间.

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

其次,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从未显式初始化,所以当第一次测试通过时它将采用一个值,这使得无法判断后续测试是否通过.当变量未正确初始化时,这实际上是一个非常常见的错误.

为了解决这个问题,我们需要做一些我评论过的东西.最明显的解决方案是设置$result一个some_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,而非unset,reset_assignment_state.

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

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

在希望显而易见的问题是,为什么PHP运行时引擎应该事先假定你想保持这样的事情的轨道,而不是让你做它明确,使用正常的代码.



3> Mark Fox..:

有时我会试图弄清楚在给定情况下使用哪种比较操作.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



4> Matijs..:

您可以使用紧凑语言构造来测试是否存在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和大多数其他语言都不认为有.


PHP没有,但我不会说_most_其他语言没有.如果未声明变量,大多数声明变量的语言都会抛出错误,但您可以将它们设置为"NULL".从语义上讲,"NULL"应该表示"没有资源",但不定义变量是程序员错误.

5> greatbigmass..:

解释NULL,逻辑思考

我想所有这一切的明显答案是......不要将你的变量初始化为NULL,将它们作为与它们的目标相关的东西进行初始化.

正确对待NULL

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

一个论点

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

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

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

摘要

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

随意引用这个.这是我的逻辑头顶:)


unset变量和变量=== null之间存在巨大差异.一个不存在,另一个值null.null表示没有值的参数不是真的.Null是null类型的值.它是完全有效的值,并且没有理由将php视为不存在的值,这很遗憾.如果不存在的变量为null并且每个现有变量都不为null并且将null赋值给变量将取消设置它,那就没关系.但是有很多情况,其中函数返回null作为实际值.然后我们搞砸了,因为没有血腥的方法来测试它.
很好的答案.很多时候,我看到人们咆哮他们如何讨厌这种或那种语言的特征.但是他们似乎在假设"如果它没有按照我的方式去做,那么它就会被打破." 是的,有糟糕的设计决定.但也有非常密切的开发人员!
这个答案在一个主要方面是有缺陷的:在OO编程中,null是表示"无对象"的逻辑选择.例如,在函数可以返回对象或没有对象的特殊情况下,null是显而易见的选择.从技术上讲,在PHP中,可以使用false或在布尔上下文中被认为是false的任何其他值,但是你会失去一些语义纯度.因此,null是一个非常合理的值,可以将变量初始化为最终应该保存对象的值,因为它与它的目标相关.
我知道我们"不应该"检查php中的变量存在,地狱,甚至没有任何真正的方法来检查它.我不打算编写代码,因为它在php中是不可能的.这是php的限制.非set和null变量之间显然存在差异,但php没有提供区分它们的方法.然而很多元功能依赖于它内部:读取不存在的var产生通知,`isset($ a ['x'])`如果`x`为null,它会告诉你,但是它会显示在`count( $ a)```compact`将适用于所有设置变量,包括`nulls`,依此类推.
只要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");
    }
}

推荐阅读
mobiledu2402851373
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有