iT邦幫忙

DAY 7
3

逐步提昇PHP技術能力系列 第 7

逐步提昇PHP技術能力 - PHP的語言特性 : PHP內建的interface與class

  • 分享至 

  • xImage
  •  

PHP定義了幾個內建的interface與class,這些interface與class主要是用來跟語法搭配使用的,同時也是SPL(Standard PHP Library)的基礎之一。
參考:
* PHP: Predefined Interfaces and Classes - Manual

除了需要操作或是實作某些跟語法相關的功能,或是理解SPL的體系(透過繼承與介面實作),平常大概不會注意到這些東西。不過理解一下對於深入這個語言還是有一些幫助。

先列出內建的interface:

  1. Traversable
  2. Iterator
  3. IteratorAggregate
  4. ArrayAccess
  5. Serialzable
  6. Closure
  7. Generator

如果用繼承的關係來看:

  1. Traversable
    1.1 Iterator
    1.1.1 Generator
    1.2 IteratorAggregate
  2. ArrayAccess
  3. Serializable
  4. Closure

這幾個interface跟PHP的關係是什麼?稍微整理一下:

* Traversable

所有可以透過foreach來做iterate的東西,都是Traversable的子孫,例如之前試過的Iterator以及Generator。在SPL中還實作了許多Iterator,就不一一介紹了。

* ArrayAccess

實作了ArrayAccess的物件,就可以用Array的方式來操作。他定義了幾個介面:

  1. boolean offsetExists(mixed $offset)
  2. mixed offsetGet(mixed $offset)
  3. void offsetSet(mixed $offset, mixed $value)
  4. void offsetUnset(mixed $offset)

只要定義了這幾個介面,就可以用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時歸零,寫起來比較簡單。但是實務上並不能這樣實作,因為...會超過整數的限制XD,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

這個介面定義了兩個方法:

  1. serialize()
  2. unserialize($data)

簡單地說,當物件被序列化時,如果有實作這個介面,就會呼叫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的話,還是會出錯XD。這個東西跟Javascript的call/apply有點像,只是如果匿名函數定義在物件外,使用$this也會出錯...這樣似乎限制了這些操作方法的使用範圍?可能還需要再多研究一下...


上一篇
逐步提昇PHP技術能力 - PHP的語言特性 : Namespaces 與 Class Autoloading
下一篇
逐步提昇PHP技術能力 - PHP的語言特性 : 型別 / Type Juggling / Type Hint
系列文
逐步提昇PHP技術能力30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言