首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 實現 ArrayAccess 與 Iterator 接口:讓對象像數組一樣操作

     編輯:PHP 相關知識

核心接口速查

完整示例:虛擬文件系統節點

場景:將目錄結構映射為對象樹,支持 `$node['subdir']` 訪問子節點、`foreach` 遍歷當前層級、懶加載文件內容。

<?php
class FSNode implements ArrayAccess, Iterator, Countable {
    private string $path;           // 物理路徑
    private array  $children = [];  // 緩存的子節點 [name => FSNode]
    private int    $position = 0;   // Iterator 內部指針
    private array  $keys = [];      // 子節點名稱列表

    public function __construct(string $path) {
        $this->path = rtrim($path, '/');
    }

    // ===== ArrayAccess =====
    public function offsetExists(mixed $offset): bool {
        $this->loadChildren();
        return isset($this->children[$offset]);
    }
    public function offsetGet(mixed $offset): mixed {
        $this->loadChildren();
        return $this->children[$offset] ?? null;
    }
    public function offsetSet(mixed $offset, mixed $value): void {
        throw new LogicException('FSNode 是只讀的');
    }
    public function offsetUnset(mixed $offset): void {
        throw new LogicException('FSNode 是只讀的');
    }

    // ===== Iterator =====
    public function current(): mixed {
        $this->loadChildren();
        $key = $this->keys[$this->position] ?? null;
        return $key ? $this->children[$key] : null;
    }
    public function key(): mixed {
        return $this->keys[$this->position] ?? null;
    }
    public function next(): void {
        $this->position++;
    }
    public function rewind(): void {
        $this->position = 0;
    }
    public function valid(): bool {
        $this->loadChildren();
        return isset($this->keys[$this->position]);
    }

    // ===== Countable =====
    public function count(): int {
        $this->loadChildren();
        return count($this->children);
    }

    // ===== 內部邏輯 =====
    private function loadChildren(): void {
        if ($this->children !== []) return; // 已緩存

        if (!is_dir($this->path)) return;
        $items = scandir($this->path);
        foreach ($items as $item) {
            if ($item === '.' || $item === '..') continue;
            $full = $this->path . '/' . $item;
            if (is_dir($full) || is_file($full)) {
                $this->children[$item] = new self($full);
                $this->keys[] = $item;
            }
        }
    }

    // 便利方法
    public function isDir(): bool { return is_dir($this->path); }
    public function isFile(): bool { return is_file($this->path); }
    public function getContent(): string|false {
        return $this->isFile() ? file_get_contents($this->path) : false;
    }
    public function getPath(): string { return $this->path; }
}

使用效果

$root = new FSNode('/var/www/html');

// 像數組一樣訪問
$config = $root['config']['database.php'];
if ($config) {
    echo $config->getContent(); // 直接讀取文件內容
}

// 遍歷
foreach ($root as $name => $node) {
    echo $name . ($node->isDir() ? '/' : '') . "\n";
}

// 計數
echo "項目數: " . count($root);

關鍵設計要點

  1. 懶加載:`loadChildren()` 僅在首次訪問時掃描目錄,避免構建整棵樹的性能開銷。
  2. 只讀語義:`offsetSet/Unset` 拋出異常,明確傳達不可變意圖。
  3. Iterator 狀態隔離:`position`、`keys` 為實例屬性,多重 `foreach` 互不干擾(PHP 內部會 clone 迭代器)。
  4. 配合 `__get/__set`:可進一步支持 `$node->config` 寫法,但需注意與 `ArrayAccess` 優先級(`__get` 只在屬性不存在時觸發)。

進階:實現 JsonSerializable


public function jsonSerialize(): array {
    $this->loadChildren();
    $data = [];
    foreach ($this->children as $name => $node) {
        $data[$name] = $node->isFile() 
            ? ['type' => 'file', 'size' => filesize($node->getPath())]
            : ['type' => 'dir', 'children' => $node]; // 遞歸序列化
    }
    return $data;
}
// json_encode($root) 直接輸出結構化 JSON

總結

實現 `ArrayAccess` + `Iterator` + `Countable` 只需十幾行樣板代碼,卻能賦予對象原生數組般的操作體驗,極大提升 API 易用性。適用於集合類、樹形結構、配置對象、查詢結果集封裝等場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
接口用途必須實現的方法
ArrayAccess對象支持 `$obj['key']` 讀寫offsetExists, offsetGet, offsetSet, offsetUnset
Iterator對象支持 `foreach ($obj as $k => $v)`current, key, next, rewind, valid
Countable支持 `count($obj)`count()
Serializable自定義序列化serialize(), unserialize()