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版本的猿们做好兼容处理,避免产生问题。

posted @ 2020-01-09 14:30  _Eternity味道  Views(559)  Comments(0Edit  收藏  举报