ArrayAccess 和 Iterator 接口
另一个有用的功能是在 PHP 中将自定义对象集合作为数组访问。PHP(> = 5.0.0)核心有两个接口支持:ArrayAccess
和 Iterator
。前者允许你以阵列形式访问自定义对象。
ArrayAccess 接口
假设我们有一个用户类和一个存储所有用户的数据库表。我们想创建一个 UserCollection
类,它将:
- 允许我们通过其用户名唯一标识符来解决某些用户
- 在我们的用户集合上执行基本(不是所有 CRUD,但至少是创建,检索和删除)操作
考虑以下来源(以下我们使用自 5.4 版以来可用的短数组创建语法 []
):
class UserCollection implements ArrayAccess {
protected $_conn;
protected $_requiredParams = ['username','password','email'];
public function __construct() {
$config = new Configuration();
$connectionParams = [
//your connection to the database
];
$this->_conn = DriverManager::getConnection($connectionParams, $config);
}
protected function _getByUsername($username) {
$ret = $this->_conn->executeQuery('SELECT * FROM `User` WHERE `username` IN (?)',
[$username]
)->fetch();
return $ret;
}
// START of methods required by ArrayAccess interface
public function offsetExists($offset) {
return (bool) $this->_getByUsername($offset);
}
public function offsetGet($offset) {
return $this->_getByUsername($offset);
}
public function offsetSet($offset, $value) {
if (!is_array($value)) {
throw new \Exception('value must be an Array');
}
$passed = array_intersect(array_values($this->_requiredParams), array_keys($value));
if (count($passed) < count($this->_requiredParams)) {
throw new \Exception('value must contain at least the following params: ' . implode(',', $this->_requiredParams));
}
$this->_conn->insert('User', $value);
}
public function offsetUnset($offset) {
if (!is_string($offset)) {
throw new \Exception('value must be the username to delete');
}
if (!$this->offsetGet($offset)) {
throw new \Exception('user not found');
}
$this->_conn->delete('User', ['username' => $offset]);
}
// END of methods required by ArrayAccess interface
}
然后我们可以:
$users = new UserCollection();
var_dump(empty($users['testuser']),isset($users['testuser']));
$users['testuser'] = ['username' => 'testuser',
'password' => 'testpassword',
'email' => 'test@test.com'];
var_dump(empty($users['testuser']), isset($users['testuser']), $users['testuser']);
unset($users['testuser']);
var_dump(empty($users['testuser']), isset($users['testuser']));
假设在我们启动代码之前没有 testuser
,它将输出以下内容:
bool(true)
bool(false)
bool(false)
bool(true)
array(17) {
["username"]=>
string(8) "testuser"
["password"]=>
string(12) "testpassword"
["email"]=>
string(13) "test@test.com"
}
bool(true)
bool(false)
重要提示: 当你检查是否存在具有 array_key_exists
功能的键时,不会调用 offsetExists
。所以下面的代码将输出 false
两次:
var_dump(array_key_exists('testuser', $users));
$users['testuser'] = ['username' => 'testuser',
'password' => 'testpassword',
'email' => 'test@test.com'];
var_dump(array_key_exists('testuser', $users));
迭代器
让我们用 Iterator
接口的一些函数从上面扩展我们的类,以允许用 foreach
和 while
迭代它。
首先,我们需要添加一个保存当前迭代器索引的属性,让我们将其添加到类属性 $_position
:
// iterator current position, required by Iterator interface methods
protected $_position = 1;
其次,让我们将 Iterator
接口添加到我们的类实现的接口列表中:
class UserCollection implements ArrayAccess, Iterator {
然后添加接口函数本身所需的:
// START of methods required by Iterator interface
public function current () {
return $this->_getById($this->_position);
}
public function key () {
return $this->_position;
}
public function next () {
$this->_position++;
}
public function rewind () {
$this->_position = 1;
}
public function valid () {
return null !== $this->_getById($this->_position);
}
// END of methods required by Iterator interface
所以这里所有这些都是实现两个接口的类的完整源代码。请注意,此示例并不完美,因为数据库中的 ID 可能不是顺序的,但这只是为了给你一个主要想法:你可以通过实现 ArrayAccess
和 Iterator
接口以任何可能的方式处理对象集合:
class UserCollection implements ArrayAccess, Iterator {
// iterator current position, required by Iterator interface methods
protected $_position = 1;
// <add the old methods from the last code snippet here>
// START of methods required by Iterator interface
public function current () {
return $this->_getById($this->_position);
}
public function key () {
return $this->_position;
}
public function next () {
$this->_position++;
}
public function rewind () {
$this->_position = 1;
}
public function valid () {
return null !== $this->_getById($this->_position);
}
// END of methods required by Iterator interface
}
以及循环遍历所有用户对象的 foreach:
foreach ($users as $user) {
var_dump($user['id']);
}
这将输出类似的东西
string(2) "1"
string(2) "2"
string(2) "3"
string(2) "4"
...