V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
WytheHuang
V2EX  ›  PHP

为什么这个代码在 PHP 7.0 和 PHP 5.6 得出结果不同?

  •  
  •   WytheHuang · 2018-04-18 21:38:03 +08:00 · 3781 次点击
    这是一个创建于 2411 天前的主题,其中的信息可能已经有所发展或是发生改变。
    $arr = array(0,1,2,3,4,5);
    $ref = &$arr;
    foreach($arr as $value){
        $arr = array();
        echo $value;
    }
    

    在 PHP7.0 中, 输出 012345, 而在 PHP 5 中 输出 0. PHP7.0 提及到 foreach 改变的有三点.

    1. foreach 不再改变内部数组指针.
    2. foreach 通过值遍历时,操作的值为数组的副本
    3. foreach 通过引用遍历时,有更好的迭代特性

    看到第二点有点懵, 我看一些资料都说 PHP 5 在 foreach 是操作数组副本的, 那么官方手册写这句话有什么含义还是有所变化? 为何会导致 PHP7.0 和 PHP 5 的结果截然不同?

    8 条回复    2018-04-19 10:43:13 +08:00
    carlclone
        1
    carlclone  
       2018-04-18 22:09:20 +08:00
    二、按照值进行循环的时候,foreach 是对该数组的拷贝操作

    foreach 按照值进行循环的时候(by-value),foreach 是对该数组的一个拷贝进行操作。这样在循环过程中对数组做的修改是不会影响循环行为的。

    $array = [0, 1, 2];
    $ref =& $array; // Necessary to trigger the old behavior
    foreach ($array as $val) {
    var_dump($val);
    unset($array[1]);
    }

    http://www.jb51.net/article/91331.htm 这里第二点 , 以前应该不是拷贝了
    WytheHuang
        2
    WytheHuang  
    OP
       2018-04-18 22:35:57 +08:00
    @carlclone 但是, PHP 5 以前不是拷贝的话, 那么下面这个代码应该跟 PHP 7 不一样
    ```
    $arr = array('a','b','c');
    foreach ($arr as $key=> $value) {
    $arr[] = 'd';
    print_r($arr);
    var_dump($key,$value);
    }
    ```
    然而输出其结果也是一样的.
    > Array ( [0] => a [1] => b [2] => c [3] => d )
    int(0) string(1) "a"
    Array ( [0] => a [1] => b [2] => c [3] => d [4] => d ) int(1)
    string(1) "b"
    Array ( [0] => a [1] => b [2] => c [3] => d [4] => d [5] => d )
    int(2) string(1) "c"
    而且上面代码$ref = &$array 为什么会出现这样结果, 不是很明白.
    jfcherng
        3
    jfcherng  
       2018-04-18 23:05:08 +08:00
    2. foreach by-value operates on a copy of the array

    When used in the default by-value mode, foreach will now operate on a copy of the array being iterated rather than the array itself. This means that changes to the array made during iteration will not affect the values that are iterated.

    把完整的官方說明看完啊,不要只看標題
    WytheHuang
        4
    WytheHuang  
    OP
       2018-04-19 09:31:02 +08:00
    DukeAnn
        5
    DukeAnn  
       2018-04-19 10:18:09 +08:00
    PHP 7 你可以这么理解,`foreach` 把整个数组隐式的镜像了一份给自己用,所以无论你怎么修改原数组都不会影响到 `foreach` 镜像数据,但是你打出来你原始数组是已经被修改了的。

    ```php
    $arr = array(0,1,2,3,4,5);
    $ref = &$arr;
    foreach($arr as $key => $value){
    $arr = array();
    echo "foreach 镜像数据:" . $value;
    echo "\n";
    echo "原始数组:";
    var_dump($arr);
    }
    ```

    运行结果:
    foreach 镜像数据:0
    原始数组:array(0) {
    }
    foreach 镜像数据:1
    原始数组:array(0) {
    }
    foreach 镜像数据:2
    原始数组:array(0) {
    }
    foreach 镜像数据:3
    原始数组:array(0) {
    }
    foreach 镜像数据:4
    原始数组:array(0) {
    }
    foreach 镜像数据:5
    原始数组:array(0) {
    }
    WytheHuang
        6
    WytheHuang  
    OP
       2018-04-19 10:34:24 +08:00
    @DukeAnn PHP 7 这样可以理解, 而在 PHP 5 中的, 加了引用. 情况有所不同的, 实时随着数组改变的, 就是这一点让我觉得很郁闷.
    ```
    // PHP 输出: foreach 镜像数据:0 原始数组:array(0) { }
    $arr = array(0,1,2,3,4,5);
    $ref = &$arr;
    foreach($arr as $key => $value){
    $arr = array();
    echo "foreach 镜像数据:" . $value;
    echo "\n";
    echo "原始数组:";
    var_dump($arr);
    }
    ```
    DukeAnn
        7
    DukeAnn  
       2018-04-19 10:42:03 +08:00
    @WytheHuang 这个没看具体文档,但是根据当前输出的情况和最近看 Go 语言的遍历机制,我推测 php5 应该是只缓存了当前遍历这一次的 $value 和 $key,而且遍历会改变原始数组的内部指针位置从而拿到下一个需要遍历的键值对,进行镜像。所以在 foreach 内部将数组赋值为空,第二次遍历就凉了。
    DukeAnn
        8
    DukeAnn  
       2018-04-19 10:43:13 +08:00
    @WytheHuang 个人推测,具体的可以自己实践一下,或者查查文档。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1065 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 19:13 · PVG 03:13 · LAX 11:13 · JFK 14:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.