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"
...