Skip to content

在我们日常使用框架开发的时候经常会看到这样一个现象,主流框架都提供了像访问数组一样访问对象的能力,那么这些框架究竟是怎么实现这样的操作的呢?本文将带你探究这些实现背后的秘密武器--ArrayAccess 接口。

本文是 PHP 内置接口系列文章的第一篇,从这篇文章开始我们探究 PHP 一系列的内置接口的使用。ArrayAccess 是我们日常开发中使用频率比较高的一系列接口之一,他能够给予你像访问数组一样访问对象的能力。

正如上边所介绍的,实现这一访问能力的主要来自于 ArrayAccess 接口,也就是说你只要正确的实现这一接口,在你使用数组式的操作访问你自定义的实现类时,PHP 将会自动调用相关方法实现对应的访问操作。

完整的 ArrayAccess 接口如下:

php
<?php

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 )
}

对应方法的功能如下:

方法功能
offsetExists检查一个偏移位置是否存在,调用 isset()时自动调用
offsetGet获取一个偏移位置的值,访问具体对象键值时自动调用
offsetSet设置一个偏移位置的值,给数据赋值时自动调用
offsetUnset复位一个偏移位置的值 ,销毁对象键值时自动调用
php
下面是一个来自于官方手册的实现这里仅作参考:

<?php

/**
* 对象数组访问能力
* 正是如此,你可以像访问数组一样访问一个对象
* @author Yousef Ismaeil <[email protected]>
*/

class ArrayAndObjectAccess implements ArrayAccess {

    private $data = [];

    /**
     * 通过键值来访问数组对象值
     * @param 访问数据的键值
     * @access public
     */
    public function &__get ($key) {
        return $this->data[$key];
    }

    /**
     * 向数组(对象)设置一个值
     * @param 需要设置数据的键(数据的key,对象的属性)
     * @param mixed  对应键的值
     * @access public
     */
    public function __set($key,$value) {
        $this->data[$key] = $value;
    }

    /**
     * 检测数组(对象)中是否存在一个值
     * @param string 需要被检测的数据的键
     * @access public
     * @return boolean
     * @abstracting ArrayAccess
     */
    public function __isset ($key) {
        return isset($this->data[$key]);
    }

    /**
     * 通过键来销毁一个数组、对象
     * @param string 需要被销毁对象的键
     * @access public
     */
    public function __unset($key) {
        unset($this->data[$key]);
    }

    /**
     * 给对应的数组,对象属性赋值
     * @param string 需要被设置的数组的键,对象的属性名称
     * @param mixed  需要被设置数组的值对象属性对应的值
     * @access public
     * @abstracting ArrayAccess
     */
    public function offsetSet($offset,$value) {
        if (is_null($offset)) {
            $this->data[] = $value;
        } else {
            $this->data[$offset] = $value;
        }
    }

    /**
     * 检测数组偏移,对象属性值是否存在
     * @param string 被检测的键
     * @access public
     * @return boolean
     * @abstracting ArrayAccess
     */
    public function offsetExists($offset) {
        return isset($this->data[$offset]);
    }

    /**
     * 销毁对应的数组偏移数据,对象对应的数据
     * @param 需要被销毁的键
     * @access public
     * @abstracting ArrayAccess
     */
    public function offsetUnset($offset) {
        if ($this->offsetExists($offset)) {
            unset($this->data[$offset]);
        }
    }

    /**
     * 返回对应的数组偏移数据,对象对应的数据
     * @param string 需要被返回的数组的键,对象的属性
     * @access public
     * @return mixed
     * @abstracting ArrayAccess
     */
    public function offsetGet($offset) {
        return $this->offsetExists($offset) ? $this->data[$offset] : null;
    }

}

需要指出的是这一接口紧实现了,数组式的对象访问能力,诸如数组的迭代,count 等操作,如果需要相关的功能你还需要同时实现 Iterator 接口和 Countable 接口。