1. 简介

Carbon 是一个 PHP 中处理日期时间的扩展包,Laravel 默认集成了该扩展包。需要注意的是,Carbon 实例是一个对象,对象赋值时,默认是引用传递。PHP 传值和传引用

2. 问题说明

例如在商城系统中,我们要统计最近三天的订单数据。

示例代码如下:

.
.
.
    public function handle()
    {
        $start = Carbon::make('2019-10-11');
        $end = Carbon::make('2019-10-13');

        for ($day = $start; $day <= $end; $day->addDay()) {
            $this->calculate($day);
        }
        .
        .
        .
    }
.
.
.
    protected function calculate(Carbon $day)
    {
        // 当天的起始和结束时间
        $start_of_day = $day->startOfDay()->toDateTimeString();
        $end_of_day = $day->endOfDay()->toDateTimeString();

        // 查询当天的订单
        .
        .
        .
    }
.
.
.

实际 calculate() 方法却只运行了两次,$day 依次为 2019-10-11 00:00:002019-10-12 23:59:59,和我们预期的运行三次并不相符。

3. 原因分析

原因在于,将 Carbon 实例作为参数传递给 calculate() 函数时,默认是引用传递,对其修改会影响原来的值。如果想在内存中生成两个一样的对象或者创建一个对象的副本,可以使用 clone。所以正确的写法是:

.
.
.
    protected function calculate(Carbon $day)
    {
        // 先克隆一个副本,对副本操作,避免影响原值
        $target_day = clone $day;

        // 当天的起始和结束时间
        $start_of_day = $target_day->startOfDay()->toDateTimeString();
        $end_of_day = $target_day->endOfDay()->toDateTimeString();

        // 查询当天的订单
        .
        .
        .
    }
.
.
.

4. 总结

PHP 传参时,如果变量是非对象,会直接拷贝其值,对这个变量做任何改动都不影响原值。传引用或者传对象,会传真实的内存地址,对这个变量做的改动会影响原值。Carbon 实例就是对象。