PHP7.1与PHP5.6的版本中foreach以及string的差异
刚用指针解答一到算法题,联想到一些问题,在此记录
$arr = array( 1 => "start", 'wd' =>111, 3 => 333, "lz" => 4444, 55 =>"end" );
// foreach循环在PHP5.6中是通过数组指针的移动实现的遍历,而在PHP7中有所改变
foreach($arr as $key => $value){
echo "<br />$key =>$value";
}
$k = key($arr); // foreach结束后,指针的位置
$v = current($arr); // foreach结束后,指针的位置所指向的值
$res = next($arr); // foreach结束后,通过next()向下移动指针
echo "<br />此时(遍历之后),“位置”为:"; var_dump($k);
echo "<br />此时(遍历之后),对应“值”为:"; var_dump($v);
echo "<br />此时(遍历之后),指针移向相下一个位置 :"; var_dump($res);
exit;
PHP5.6的输出如下:
// $arr = array( 1 => "start", 'wd' =>111, 3 => 333, "lz" => 4444, 55 =>"end" ); // 参考原数组
1 =>start // 键值对
wd =>111
3 =>333
lz =>4444
55 =>end
此时(遍历之后),“位置”为:NULL
此时(遍历之后),对应“值”为:bool(false)
此时(遍历之后),指针移向相下一个位置 :bool(false)
PHP7.1的输出如下:
1 =>start
wd =>111
3 =>333
lz =>4444
55 =>end
此时(遍历之后),“位置”为:int 1
此时(遍历之后),对应“值”为:string 'start'
此时(遍历之后),指针移向相下一个位置 :int 111
区别
- PHP5.6中数组遍历后,是移动的指针,移呀移呀,就不在数组的有效范围内了。故此,执行各种操作都是NULL或false
- PHP7.1中,遍历后指针仍在数组的头部,因为按照值进行循环的时候, foreach是对该数组的副本操作,原数组指针并未移动,故此可以继续执行指针操作。
$arr = array( 1 => "start", 'wd' =>111, 3 => 333, "lz" => 4444, 55 =>"end" );
foreach($arr as $key => $value){
echo current($arr); // 全部输出 start, 说明$arr的指针并未移动
// echo "<br />$key =>$value";
}
$k = key($arr);
$v = current($arr);
$res = next($arr);
echo "<br />此时(遍历之后),“位置”为:"; var_dump($k);
echo "<br />此时(遍历之后),对应“值”为:"; var_dump($v);
echo "<br />此时(遍历之后),移向相下一个位置是否成功:"; var_dump($res);
$res = next($arr); // 仅加入几行next()和一行输出,其他代码一样
$res = next($arr);
$res = next($arr);
$res = next($arr);
echo "<br />此时(next移动到数组尾端后),移向相下一个位置是否成功:"; var_dump($res);
exit;
PHP7.1输出如下
startstartstartstartstart
此时(遍历之后),“位置”为:
D:\workspace\gitlab\water.service.klagri.com.cn\dev\index.php:10:int 1
此时(遍历之后),对应“值”为:
D:\workspace\gitlab\water.service.klagri.com.cn\dev\index.php:11:string 'start' (length=5)
此时(遍历之后),移向相下一个位置是否成功:
D:\workspace\gitlab\water.service.klagri.com.cn\dev\index.php:12:int 111
此时(next移动到数组尾端后),移向相下一个位置是否成功:
D:\workspace\gitlab\water.service.klagri.com.cn\dev\index.php:17:boolean false
- 由结果可以看出来,next()到数组最后一个位置后,再进行next()返回false,跟5.6的foreach一样。
----------------------------------------------------------------------分割线----------------------------------------------------------------------------------------
下面再来说说string的一切区别
- 请看下面这段代码,将罗马数字转为整数
$s = "LVIII"; // 罗马数字58
$rome = ["I"=>1, 'V'=>5, 'X'=>10, 'L'=>50, 'C'=>100, 'D'=>500, 'M'=>1000];
$len = strlen($s);
$sum = 0;
for($i=0; $i<$len; $i++){
$sum += $rome[$s[$i]];
if(isset($s[$i-1]) && ($rome[$s[$i-1]] < $rome[$s[$i]])){
$sum -= $rome[$s[$i-1]]*2;
if(isset($s[$i-2]) && ($rome[$s[$i-2]] < $rome[$s[$i-1]])){
$sum -= $rome[$s[$i-2]]*2;
}
}
}
var_dump($sum);exit;
- 用PHP5.6运行,结果是58
- 用PHP7.1运行,结果是56
原因分析
$s = "LVIII";
echo $s[-1];exit;
- 这两行代码,
- 用PHP5.6执行,Notice: Uninitialized string offset: -1
- 用PHP7.1执行,结果为I
- PHP7支持了反向索引,查看了下源码,无论输入正负索引,最终都会转为正序索引。
if (UNEXPECTED(Z_STRLEN_P(container) < (size_t)((offset < 0) ? -offset : (offset + 1)))) {
(gdb) s
1861 ? (zend_long)Z_STRLEN_P(container) + offset : offset;
我认为这两行最主要,与索引位置查找相关。下面个zend_string_init 是一个赋值操作
zend_string_init (persistent=0, len=1, str=0x7ffff145563c "I") //str为73 I的ASCII码
memcpy(ZSTR_VAL(ret), str, len);
看的不是太懂,第一次看源码也有点晕,找到这个之后,一下退出断点了,就没二次查找
总之,不论正负索引,在最后都会转化为正确的正序索引。
总结
使用foreach时,要注意PHP版本,同时要知道产生差异的原因: 一个是移动原数组指针,一个是移动副本的指针。了解原理才能更好的使用。
使用string 进行索引是否存在的判断是,要注意PHP版本,$str[-1]在PHP5.6中代表字符串之前的地址,显然是不存在的,在PHP7中代表从后往前数第一个字符。
建议工作中使用多个PHP版本的猿们做好兼容处理,避免产生问题。
知止而后有定,定而后能静,静而后能安,安而后能虑,虑而后能得。
所谓诚其意者,毋自欺也。