答案是肯定的,它确实复制了字符串。有点……不是真的。好吧,这取决于您对“复制”的定义...
>= 5.4
要了解发生了什么,让我们看看源代码。执行器在此处处理 5.5 中的变量转换。
zend_make_printable_zval(expr, &var_copy, &use_copy);
if (use_copy) {
ZVAL_COPY_VALUE(result, &var_copy);
// if optimized out
} else {
ZVAL_COPY_VALUE(result, expr);
// if optimized out
zendi_zval_copy_ctor(*result);
}
如您所见,zend_make_printable_zval()
如果 zval 已经是一个字符串,则调用使用 which 只是短路。
所以执行复制的代码是(else分支):
ZVAL_COPY_VALUE(result, expr);
现在,让我们看一下的定义ZVAL_COPY_VALUE
:
#define ZVAL_COPY_VALUE(z, v) \
do { \
(z)->value = (v)->value; \
Z_TYPE_P(z) = Z_TYPE_P(v); \
} while (0)
注意这是在做什么。字符串本身不会被复制(存储在->value
zval 的块中)。它只是被引用(指针保持不变,因此字符串值相同,没有副本)。但它正在创建一个新变量(包装值的 zval 部分)。
现在,我们开始zendi_zval_copy_ctor
通话。它在内部自己做了一些有趣的事情。笔记:
case IS_STRING:
CHECK_ZVAL_STRING_REL(zvalue);
if (!IS_INTERNED(zvalue->value.str.val)) {
zvalue->value.str.val = (char *) estrndup_rel(zvalue->value.str.val, zvalue->value.str.len);
}
break;
基本上,这意味着如果它是一个实习字符串,它将不会被复制。但如果不是,它将被复制......那么什么是实习字符串,这是什么意思?
<= 5.3
在 5.3 中,不存在内部字符串。所以字符串总是被复制。这真的是唯一的区别...
基准时间:
好吧,在这样的情况下:
$a = "foo";
$b = (string) $a;
在 5.4 中不会出现字符串的副本,但在 5.3 中会出现副本。
但在这样的情况下:
$a = str_repeat("a", 10);
$b = (string) $a;
所有版本都会生成一个副本。那是因为在 PHP 中,并非所有字符串都被保留...
让我们在基准测试中尝试一下:http: //3v4l.org/HEelW
$a = "foobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisout";
$b = str_repeat("a", 300);
echo "Static Var\n";
testCopy($a);
echo "Dynamic Var\n";
testCopy($b);
function testCopy($var) {
echo memory_get_usage() . "\n";
$var = (string) $var;
echo memory_get_usage() . "\n";
}
结果:
5.4 - 5.5 alpha 1(不包括其他 alpha,因为差异很小,不会产生根本差异)
Static Var
220152
220200
Dynamic Var
220152
220520
所以静态var增加了48字节,动态var增加了368字节。
5.3.11 至 5.3.22:
Static Var
624472
625408
Dynamic Var
624472
624840
静态变量增加了 936 字节,而动态变量增加了 368 字节。
所以请注意,在 5.3 中,静态和动态变量都被复制了。所以字符串总是重复的。
但是在带有静态字符串的 5.4 中,只复制了 zval 结构。这意味着被实习的字符串本身保持不变并且不会被复制......
另一件事
另一件需要注意的是,以上所有内容都没有实际意义。您将变量作为参数传递给函数。然后你在函数内部进行强制转换。所以写时复制将由您的线路触发。所以运行它总是会触发一个变量副本(好吧,在 99.9% 的情况下)。所以充其量(实习字符串)你在谈论 zval 重复和相关的开销。在最坏的情况下,您正在谈论字符串重复...