【问题标题】:(bool)true var does not get converted to array on array assignment, but (bool)false does. Why?(bool)true var 不会在数组赋值时转换为数组,但 (bool)false 会。为什么?
【发布时间】:2010-07-12 20:34:32
【问题描述】:

使用 PHP... 一个例子。这会产生一个警告 - 正如预期的那样 - 并且 $myVar 保持为 bool(true)。

$myVar = true;
$myVar[] = 'Hello';  // Warning: Cannot use a scalar value as an array

但是下一个示例'works',$myVar 被转换为具有单个元素'Hello' 的数组。

$myVar = false;
$myVar[] = 'Hello';  // Converted into an array

结果:

array(1) {
  [0]=>
  string(5) "Hello"
}

然而 bool(true) 和 bool(false) 都是标量。那么为什么会有差异呢? PHP 中的什么规则控制这种行为?还是“就是这样”?!

我最初认为这可能与类型转换规则有关,但 bool(true) 和 bool(false) 在这方面的行为相同。

谢谢。

【问题讨论】:

  • 你到底为什么要做这样的事情呢? :]
  • 嗯,这是一个非常简单的例子。一个函数在允许的错误状态下返回一个数组或 (bool)false。然后使用$myVar[$key] 将元素添加到此数组中。在 (bool)false 的情况下,会创建一个新数组 - 很棒。然后有一天该函数做了一些奇怪的事情并返回 int(1) 并且脚本失败了。这让我想知道为什么脚本一开始就使用 (bool)false!

标签: php arrays


【解决方案1】:

所以,尽管我不知道为什么 PHP 会这样做,但我查看了一些 Zend 代码,至少可以告诉你在哪里可以找到 PHP 是如何做到这一点的。

所以,重要的代码在zend_fetch_dimension_address。

那么,让我们涵盖上述情况:

如果是IS_ARRAY - 一切都很明显。

如果IS_OBJECT 抛出错误,除非它有ArrayAccess。

如果 IS_STRING 抛出错误,除非字符串长度为零。

如果它IS_NULL 创建一个新数组。

如果它IS_BOOL 抛出错误,除非它是错误的。

否则,抛出错误。

所以,这证实了你和我的测试:

如果对象、非空字符串、true 和其他标量(即 long 和 double)则出错。 如果数组、空字符串、null 和 false 没有错误。所以基本上它会对大多数(但不是全部)“虚假”值进行自动转换。

【讨论】:

  • 是的,感谢您的故障。因此,您最初的反应似乎是,“我认为这是因为一个是 true,另一个是 false :D 那是 PHP!”,几乎是在按钮上! :)
  • 一点也不奇怪。将空数组转换为bool 会产生false,而将非空数组转换为bool 会产生true。这是一个愚蠢的语法糖,它允许您通过简单的if ($array) 测试数组是否为空。向后看,false 应该在逻辑上转换为一个空数组。但是,没有将true 转换为填充数组的合理方法,因此会引发错误。
  • @meagar:当然你可以将 true 转换为填充数组: php > var_dump((array)true);数组(1){[0]=>布尔(真)}
  • @w3d:随着时间的推移,我完全忘记了这一点。但现在再看一遍,我认为这种行为确实有道理。 PHP 的一个特性是数组不需要显式初始化,即即使 $array 尚未定义,您也可以编写 $array[] = 'x'。上述行为与此匹配,通过允许推送到 null 值(在 PHP 中 null 与 undefined 大致相同)和任何虚假(null、false、''、array())值一般。
  • @w3d: PS:在 PHP 5.4 中有一个相关的行为改变:现在你不能再将 false 值转换为对象了(好吧,你可以,但是你会得到一个警告)通过在其上设置属性。 (即在 5.4 之前,数组和对象的行为是相同的。现在对象不再允许这样做了。)
【解决方案2】:

PHP 不是强类型语言。您正在将数组分配给包含 false 值的变量,而不是 boolean false 值。

在 PHP 的保护下,它必须将 $myVar 视为具有评估为空的值,因此允许数组赋值。

再一次,如果您将 PHP 视为一种动态脚本语言,这并不完全出乎意料。

【讨论】:

  • 这不可能是全部真相。正如我在回答中所说,0 即使empty 也会引发错误。
  • 虽然 PHP 不是强类型的,但它是一个 boolean false 值(=== false)。是的,正如 nikic 所说,也不一定当 var 评估为空时。
  • @nikic,这绝对不是全部。但显然它并没有像 w3d 中提到的那样进行严格的相等检查。如果你发现了全部真相,那么你就已经回答了你自己的问题;)
