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
相关产品推荐
相关产品推荐

