PHP定義了幾個內建的interface與class,這些interface與class主要是用來跟語法搭配使用的,同時也是SPL(Standard PHP Library)的基礎之一。
參考:
* PHP: Predefined Interfaces and Classes - Manual
除了需要操作或是實作某些跟語法相關的功能,或是理解SPL的體系(透過繼承與介面實作),平常大概不會注意到這些東西。不過理解一下對於深入這個語言還是有一些幫助。
先列出內建的interface:
如果用繼承的關係來看:
這幾個interface跟PHP的關係是什麼?稍微整理一下:
* Traversable
所有可以透過foreach來做iterate的東西,都是Traversable的子孫,例如之前試過的Iterator以及Generator。在SPL中還實作了許多Iterator,就不一一介紹了。
* ArrayAccess
實作了ArrayAccess的物件,就可以用Array的方式來操作。他定義了幾個介面:
只要定義了這幾個介面,就可以用Array的方式存取物件。來試一下看看:
<?php
class CircularArray implements ArrayAccess {
private $arr = array();
private $capacity = 10;
private $current = 0;
function __construct($capacity) {
$this->capacity = intval($capacity);
}
function offsetExists($offset) {
return isset($this->arr[$offset%$this->capacity])&&($offset>$this->current+1);
}
function offsetGet($offset) {
return isset($this->arr[$offset%$this->capacity])?$this->arr[$offset%$this->capacity]:null;
}
function offsetSet($offset, $value) {
if(is_null($offset)) {
$this->arr[$this->current%$this->capacity] = $value;
$this->current++;
} else {
$this->arr[$offset%$this->capacity] = $value;
$this->current = $offset+1;
}
}
function offsetUnset($offset) {
unset($this->arr[$offset%$this->capacity]);
}
}
class CircularQueue {
private $ca;
private $front;
private $rear;
private $capacity;
function __construct($capacity) {
$this->capacity = $capacity;
$this->ca = new CircularArray($this->capacity);
$this->front = -1;
$this->rear = 0;
}
function write($value) {
if($this->front-$this->rear<$this->capacity-1) {
$this->ca[] = $value;
$this->front++;
return true;
} else {
return false;
}
}
function read() {
if($this->rear<=$this->front) {
$this->rear++;
return $this->ca[$this->rear-1];
} else {
return null;
}
}
}
$cq = new CircularQueue(5);
$cq->write('row1');
var_dump($cq->read());
$cq->write('row2');
$cq->write('row3');
var_dump($cq->read());
$cq->write('row4');
var_dump($cq->read());
$cq->write('row5');
$cq->write('row6');
$cq->write('row7');
$cq->write('row8');
$cq->write('row9');
var_dump($cq->read());
var_dump($cq->read());
var_dump($cq->read());
var_dump($cq->read());
var_dump($cq->read());
var_dump($cq->read());
這個例子中,先用ArrayAccess實作一個循環存放內容的陣列,然後利用他實作一個CicularQueue,在consturctor中設定容量,當queue是空的時候讀取會得到null,當寫入循環回到上未讀取的位置,寫入會失敗回傳false。使用一個循環寫入的Array有一個好處,就是queue的指標只需要遞增,不必在超過capacity時歸零,寫起來比較簡單。但是實務上並不能這樣實作,因為...會超過整數的限制,for demo only...
執行結果:
Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ php 1-6a.php
string(4) "row1"
string(4) "row2"
string(4) "row3"
string(4) "row4"
string(4) "row5"
string(4) "row6"
string(4) "row7"
string(4) "row8"
NULL
* Serializable
這個介面定義了兩個方法:
簡單地說,當物件被序列化時,如果有實作這個介面,就會呼叫serialize方法並使用他的回傳值作為序列化的結果。反之,在還原時則會呼叫unserialize方法,並且把序列化的結果傳給這個方法。
PHP的物件序列化後的資訊其實還是可辨識的,如果想做額外處理,例如加解密,就可以使用這個方法。在需要在網路上傳遞序列化的PHP物件時,又要考慮到安全的話,使用這個介面來設計物件,會讓過程更方便。
* Closure
當使用例名函數時,匿名函數物件就是這個類別的實例。他定義了兩個方法,其中一個是靜態方法,不過這兩個方法的作用是一樣的,就是把匿名函數執行的context綁定到另一個物件上。從PHP5.4開始,就可以在匿名函數中使用$this。使用bind/bindTo,就可以改變函數中$this參考的物件。
<?php
class a {
var $name='fillano';
function show($c) {
$f = function(){echo $this->name."\n";};
$f();
$g = $f->bindTo($c);
$g();
}
}
class b {
var $name='hildegard';
}
$a = new a;
$a->show(new b);
執行結果:
Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ php 1-6b.php
fillano
hildegard
在這個例子中,show()裡面使用一個匿名函數來顯示$this->name,但是把它bind到另一個物件時,他會顯示另一個物件的name屬性。不過如果b->name的visibility是設定成private的話,還是會出錯。這個東西跟Javascript的call/apply有點像,只是如果匿名函數定義在物件外,使用$this也會出錯...這樣似乎限制了這些操作方法的使用範圍?可能還需要再多研究一下...