PHP的ArrayAccess接口应用

花无人戴,酒无人劝,醉也无人管。

引起问题

在Laravel下, 我们可以通过两种方式访问模型中的属性

  • 对象的方式
    1
    2
    $user = \App\Models\User::query()->find(1);
    dump($user->name);
  • 数组的方式
    1
    2
    $user = \App\Models\User::query()->find(1);
    dump($user['name']);

同样我们也可以通过这两种方式达到修改模型属性值的效果

1
2
3
4
$user = \App\Models\User::query()->find(1);

$user['name'] = '杨国奇';
$user->save();

这是什么原理呢?

一探究竟

我们通过查看User模型的源码, 发现了其中的奥秘

1
2
3
4
5
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
...

进一步查看Authenticatable类的源码

1
2
3
4
5
6
7
class User extends Model implements
AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
}

再查看Model类的源码

1
2
3
4
5
6
7
8
abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
{
use Concerns\HasAttributes,
Concerns\HasEvents,
Concerns\HasGlobalScopes,
Concerns\HasRelationships,

...

发现了其中的奥秘, Model类实现了ArrayAccess接口

什么是ArrayAccess接口

ArrayAccess(数组式访问)接口:提供像访问数组一样访问对象的能力的接口。

实现改接口需要实现以下几个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
ArrayAccess {
//检查一个偏移位置是否存在
abstract public boolean offsetExists ( mixed $offset );

//获取一个偏移位置的值
abstract public mixed offsetGet ( mixed $offset );

//设置一个偏移位置的值
abstract public void offsetSet ( mixed $offset , mixed $value );

//复位一个偏移位置的值
abstract public void offsetUnset ( mixed $offset );
}

我们Model类的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public function offsetExists($offset)
{
return ! is_null($this->getAttribute($offset));
}

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

/**
* Set the value for a given offset.
*
* @param mixed $offset
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value)
{
$this->setAttribute($offset, $value);
}

/**
* Unset the value for a given offset.
*
* @param mixed $offset
* @return void
*/
public function offsetUnset($offset)
{
unset($this->attributes[$offset], $this->relations[$offset]);
}