iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 7
1
Modern Web

成為 Modern PHPer系列 第 7

Day 07:善用預定義的 interface 及 class

前言

PHP 有預定義一些 interface 及 class,妥善運用這些類別與介面,有助於讓標準函式或敘述式使用它們,也易於讓其它開發者理解。

介面

Traversable

實現了 Traversable 的 class 表示它可以被 foreach

  • 實際上它並不可以被直接拿來 implement
    • 它將會在實現了 IteratorIteratorAggregate 的 interface 中被繼承
  • 通常這會與 instanceof 做搭配
if( is_array( $items ) || $items instanceof Traversable ) {
    // 表示 $items 可以用 foreach
    foreach ($items as $item) { }
}

Iterator

Iterator 又稱「迭代器」,這是 Traversable 的具現化之一。

Iterator 需要實現五個 methods

  • current(): mixed:取得目前的項目
  • key(): scalar:取得目前項目的索引
  • next(): void:取得下一個項目
  • rewind(): void:回到第一個項目
  • valid(): bool:確認目前位置是否有效,通常在 next()rewind() 之後使用
class DemoIterator implements Iterator
{
    private $position = 0;
    private $data = ['one', 'two', 'three'];
    
    public function currnet()
    {
        return $this->data[$position];
    }
    
    public function key()
    {
        return $this->position;
    }
    
    public function next()
    {
        $this->position++;
    }
    
    public function rewind()
    {
        $this->position = 0;
    }
    
    public function valid()
    {
        return isset($this->data[$this->position]);
    }
}

IteratorAggregate

IteratorAggregate 又稱「聚合迭代器」,是另一個 Traversable 的具現化。

IteratorAggregate 需要實現一個 method:

  • getIterator(): Traversable:直接回傳具有 Traversable 的 Class,它有助於整合多個 Iterator 或其它實現了 Traversable 的 Class。
class DemoIteratorAggregate implements IteratorAggregate
{
    private $data1;
    private $data2;
    
    public function __construct()
    {
        $this->data1 = [1, 2, 3];
        $this->data2 = new ArrayIterator([4, 5, 6]);
    }
    
    public function getIterator()
    {
        return new ArrayIterator([$this->data1, $this->data2]);
    }
}

Throwable

Throwable 表示該 Class 可被 throw,這實現於 ErrorException(PHP 7 之後,內部 Error 會被當作一個可被 catch 存在)

通常 Throwable 不會自行實現,因為通常都是建立 Exception,但與 Traversable 不同 Throwable 是可以被直接拿來使用的。

try {

} catch(Exception $e) {
    // 表示丟出 Exception,這通常是出現一些邏輯層的問題
} catch (Error $e) {
    // 表示丟出 Error,這通常是語法上具有問題(例如 type error)
}

// 我們可以將上敘述式簡化為

try {
} catch (Throwable $e) {
}

ArrayAccess

ArrayAccess 表示該類別所建立出來的變數可以像 array 一樣直接使用 offset 存取。

ArrayAccess 需要實現四個 methods:

  • offsetExists(mixed $offset): bool:確認 offset 是否存在
  • offsetGet(mixed $offset): mixed:取得該 offset 所在之值
  • offsetSet(mixed $offset, mixed $value): void:為指定的 offset 設定 value
  • offsetUnset(mixed $offset): void:移除指定的 offset
class DemoArrayAccess implements ArrayAccess
{
    private $data = [1, 2, 3];
    
    public function offsetExists($offset)
    {
        return isset($this->data[$offset]);
    }
    
    public function offsetGet($offset)
    {
        return $this->data[$offset];
    }
    
    public function offsetSet($offset, $value)
    {
        $offset === null
            ? $this->data[] = $value;
            : $this->data[$offset] = $value;
    }
    
    public function offsetUnset($offset)
    {
        unset($this->data[$offset]);
    }
}

Serializable

Serializable 表示此 Class 可以被「序列化」,序列化表示將一個 Class 的資訊轉為 string,有需要時可以利用該 string 還原回原本的 Class:例如將一個 class 做 json_encode() 就是一種序列化,它只要再被 json_decode() 就可以還原回來。

值得注意的是,如果 Class 有實現 __sleep()__wakeup() 再實現 Serializable__sleep()__wakeup() 會失效。

Serializable 需要實現兩個 methods:

  • serialize(): string:表示序列化所需的操作
  • unserialize(string $serialized): void:表示反序列化時的操作
class DemoSerializable implements Serializable
{
    private $data = 'My Data';
    
    public function serialize()
    {
        return $this->data;
    }
    
    public function unserialize(string $serialized)
    {
        $this->data = $serialized;
    }
}

$obj = new DemoSerializable();

$serialized = serialize($obj);
$unserialized = unserialize($obj);

類別

Closure

Closure 主要用於表示匿名函式。

$a = function () { echo 'hello world'; }

$a instanceof Closure; // true

Generator

Generator 主要用於 yield 的回傳值,這部份可參考我昨天的文章:Day 06: yield 的使用

值得注意的是,Generator 無法被 new 出來。

function generator(): Generator
{
    yield 1;
    yield 2;
    yield 3;
}

foreach (generator() as $item) {

}

WeakReference

WeakReference 是一個來自於 WeakRef 的 extension,在 PHP 7.4 之後被加入標準之中。

Weak Reference 又名「弱引用」,這在部份的物件導向程式設計中被認為是優化 GC 行為的一項 Feature。

PHP 的 GC 與 Java 類似,當兩個條件達成時,使用的記憶體就會被回收

  • 該段記憶體並未被引用
  • 達到 PHP 的 GC 時間(這是不定時的,Zend VM 會自行排程回收時機)
class A 
{
}

$a = new A; // $a 與 Class A 是引用關係,在 unset($a) 或生命週期結束之前它不會被 GC。

WeakReference 的引入目的在於做到類似於「Object 的快取」:當 Object 存活時,WeakReference 就可以使用它,當 Object 被回收時,WeakReference 也不會阻止 GC 去回收它。

class A
{
}

$a = new A;
$b = $a;
$weakA = WeakReference::create($a);

$weakA->get(); // object (A)#1 (0) {}
unset($a)
$weakA->get(); // null

後記

通常預定義的 Interface 及 Class 比較常用於 Array 系列(Iterator、ArrayAccess),事實上只要瞭解如何使用就能寫出易於理解的程式碼


上一篇
Day 06: yield 的使用
下一篇
Day 08:輸出內容的過濾
系列文
成為 Modern PHPer30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言