Fluent 的一个坑

Fluent 是一个很方便的 model ,有一处需要注意的地方,就是用数组方式操作 (ArrayAccess) 时可能会直接读写到已定义的属性(property)。

比如 setter 方法:

public function __set($key, $value)
{
    $this->attributes[$key] = $value;
}

public function offsetSet($offset, $value)
{
    $this->{$offset} = $value;
}

如果我们的 Fluent 子类有一个属性 $foo ,那么调用 $model['foo'] = 'bar'; 会直接覆盖 $foo 这个属性值,不论 $foo 是 private 还是 public 的;而 $model->foo = 'bar'; 则是修改 $attributes['foo'] = 'bar'.

:warning: 数组方式的 get, set, isset, unset 操作 Fluent 都存在这个问题。

>>> $model = new Illuminate\Support\Fluent; $model->attributes = 'bar'; dd($model);
Illuminate\Support\Fluent {#935
  #attributes: array:1 [
    "attributes" => "bar"
  ]
}
>>> $model = new Illuminate\Support\Fluent; $model['attributes'] = 'bar'; dd($model);
Illuminate\Support\Fluent {#936
  #attributes: "bar"
}

这个是 Laravel 的一个历史遗留问题,我也提交过一个 PR 让数组方式直接操作 $attributes 数组,但是因为此修改对用户代码的影响太大,PR 被关闭了。

建议使用 Fluent 时重写相关方法,以避免私有的 property 被覆盖或者 $model['attributes'] = ... 导致程序崩溃。

trait FluentArrayAccess
{
    /**
     * Get an attribute from the container.
     *
     * @param  string  $key
     * @param  mixed   $default
     * @return mixed
     */
    abstract public function get($key, $default = null);

    /**
     * Determine if the given offset exists.
     *
     * @param  string  $offset
     * @return bool
     */
    public function offsetExists($offset)
    {
        return isset($this->attributes[$offset]);
    }

    /**
     * Get the value for a given offset.
     *
     * @param  string  $offset
     * @return mixed
     */
    public function offsetGet($offset)
    {
        return $this->get($offset);
    }

    /**
     * Set the value at the given offset.
     *
     * @param  string  $offset
     * @param  mixed   $value
     * @return void
     */
    public function offsetSet($offset, $value)
    {
        $this->attributes[$offset] = $value;
    }

    /**
     * Unset the value at the given offset.
     *
     * @param  string  $offset
     * @return void
     */
    public function offsetUnset($offset)
    {
        unset($this->attributes[$offset]);
    }
}
:point_right: Laravel 官网镜像 :cn:
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 3

Fluent这个类是用来做什么的?

6年前 评论

@相惜恨离 就是个 model 的基类,用一个数组维护动态属性,提供了数组存取方式访问和修改这些动态属性值。简易版的 Eloquent.

6年前 评论

兄dei,过了10个月了你理解了吗?
这两个方法是想让你在调用offsetSet时会自动重载到__set的意思呀,attributes当做个关键字不要用就好了

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!