ArrayAccess 接口

(PHP 5, PHP 7, PHP 8)

简介

提供将对象作为数组访问的接口。

接口概要

interface ArrayAccess {
/* 方法 */
public offsetExists(混合 $offset): 布尔值
public offsetGet(混合 $offset): 混合
public offsetSet(混合 $offset, 混合 $value):
public offsetUnset(混合 $offset):
}

示例

示例 #1 基本用法

<?php
class Obj implements ArrayAccess {
public
$container = [
"one" => 1,
"two" => 2,
"three" => 3,
];

public function
offsetSet($offset, $value): void {
if (
is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}

public function
offsetExists($offset): bool {
return isset(
$this->container[$offset]);
}

public function
offsetUnset($offset): void {
unset(
$this->container[$offset]);
}

public function
offsetGet($offset): mixed {
return isset(
$this->container[$offset]) ? $this->container[$offset] : null;
}
}

$obj = new Obj;

var_dump(isset($obj["two"]));
var_dump($obj["two"]);
unset(
$obj["two"]);
var_dump(isset($obj["two"]));
$obj["two"] = "A value";
var_dump($obj["two"]);
$obj[] = 'Append 1';
$obj[] = 'Append 2';
$obj[] = 'Append 3';
print_r($obj);
?>

上面的示例将输出类似于

bool(true)
int(2)
bool(false)
string(7) "A value"
obj Object
(
    [container:obj:private] => Array
        (
            [one] => 1
            [three] => 3
            [two] => A value
            [0] => Append 1
            [1] => Append 2
            [2] => Append 3
        )

)

目录

添加注释

用户贡献的注释 12 个注释

Per
13 年前
今天它咬了我一口,所以在这里记录下来,希望对其他人有帮助。
如果对实现 ArrayAccess 的类的对象调用 array_key_exists(),则不会调用 ArrayAccess::offsetExists()。
Yousef Ismaeil Cliprz
10 年前
<?php

/**
* 数组和对象访问
* 是的,您可以将类作为数组和对象访问
*
* @author Yousef Ismaeil <[email protected]>
*/

class ArrayAndObjectAccess implements ArrayAccess {

/**
* 数据
*
* @var array
* @access private
*/
private $data = [];

/**
* 通过键获取数据
*
* @param string 要检索的数据键
* @access public
*/
public function &__get ($key) {
return
$this->data[$key];
}

/**
* 为指定数据分配值
*
* @param string 要分配值的數據鍵
* @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 string 要取消设置的偏移量
* @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;
}

}

?>

用法

<?php
$foo
= new ArrayAndObjectAccess();
// 将数据设置为数组和对象
$foo->fname = 'Yousef';
$foo->lname = 'Ismaeil';
// 作为对象调用
echo 'fname 作为对象 '.$foo->fname."\n";
// 作为数组调用
echo 'lname 作为数组 '.$foo['lname']."\n";
// 作为数组重置
$foo['fname'] = 'Cliprz';
echo
$foo['fname']."\n";

/** 输出
fname 作为对象 Yousef
lname 作为数组 Ismaeil
Cliprz
*/

?>
Taliesin Nuin public at taliesinnuin dot net
5 年前
您可能想知道,实现 ArrayAccess 接口是否会使类可迭代。毕竟,它是一个“数组”。答案是否定的,不会。此外,如果您同时添加两者并希望它成为一个关联数组,则会有一些细微的陷阱。下面是一个同时具有 ArrayAccess 和 Iterator 接口的类。为了完整起见,还包括 Countable。

<?php
// 此代码使用了 PHP 7 中才有效的返回值类型。如果您必须使用旧版本的 PHP,则可以删除它们。
// 注意:offsetSet 方法包含一个仅在 PHP 7.3 及更高版本中有效的函数。

class HandyClass implements ArrayAccess, Iterator, Countable {

private
$container = array(); // 存储实际值的数组。
private $keys = array(); // 我们使用一个单独的键数组,而不是直接使用 $this->position,这样我们可以
private $position; // 处理关联数组。

public function __construct() {
$position = 0;

$this->container = array( // 用于演示的任意数组。在实际应用中,您可能需要将其设置为空数组,或者
"a" => 1, // 从其他地方获取,例如在构造函数中传递。
"b" => 2,
"c" => 3,
);
$this->keys = array_keys($this->container);
}

public function
count() : int { // 这对于 Countable 接口是必需的。它也可以返回
return count($this->keys); // count($this->container)。元素数量将相同。
}

public function
rewind() { // 对于 Iterator 接口是必需的。$this->position 显示我们在键列表中的位置。
$this->position = 0; // 请记住,我们希望所有操作都通过 $this->keys 来处理关联数组。
}

public function
current() { // 对于 Iterator 接口是必需的。
return $this->container[$this->keys[$this->position]];
}

public function
key() { // 对于 Iterator 接口是必需的。
return $this->keys[$this->position];
}

public function
next() { // 对于 Iterator 接口是必需的。
++$this->position;
}

public function
valid() { // 对于 Iterator 接口是必需的。
return isset($this->keys[$this->position]);
}

public function
offsetSet($offset, $value) { // 对于 ArrayAccess 接口是必需的。
if(is_null($offset)) {
$this->container[] = $value;
$this->keys[] = array_key_last($this->container); // 这仅在 PHP 7.3 及更高版本中有效。有关替代方法,请参见下面的说明。
} else {
$this->container[$offset] = $value;
if(!
in_array($offset, $this->keys)) $this->keys[] = $offset;
}
}

public function
offsetExists($offset) {
return isset(
$this->container[$offset]);
}

public function
offsetUnset($offset) {
unset(
$this->container[$offset]);
unset(
$this->keys[array_search($offset,$this->keys)]);
$this->keys = array_values($this->keys); // 这行代码重新索引容器键数组,因为如果有人
} // 删除了第一个元素,则在迭代时返回到位置 0 时将找不到任何元素。
public function offsetGet($offset) {
return isset(
$this->container[$offset]) ? $this->container[$offset] : null;
}
}
?>

示例用法

<?php
$myClass
= new HandyClass();
echo(
'元素数量: ' . count($myClass) . "\n\n");

echo(
"遍历内置的测试元素:\n");
foreach(
$myClass as $key => $value) {
echo(
"$value\n");
}
echo(
"\n");

$myClass['d'] = 4;
$myClass['e'] = 5;
echo(
'添加两个元素后的元素数量: ' . count($myClass) . "\n\n");

unset(
$myClass['a']);
echo(
'删除一个元素后的元素数量: ' . count($myClass) . "\n\n");

echo(
"直接访问元素:\n");
echo(
$myClass['b'] . "\n\n");

$myClass['b'] = 5;
echo(
"更改元素后的遍历:\n");
foreach(
$myClass as $key => $value) {
echo(
"$value\n");
}
echo(
"\n");
?>
Hayley Watson
11 年前
ArrayAccess 对象中使用的索引不限于字符串和整数,就像数组一样:您可以使用任何类型的索引,只要您编写实现来处理它们即可。SplObjectStorage 类利用了这一点。
Aussie Bags
7 年前
虽然 $offset 可以是任何东西,但看起来像整数的字符串在调用任何方法之前都会被强制转换为整数。

$x[1] 的偏移量是整数 1
$x['1'] 的偏移量是整数 1
$x['1.'] 的偏移量是字符串 '1.'
msherazjaved at gmail dot com
5 年前
这里添加了一个实验性的检查,是对 Per(关于 offsetExists 方法的发现)的补充(8 年前)。

<?php
class obj implements ArrayAccess {
private
$container = array();

public function
__construct() {
$this->container = array(
"one" => 1,
"two" => 2,
"three" => 3,
);
}

public function
offsetSet($offset, $value) {
print
"offsetSet method Triggered";

if (
is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}

public function
offsetExists($offset) {
print
"offsetExists method Triggered";

return isset(
$this->container[$offset]);
}

public function
offsetUnset($offset) {
print
"offsetUnset method Triggered";

unset(
$this->container[$offset]);
}

public function
offsetGet($offset) {
print
"offsetGet method Triggered";

return isset(
$this->container[$offset]) ? $this->container[$offset] : null;
}
}

$obj = new obj;

## Assigning a Value
$obj['two'] = '2'; // Output: offsetSet method Triggered

## Checking if array offset already set
isset($obj['two']); // Output : offsetExists method Triggered

## Unsetting array value on the offset 'two'
unset($obj['two']); // Output : offsetUnset method Triggered

## Accessing array value at offset 'two'
return $obj['two']; // Output : offsetGet method Triggered

?>
ivan dot dossev at gmail dot com
11 年前
很遗憾,您无法通过 ArrayAccess 对变量进行引用赋值(至少在 PHP 5.3.23 中)。
没有语法可以可选地将变量按引用传递给函数(这是旧版 PHP 的一个特性),实在太可惜了。
如果有了这个选项,ArrayAccess 就能完全模仿普通数组赋值的功能。

<?php
$var
= 'hello';
$arr = array();
$arr[0] = $var;
$arr[1] = &$var;
$var = 'world';
var_dump($arr[0], $arr[1]);

// string(5) "hello"
// string(5) "world"
?>

声明 "function offsetSet($offset, &$value)" 会导致致命错误。
所以,要按引用赋值,您可以使用丑陋的函数调用,例如

<?php
class obj implements ArrayAccess {

// ... ArrayAccess example code ...

public function &offsetSetRef($offset, &$value) {
if (
is_null($offset)) {
$this->container[] = &$value;
} else {
$this->container[$offset] = &$value;
}
return
$value; // should return in case called within an assignment chain
}
}

$var = 'hello';
$obj = new obj();
$obj[0] = $var;
//$obj[1] = &$var; // Fatal error: Cannot assign by reference to overloaded object
$obj->offsetSetRef(1, $var); // the work around
$var = 'world';
var_dump($obj[0], $obj[1]);

// string(5) "hello"
// string(5) "world"

?>
kaRemovTihsjouni at gmAndTihsaildot com
9 年前
reset() 方法在 ArrayAccess 对象中可能无法按预期工作。

使用 reset($myArrayAccessObject) 会返回 $myArrayAccessObject 中的第一个属性,而不是 items 数组中的第一个项目。

如果您想使用 reset() 方法返回第一个数组项目,那么可以使用以下简单的方法解决:

<?php
class MyArrayAccessObject implements Iterator, ArrayAccess, Countable {
protected
$first = null; //WARNING! Keep this always first.
protected $items = null;
private function
supportReset() {
$this->first = reset($this->items); //Support reset().
}
// ...
public function offsetSet($offset, $value) {
if (
$offset === null) {
$this->items[] = $value;
}
else {
$this->items[$offset] = $value;
}
$this->supportReset();
}
}
?>

最后,在所有更改内部 $items 数组的方法(如 offsetSet()、offsetUnset() 等)的末尾调用 $this->supportReset()。

这样,您就可以像往常一样使用 reset() 方法。

<?php
$firstArrayItem
= reset($myArrayAccessObject);
?>
max at flashdroid dot com
14 年前
实现 ArrayAccess 的对象可以在 PHP 5.3.0 中按引用返回对象。

您可以这样实现您的 ArrayAccess 对象

class Reflectable implements ArrayAccess {

public function set($name, $value) {
$this->{$name} = $value;
}

public function &get($name) {
return $this->{$name};
}

public function offsetGet($offset) {
return $this->get($offset);
}

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

...

}

这个基类允许您像在 Javascript 中一样使用 [] 操作符来获取/设置对象属性。

class Boo extends Reflectable {
public $name;
}

$obj = new Boo();
$obj['name'] = "boo";
echo $obj['name']; // prints boo
ProgMiner
6 年前
也许对大家有帮助,如果你这样做
<?php
$arrayAccessObject
[] = 'foo';
?>
PHP 会将空字符串作为 $offset 传递给 offsetSet($offset, $value) 方法。
php at lanar dot com dot au
10 年前
实现 ArrayAccess 的对象不支持递增/递减运算符 ++ 和 --,这与 array() 和 ArrayObject() 不同。

<?php

class MyArray implements ArrayAccess
{
// offsetSet, offsetGet etc implemented
}

$x = new MyArray() ;
$x[0] = 0 ;
$x[0]++ ; //error 'Indirect modification of overloaded element has no effect'
$x[0] += 1 ; // this works OK.

?>
jordistc at gmail dot com
8 年前
您可以使用 __invoke 魔术方法,在实现 ArrayAccess 的类的对象上使用数组函数,方法如下:

<?php
class ArrayVar implements ArrayAccess
{
private
$data = [];

public function
__invoke()
{
return
$this->data;
}
}
?>

现在您可以这样使用它:
<?php
$arrayar
= new ArrayVar();
$arrayar['one'] = 'primer';
$arrayar['two'] = 'segon';
$arrayar['three'] = 'tercer';

$keys = array_keys($arrayar());
var_dump($keys);
// array (size=3)
// 0 => string 'one'
// 1 => string 'two'
// 2 => string 'three'

$diff = array_diff($arrayar(), [ 'two' => 'segon']);
var_dump($diff);
// array (size=2)
// 'one' => string 'primer'
// 'three' => string 'tercer'
?>
To Top