cha*_*cus 186 php variables isset
来自isset()
文档:
isset() will return FALSE if testing a variable that has been set to NULL.
Run Code Online (Sandbox Code Playgroud)
基本上,isset()
不检查变量是否设置,但是否设置为除了NULL
.
鉴于此,实际检查变量是否存在的最佳方法是什么?我尝试过类似的东西:
if(isset($v) || @is_null($v))
Run Code Online (Sandbox Code Playgroud)
(这@
是必要的,以避免$v
未设置时的警告)但is_null()
有一个类似的问题isset()
:它返回未TRUE
设置的变量!它似乎也是:
@($v === NULL)
Run Code Online (Sandbox Code Playgroud)
工作完全一样@is_null($v)
,所以也是这样.
我们如何可靠地检查PHP中是否存在变量?
编辑:在未设置的变量和设置为的变量之间,PHP显然存在差异NULL
:
<?php
$a = array('b' => NULL);
var_dump($a);
Run Code Online (Sandbox Code Playgroud)
PHP显示$a['b']
存在,并且具有NULL
值.如果你添加:
var_dump(isset($a['b']));
var_dump(isset($a['c']));
Run Code Online (Sandbox Code Playgroud)
你可以看到我正在谈论的isset()
功能歧义.以下是所有这三个的输出var_dump()s
:
array(1) {
["b"]=>
NULL
}
bool(false)
bool(false)
Run Code Online (Sandbox Code Playgroud)
进一步编辑:两件事.
一,用例.将数组转换为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));
Run Code Online (Sandbox Code Playgroud)
输出:
bool(true)
bool(false)
Run Code Online (Sandbox Code Playgroud)
由于它可以正确处理任何地方,我可以看到在不存在的变量和设置的变量之间存在任何歧义NULL
,我array_key_exists()
在PHP中调用官方最简单的方法来真正检查变量的存在.
(只有我可以想到的其他情况是类属性,property_exists()
根据它的文档,它的工作方式类似于array_key_exists()
它正确地区分未被设置和被设置为NULL
.)
Zor*_*che 96
如果您要检查的变量位于全局范围内,您可以执行以下操作:
array_key_exists('v', $GLOBALS)
Run Code Online (Sandbox Code Playgroud)
IMS*_*SoP 46
试图概述各种讨论和答案:
这个问题没有一个单一的答案可以取代所有isset
可以使用的方法.一些用例由其他功能解决,而其他用户则无法经受审查,或者在代码高尔夫之外具有可疑价值.其他用例远非"破碎"或"不一致",而是证明了为什么isset
反应null
是逻辑行为.
数组可以像对待的变量集合,与unset
和isset
对待他们,好像他们是.但是,由于它们可以被迭代,计数等,因此缺失值与其值不同null
.
在这种情况下,答案是使用array_key_exists()
而不是isset()
.
由于这是将数组作为函数参数进行检查,如果数组本身不存在,PHP仍将引发"通知".在某些情况下,可以有效地论证每个维度应该首先进行初始化,因此通知正在完成其工作.对于其他情况,array_key_exists
依次检查数组的每个维度的"递归" 函数将避免这种情况,但基本上与此相同@array_key_exists
.它与处理null
价值观有些相关.
在传统的"面向对象程序设计"理论中,封装和多态是对象的关键属性; 在基于类的OOP实现像PHP的,封装的属性声明为类定义的一部分,并给予访问级别(public
,protected
,或private
).
但是,PHP还允许您动态地向对象添加属性,就像您对数组的键一样,并且有些人stdClass
在类似的对象中使用无类对象(技术上,内置的实例,没有方法或私有功能)关联数组的方法.这导致函数可能想要知道是否已将特定属性添加到给予它的对象的情况.
与数组键一样,用于检查对象属性的解决方案包含在语言中,合理地称为property_exists
.
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
如果其他地方的代码区分两种状态,那么"覆盖"一个未初始化的变量只会产生问题,所以就其自身而言,这种可能性是反对进行这种区分的论据.
get_defined_vars
和compact
PHP中的一些很少使用的函数,例如get_defined_vars
和compact
,允许您将变量名称视为数组中的键.对于全局变量,超全局数组$GLOBALS
允许类似的访问,并且更常见.如果未在相关范围中定义变量,则这些访问方法的行为会有所不同.
一旦您决定使用这些机制之一将一组变量视为一个数组,您就可以对其执行与任何普通数组相同的操作.因此,见1.
仅存在预测这些函数将如何表现的功能(例如"返回的数组中是否存在键'foo' get_defined_vars
?")是多余的,因为您可以简单地运行该函数并找出没有不良影响的函数.
$$foo
)虽然与将一组变量转换为关联数组的函数不完全相同,但大多数情况下使用"变量变量"("赋值给基于此另一个变量命名的变量")可以而且应该更改为使用关联数组.
从根本上说,变量名是程序员赋予值的标签; 如果你在运行时确定它,它不是真正的标签,而是某个键值存储中的键.更实际的是,通过不使用数组,您将失去计数,迭代等功能; 它也可能变得不可能在键值存储"外部"变量,因为它可能被覆盖了$$foo
.
一旦更改为使用关联数组,代码将适用于解决方案1. $foo->$property_name
可以使用解决方案2 来解决间接对象属性访问(例如).
isset
打字比打字容易得多array_key_exists
我不确定这是否真的相关,但是,PHP的函数名称可能非常冗长且有时不一致.显然,PHP的预史版本使用函数名称的长度作为哈希键,因此Rasmus故意编写函数名称,htmlspecialchars
因此它们将具有不寻常的字符数...
不过,至少我们不是在写Java,嗯?;)
未初始化的变量具有其类型的默认值,具体取决于使用它们的上下文
我不确定Zend引擎中是否存在"未初始化但已知类型"的概念,或者这是否对该语句的读取过多.
很清楚的是,它对它们的行为没有实际的区别,因为该页面上描述的未初始化变量的行为与其值为的变量的行为相同null
.要选择一个示例,这两个$a
和$b
在此代码中将最终为整数42
:
unset($a);
$a += 42;
$b = null;
$b += 42;
Run Code Online (Sandbox Code Playgroud)
(第一个会引发关于未声明变量的通知,试图让你编写更好的代码,但它对代码的实际运行方式没有任何影响.)
(保持最后一个,因为它比其他人长得多.也许我会稍后编辑它......)
请考虑以下代码:
$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!';
}
Run Code Online (Sandbox Code Playgroud)
如果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!';
}
}
Run Code Online (Sandbox Code Playgroud)
因为$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_count
与reset_assignment_count
...
显然,添加这样的功能会在语言的复杂性和性能上进行权衡,因此需要仔细权衡其预期的有用性.与very_null
常数一样,它仅在非常狭窄的情况下才有用,并且同样可以抵抗重新分解.
在希望显而易见的问题是,为什么PHP运行时引擎应该事先假定你想保持这样的事情的轨道,而不是让你做它明确,使用正常的代码.
Mar*_*Fox 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 |
Run Code Online (Sandbox Code Playgroud)
为了适应表格我压缩了标签:
$a;
指的是声明但未赋值的变量$a = null;
$a = [];
$a = 0;
$a === null
isset($a)
empty($a)
$a ? true : false
所有结果都是布尔值,true
打印并false
省略.
你可以自己运行测试,检查这个要点:https:
//gist.github.com/mfdj/8165967
Mat*_*ijs 17
您可以使用紧凑语言构造来测试是否存在null变量.不存在的变量不会在结果中出现,而空值将显示.
$x = null;
$y = 'y';
$r = compact('x', 'y', 'z');
print_r($r);
// Output:
// Array (
// [x] =>
// [y] => y
// )
Run Code Online (Sandbox Code Playgroud)
就你的例子而言:
if (compact('v')) {
// True if $v exists, even when null.
// False on var $v; without assignment and when $v does not exist.
}
Run Code Online (Sandbox Code Playgroud)
当然,对于全局范围内的变量,您也可以使用array_key_exists().
顺便说一句,我个人会避免像瘟疫这样的情况,即不存在的变量和具有空值的变量之间存在语义差异.PHP和大多数其他语言都不认为有.
gre*_*ive 15
我想所有这一切的明显答案是......不要将你的变量初始化为NULL,将它们作为与它们的目标相关的东西进行初始化.
NULL应该被视为"非存在值",这是NULL的含义.该变量不能被归类为PHP的现有变量,因为它没有被告知它试图成为什么类型的实体.它可能也不存在,所以PHP只是说"很好,它不会因为无论如何都没有意义,而NULL是我这样说的方式".
我们现在争论吧."但是NULL就像说0或FALSE或''.
错误,0-FALSE-''仍被归类为空值,但它们被指定为某种类型的值或问题的预定答案.FALSE是肯定或否定的答案,''是某人提交的标题的答案,0是数量或时间等的答案.它们被设置为某种类型的答案/结果,这使得它们在设置时有效.
NULL只是没有回答什么,它没有告诉我们是或否它并没有告诉我们时间它并没有告诉我们一个空白字符串被提交.这是理解NULL的基本逻辑.
这不是为了解决问题而创建古怪的函数,它只是改变了你的大脑看待NULL的方式.如果它为NULL,则假设它未设置为任何值.如果您预先定义变量,则根据您打算使用的类型将它们预先定义为0,FALSE或"".
随意引用这个.这是我的逻辑头顶:)
小智 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");
}
}
Run Code Online (Sandbox Code Playgroud)
作为greatbigmassive 对NULL 含义的讨论的补充,请考虑“变量的存在”的实际含义。
在许多语言中,您必须在使用之前显式声明每个变量;这可能决定了它的类型,但更重要的是它声明了它的作用域。变量“存在”在其范围内的任何地方,而在其范围之外的任何地方都“存在”——无论是整个函数还是单个“块”。
在它的范围内,一个变量为您(程序员)选择的标签分配了一些含义。在它的范围之外,那个标签是没有意义的(你是否在不同的范围内使用相同的标签基本上是无关紧要的)。
在 PHP 中,不需要声明变量- 只要您需要它们,它们就会立即生效。当您第一次写入变量时,PHP 会在内存中为该变量分配一个条目。如果您从当前没有条目的变量中读取,PHP 会认为该变量具有值NULL
。
但是,如果您使用一个变量而没有先“初始化”它,自动代码质量检测器通常会警告您。首先,这有助于检测错别字,例如分配给$thingId
但读取自$thing_id
;但其次,它迫使您考虑该变量具有意义的范围,就像声明一样。
任何关心变量是否“存在”的代码都是该变量作用域的一部分——无论它是否已被初始化,作为程序员的你已经在代码的那个点赋予了该标签的含义。既然你在使用它,它就必须在某种意义上“存在”,如果它存在,它必须有一个隐含的值;在 PHP 中,隐含值是null
.
由于 PHP 的工作方式,可以编写代码来将现有变量的名称空间视为您赋予意义的标签范围,而不是某种键值存储。你可以,例如,运行这样的代码:$var = $_GET['var_name']; $$var = $_GET['var_value'];
。仅仅因为你可以,并不意味着这是一个好主意。
事实证明,PHP 有一种更好的表示键值存储的方法,称为关联数组。尽管可以将数组的值视为变量,但您也可以对整个数组执行操作。如果您有关联数组,则可以使用array_key_exists()
.
您还可以以类似的方式使用对象,动态设置属性,在这种情况下,您可以property_exists()
以完全相同的方式使用。当然,如果你定义一个类时,你可以声明其性能已经-你甚至可以选择public
,private
和protected
范围。
虽然有技术的可变(相对于阵列键,或一个对象的属性)之间的差异还未被初始化(或者是已经明确地unset()
和一个其值)null
,即认为差异是任何代码有意义以不打算使用的方式使用变量。