PHP:为什么零长度字符串的数组语法将字符串转换为数组?
PHP: Why Does array Syntax on a Zero Length String Cast the String as an Array?
在PHP中,您可以使用数组语法访问字符串索引。下面的程序
<?php
$foo = "Hello";
echo $foo[0],"\n";
?>
回声
H
但是,如果您访问零长度字符串的第一个字符
<?php
$bar = "";
$bar[0] = "test";
var_dump($bar);
?>
PHP 将您的字符串转换为数组。上面的代码产生
array(1) {
[0] =>
string(4) "test"
}
即我的零长度字符串被转换为数组。类似的 "accessing an undefined index of a string" 示例不会产生这种转换行为。
$bar = " ";
$bar[1] = "test";
var_dump($bar);
生成字符串 t
。即 $bar
仍然是一个字符串,不会转换为数组。
我知道当语言需要为您推断 and/or 自动转换变量时,这些不直观的边缘情况是不可避免的,但是有人知道这里的幕后发生了什么吗?
即在 PHP 中的 C/C++ 层级正在发生什么来实现这一点。为什么我的变量变成了数组。
PHP 5.6,如果重要的话。
我怀疑 "" 被视为未设置,然后被转换为数组。通常 "" != null != unset,但是,php 就此而言有点奇怪。
php > $a="test"; $a[0] = "yourmom"; var_dump( $a );
string(4) "yest"
php > $a=""; $a[0] = "yourmom"; var_dump( $a );
array(1) {
[0]=>
string(7) "yourmom"
}
php > var_dump((bool) "" == null);
bool(true)
php > var_dump((bool) $f == null);
PHP Notice: Undefined variable: f in php shell code on line 1
PHP Stack trace:
PHP 1. {main}() php shell code:0
Notice: Undefined variable: f in php shell code on line 1
Call Stack:
470.6157 225848 1. {main}() php shell code:0
bool(true)
当您对空字符串使用数组语法时,它被更改为数组的原因是因为索引 0 未定义,并且此时没有类型。这是一种案例研究。
<?php
$foo = "Hello"; // $foo[0] is a string "H"
echo $foo[0],"\n"; // H
$foo[0] = "same?"; // $foo[0] is still a string, "s" note that only the s is kept.
echo $foo,"\n"; // sello
echo $foo[0],"\n"; // s
$foo[1] = "b"; // $foo[1] is a string "b"
echo $foo,"\n"; // sbllo
$bar = ""; // nothing defined at position 0
$bar[0] = "t"; // array syntax creates an array with a string as the first index
var_dump($bar); // array(1) { [0] => string(1) "t" }
我试图在 PHP 来源中找到发生这种情况的位置。我在 PHP 内部和一般 C 方面的经验有限,所以如果我错了,请有人纠正我。
我认为这发生在 zend_fetch_dimension_address:
if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) {
if (type != BP_VAR_UNSET && UNEXPECTED(Z_STRLEN_P(container) == 0)) {
zval_ptr_dtor_nogc(container);
convert_to_array:
ZVAL_NEW_ARR(container);
zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
goto fetch_from_array;
}
看起来如果容器是零长度字符串,它会先将其转换为数组,然后再对其执行任何操作。
在 C 级别上,当使用 [] 运算符完成赋值时,变量将转换为数组。当然,当它是一个字符串时,长度为 0 并且不是未设置类型的调用(例如 unset($test[0])).
case IS_STRING: {
zval tmp;
if (type != BP_VAR_UNSET && Z_STRLEN_P(container)==0) {
goto convert_to_array;
}
https://github.com/php/php-src/blob/PHP-5.6.0/Zend/zend_execute.c#L1156
布尔假值发生相同的转换。
case IS_BOOL:
if (type != BP_VAR_UNSET && Z_LVAL_P(container)==0) {
goto convert_to_array;
}
通过测试确认:
<?php
$bar = false;
$bar[0] = "test";
var_dump($bar);
输出:
array(1) { [0]=> string(4) "test" }
当使用 true 时:
<?php
$bar = true;
$bar[0] = "test";
var_dump($bar);
输出:
WARNING Cannot use a scalar value as an array on line number 3
bool(true)
https://github.com/php/php-src/blob/PHP-5.6.0/Zend/zend_execute.c#L1249
当值为 bool 类型且值为 true 时,将执行以下代码:
case IS_BOOL:
if (type != BP_VAR_UNSET && Z_LVAL_P(container)==0) {
goto convert_to_array;
}
/* break missing intentionally */
default:
if (type == BP_VAR_UNSET) {
zend_error(E_WARNING, "Cannot unset offset in a non-array variable");
result->var.ptr_ptr = &EG(uninitialized_zval_ptr);
PZVAL_LOCK(EG(uninitialized_zval_ptr));
} else { // Gets here when boolean value equals true.
zend_error(E_WARNING, "Cannot use a scalar value as an array");
result->var.ptr_ptr = &EG(error_zval_ptr);
PZVAL_LOCK(EG(error_zval_ptr));
}
break;
PHP 5.6 版使用 ZEND 2.6.0 版
在PHP中,您可以使用数组语法访问字符串索引。下面的程序
<?php
$foo = "Hello";
echo $foo[0],"\n";
?>
回声
H
但是,如果您访问零长度字符串的第一个字符
<?php
$bar = "";
$bar[0] = "test";
var_dump($bar);
?>
PHP 将您的字符串转换为数组。上面的代码产生
array(1) {
[0] =>
string(4) "test"
}
即我的零长度字符串被转换为数组。类似的 "accessing an undefined index of a string" 示例不会产生这种转换行为。
$bar = " ";
$bar[1] = "test";
var_dump($bar);
生成字符串 t
。即 $bar
仍然是一个字符串,不会转换为数组。
我知道当语言需要为您推断 and/or 自动转换变量时,这些不直观的边缘情况是不可避免的,但是有人知道这里的幕后发生了什么吗?
即在 PHP 中的 C/C++ 层级正在发生什么来实现这一点。为什么我的变量变成了数组。
PHP 5.6,如果重要的话。
我怀疑 "" 被视为未设置,然后被转换为数组。通常 "" != null != unset,但是,php 就此而言有点奇怪。
php > $a="test"; $a[0] = "yourmom"; var_dump( $a );
string(4) "yest"
php > $a=""; $a[0] = "yourmom"; var_dump( $a );
array(1) {
[0]=>
string(7) "yourmom"
}
php > var_dump((bool) "" == null);
bool(true)
php > var_dump((bool) $f == null);
PHP Notice: Undefined variable: f in php shell code on line 1
PHP Stack trace:
PHP 1. {main}() php shell code:0
Notice: Undefined variable: f in php shell code on line 1
Call Stack:
470.6157 225848 1. {main}() php shell code:0
bool(true)
当您对空字符串使用数组语法时,它被更改为数组的原因是因为索引 0 未定义,并且此时没有类型。这是一种案例研究。
<?php
$foo = "Hello"; // $foo[0] is a string "H"
echo $foo[0],"\n"; // H
$foo[0] = "same?"; // $foo[0] is still a string, "s" note that only the s is kept.
echo $foo,"\n"; // sello
echo $foo[0],"\n"; // s
$foo[1] = "b"; // $foo[1] is a string "b"
echo $foo,"\n"; // sbllo
$bar = ""; // nothing defined at position 0
$bar[0] = "t"; // array syntax creates an array with a string as the first index
var_dump($bar); // array(1) { [0] => string(1) "t" }
我试图在 PHP 来源中找到发生这种情况的位置。我在 PHP 内部和一般 C 方面的经验有限,所以如果我错了,请有人纠正我。
我认为这发生在 zend_fetch_dimension_address:
if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) {
if (type != BP_VAR_UNSET && UNEXPECTED(Z_STRLEN_P(container) == 0)) {
zval_ptr_dtor_nogc(container);
convert_to_array:
ZVAL_NEW_ARR(container);
zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
goto fetch_from_array;
}
看起来如果容器是零长度字符串,它会先将其转换为数组,然后再对其执行任何操作。
在 C 级别上,当使用 [] 运算符完成赋值时,变量将转换为数组。当然,当它是一个字符串时,长度为 0 并且不是未设置类型的调用(例如 unset($test[0])).
case IS_STRING: {
zval tmp;
if (type != BP_VAR_UNSET && Z_STRLEN_P(container)==0) {
goto convert_to_array;
}
https://github.com/php/php-src/blob/PHP-5.6.0/Zend/zend_execute.c#L1156
布尔假值发生相同的转换。
case IS_BOOL:
if (type != BP_VAR_UNSET && Z_LVAL_P(container)==0) {
goto convert_to_array;
}
通过测试确认:
<?php
$bar = false;
$bar[0] = "test";
var_dump($bar);
输出:
array(1) { [0]=> string(4) "test" }
当使用 true 时:
<?php
$bar = true;
$bar[0] = "test";
var_dump($bar);
输出:
WARNING Cannot use a scalar value as an array on line number 3
bool(true)
https://github.com/php/php-src/blob/PHP-5.6.0/Zend/zend_execute.c#L1249
当值为 bool 类型且值为 true 时,将执行以下代码:
case IS_BOOL:
if (type != BP_VAR_UNSET && Z_LVAL_P(container)==0) {
goto convert_to_array;
}
/* break missing intentionally */
default:
if (type == BP_VAR_UNSET) {
zend_error(E_WARNING, "Cannot unset offset in a non-array variable");
result->var.ptr_ptr = &EG(uninitialized_zval_ptr);
PZVAL_LOCK(EG(uninitialized_zval_ptr));
} else { // Gets here when boolean value equals true.
zend_error(E_WARNING, "Cannot use a scalar value as an array");
result->var.ptr_ptr = &EG(error_zval_ptr);
PZVAL_LOCK(EG(error_zval_ptr));
}
break;
PHP 5.6 版使用 ZEND 2.6.0 版