You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP循环生成时间段数组时元素被重复覆盖的问题

问题:循环生成DateTime时间段数组时所有元素被最终值覆盖

尝试在循环中生成包含多个时间段(每个时间段含start和end)的数组,但每次对临时变量调用DateTime的add()方法后,最终数组内的所有值都会被临时变量的最终值覆盖。

原代码

$newPeriods = []; //final array

$firstPeriod = array(
    "start"=>new DateTime("2022/10/05 16:30:00"), 
    "end"=>new DateTime("2022/10/05 16:45:00")
);

for ($i = 0; $i < 3; $i++) {
    //if not first repetition
    if (isset($newPeriod)) {
        $newPeriod["start"] = end($newPeriods)["start"];
        $newPeriod["end"] = end($newPeriods)["end"];

        //I figured out that the array $newPeriods gets rewriten in the next two lines
        $newPeriod["start"]->add(new DateInterval('P1W'));
        $newPeriod["end"]->add(new DateInterval('P1W'));
    }
    //first repetition
    else {
        $newPeriod['start'] = $firstPeriod["start"];
        $newPeriod['end'] = $firstPeriod["end"];
    }

    var_dump($newPeriod); //this prints what I expect to find in $newPeriods

    array_push($newPeriods, $newPeriod);
}

var_dump($newPeriods);//all elements are like the last $newPeriod

预期输出(var_dump($newPeriod)结果)

array(2) {
  ["start"]=>"2022-10-05 23:55:00.000000"
  ["end"]=>"2022-10-06 00:15:00.000000"
}
array(2) {
  ["start"]=>"2022-10-12 23:55:00.000000"
  ["end"]=>"2022-10-13 00:15:00.000000"
}
array(2) {
  ["start"]=>"2022-10-19 23:55:00.000000"
  ["end"]=>"2022-10-20 00:15:00.000000"
}

实际输出(var_dump($newPeriods)结果)

array(3) {
  [0]=>
  array(2) {
    ["start"]=>"2022-10-19 23:55:00.000000"
    ["end"]=>"2022-10-20 00:15:00.000000"
  }
  [1]=>
  array(2) {
    ["start"]=>"2022-10-19 23:55:00.000000"
    ["end"]=>"2022-10-20 00:15:00.000000"
  }
  [2]=>
  array(2) {
    ["start"]=>"2022-10-19 23:55:00.000000"
    ["end"]=>"2022-10-20 00:15:00.000000"
  }
}

问题原因

核心问题是PHP中对象是引用传递,而非值传递。你每次将同一个DateTime对象的引用存入数组,后续调用add()方法修改的是同一个对象的内容,数组里所有元素指向的都是同一个对象实例,最终显示的自然都是最后一次修改后的结果。

解决办法

方案一:使用clone复制DateTime对象

通过clone关键字创建独立的DateTime对象副本,确保每个数组元素持有不同的对象实例:

$newPeriods = []; //final array

$firstPeriod = array(
    "start"=>new DateTime("2022/10/05 16:30:00"), 
    "end"=>new DateTime("2022/10/05 16:45:00")
);

for ($i = 0; $i < 3; $i++) {
    if (isset($newPeriod)) {
        // 复制上一个周期的DateTime对象,而非直接引用
        $newPeriod["start"] = clone end($newPeriods)["start"];
        $newPeriod["end"] = clone end($newPeriods)["end"];

        $newPeriod["start"]->add(new DateInterval('P1W'));
        $newPeriod["end"]->add(new DateInterval('P1W'));
    } else {
        // 复制初始的DateTime对象
        $newPeriod['start'] = clone $firstPeriod["start"];
        $newPeriod['end'] = clone $firstPeriod["end"];
    }

    array_push($newPeriods, $newPeriod);
}

var_dump($newPeriods);

方案二:使用DateTimeImmutable(推荐)

DateTimeImmutable是不可变版本的DateTime,它的所有修改方法(如add())都会返回一个新的对象,原对象保持不变,从根源上避免引用传递问题:

$newPeriods = []; //final array

$firstPeriod = array(
    "start"=>new DateTimeImmutable("2022/10/05 16:30:00"), 
    "end"=>new DateTimeImmutable("2022/10/05 16:45:00")
);

for ($i = 0; $i < 3; $i++) {
    if (isset($newPeriod)) {
        $prevStart = end($newPeriods)["start"];
        $prevEnd = end($newPeriods)["end"];
        // add()返回新对象,无需克隆
        $newPeriod["start"] = $prevStart->add(new DateInterval('P1W'));
        $newPeriod["end"] = $prevEnd->add(new DateInterval('P1W'));
    } else {
        $newPeriod['start'] = $firstPeriod["start"];
        $newPeriod['end'] = $firstPeriod["end"];
    }

    array_push($newPeriods, $newPeriod);
}

var_dump($newPeriods);

内容的提问来源于stack exchange,提问作者Xavier Patiño

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 19:50:39