【解决方案3】:

如果你想知道为什么会这样,那么检查 php from here I think,编译它并使用 gdb 进行一些逐步调试......除非有人足够好找到负责的代码这个。 然后查看负责此的代码周围的 cmets(如果有的话)。 正如下面的 cmets 中所提到的,另一种查找方法是在代码中搜索错误消息。让我们这样做吧!

    [greg@liche php-src-5.3]$ grep -rn --exclude-dir=".svn" "Cannot use a scalar value as an array" .
./tests/lang/bug29893.phpt:10:Warning: Cannot use a scalar value as an array in %sbug29893.php on line %d
./tests/lang/engine_assignExecutionOrder_002.phpt:12:// Warning: Cannot use a scalar value as an array in %s on line %d
./tests/lang/engine_assignExecutionOrder_002.phpt:94:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/zend_execute.c:1015:                             zend_error(E_WARNING, "Cannot use a scalar value as an array");
./Zend/tests/indexing_001.phpt:51:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:54:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:57:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:77:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:96:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:99:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:102:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:119:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:137:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:140:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:143:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:160:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:179:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:182:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:185:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:202:Warning: Cannot use a scalar value as an array in %s on line %d

看起来它在 zend_execute.c 中,这是我找到的:

 case IS_BOOL:
1223    if (type != BP_VAR_UNSET && Z_LVAL_P(container)==0) {
1224    goto convert_to_array;
1225    }
1226    /* break missing intentionally */
1227    
1228    default:
1229    if (type == BP_VAR_UNSET) {
1230    zend_error(E_WARNING, "Cannot unset offset in a non-array variable");
1231    AI_SET_PTR(result, &EG(uninitialized_zval));
1232    PZVAL_LOCK(&EG(uninitialized_zval));
1233    } else {
1234    zend_error(E_WARNING, "Cannot use a scalar value as an array");
1235    result->var.ptr_ptr = &EG(error_zval_ptr);
1236    PZVAL_LOCK(EG(error_zval_ptr));
1237    }
1238    break; 

我认为ZLVAL_P(container)==0 条件是造成这种差异的原因... lval 表示左值,即被分配的值...我认为它的计算结果为 0。

【讨论】:

  • 我已经在查看 Zend 代码,但找不到导致这种奇怪现象的文件。
  • 找到 svn.php.net/viewvc/php/php-src/trunk/Zend/…> 行 612 convert_scalar_to_array。这可能是功能,但它也对我没有帮助。至少它没有向我解释任何事情。
  • 如果将 (bool)true 或 (bool)false 类型转换为数组,您将得到基本相同的结果...元素值。
  • 我们不知道是否有人尝试投射。我们正在寻找的代码是使用运算符'['。在签出的本地副本中搜索错误消息将是找到它的好方法。
  • @greg0ire 是的。事实上,该值被覆盖/替换(或“转换”),根本没有强制转换。
【解决方案4】:

虽然 PHP 是松散类型的,但类型仍然具有一定的重要性,因此例如 === 或 !==。

如果将 Nikic 重置为零,则会出错的原因是它现在实际上是一个整数,而不是一个布尔值。

为了演示,您可以执行以下操作并通过强制转换得到相同的错误。

$a = (int) false;
$a[] = 'goat'; #throws warning, thinks it's a 0

$a = (bool) 0;
$a[] = 'goat'; #works, thinks it's a false

但是,我知道这只是一个迂腐的例子! (bool) true 仍然计算为 1。

【讨论】:

  • 是的,int(0) - 整数 - 标量 - 失败。最初背后的唯一想法是,如果值“评估”为假,它可能会起作用。但事实并非如此。对于“简单”类型,此数组转换仅适用于 null、string('') 和 bool(false)。空字符串和 bool(false) 似乎是特殊情况(如上所述)。
  • 我实际上认为它应该像 isset 那样工作,那里的任何东西都会通过,除非是空值,否则在这里抛出警告!
  • 是的,这肯定是有道理的。尽管 PHP 能够切换类型以适应它似乎是在试图猜测程序员的真实意图? “哦,那就继续吧,它几乎 NULL,我会让你!”
猜你喜欢
  • 1970-01-01
  • 2017-06-02
  • 1970-01-01
  • 2011-01-03
  • 1970-01-01
  • 2011-12-16
  • 2015-06-12
  • 2019-02-04
  • 2015-02-04
相关资源
最近更新 更多