iT邦幫忙

DAY 2
8

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

逐步提昇PHP技術能力 - PHP的語言特性 : 多載 (overloading)

印象中PHP是從PHP4開始有OOP的特性,但是這些要到PHP5才夠完整。基本上在PHP5,類別(class)、介面(interface)、繼承(inheritance: extends/implements)、可視性(visibility)、覆載(overriding)、建構式(constructor)、解構式(destructor)等概念跟一般的Classical OOP大同小異,比較有不同的是多載(overloading)。

多載是實作多型的一種方式,以Java為例,某個class可能會有數種同名的方法,但是會有不同型別或數量的參數作為識別。當傳遞的參數型別符合時,就可以成功呼叫這個方法。不過在PHP中,多載長得不太一樣XD
參考:PHP:Overloading - Manual

PHP把多載定義為「動態」定義的方法或屬性...這跟Java的method overloading的概念不太一樣,所以如果用Java的概念來理解,就會有很大的誤會(我就犯過這樣的錯誤)。

在PHP中,有幾個**魔術方法(magic methods)**跟多載相關,包括:

*使用在屬性多載的魔術方法

  1. __set($name, $value):如果想要寫入物件無法存取的屬性,就會觸發這個方法
  2. __get($name):如果想要讀取物件無法存取的屬性,就會觸發這個方法
  3. __isset($name):如果想要用isset()或是empty()判斷物件所無法存取的屬性是否存在或為空,就會觸發這個方法
  4. __unset($name):如果想要用unset()來清除物件無法存取的屬性,就會觸發這個方法

用一個最簡單的例子來看:

<?php
class a {
  function __get($name) {
    if($name==='var1') return 'abc';
  }
}

$a = new a;
echo $a->var1."\n";
echo $a->var2."\n";

class b {
  private $var1 = 'abc';
  private $var2 = 'def';
  function __get($name) {
    switch($name) {
      case 'var1': 
        return $this->var1;
        break;
      case 'var2':
        return 'ghi';
        break;
    }
  }
}

$b = new b;
echo $b->var1."\n";
echo $b->var2."\n";

class c {}

$c = new c;
echo $c->var1."\n";

執行結果是:

Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ php 1-1a.php
abc

abc
ghi
PHP Notice:  Undefined property: c::$var1 in /Users/fillano/builds/ironman6/1-1a.php on line 34

可以看出,只要是物件未定義或是無法存取的屬性,在讀取時就會會觸發這個方法。如果連這個方法都沒定義,就只好報錯。__set()、__isset()、__unset()都是類似的作用。

*使用在方法多載的魔術方法

  1. __call($name, $args) :呼叫了物件未定義或無法存取的方法時觸發,$name是呼叫的方法名,$args是參數陣列
  2. __callStatic($name, $args) (php5.3~) :呼叫了物件未定義或無法存取的靜態方法時觸發,$name是呼叫的方法名,$args是參數陣列

簡單的例子:

<?php
class a {
  function __call($name, $args) {
    echo $name . " : " . print_r($args, true) . "\n";
  }
}

$a = new a;
$a->func1('abc', 'def');

class b {
  function __call($name, $args) {
    switch($name) {
      case 'add':
        if(count($args)===2) {
          if(is_numeric($args[0]) && is_numeric($args[1]))
            return $args[0]+$args[1];
          if(is_string($args[0]) && is_string($args[1]))
            return $args[0].$args[1];
        }
      default:
        throw new Exception("[warning] b::$name method not found.\n");
    }
  }
}

$b = new b;
echo $b->add(2,3)."\n";
echo $b->add('hello', ' world.')."\n";
try {
  echo $b->add(2, ' world.')."\n";  
}
catch (Exception $e) {
  echo $e->getMessage();
}

執行結果:

Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ php 1-1b.php
func1 : Array
(
    [0] => abc
    [1] => def
)

5
hello world.
[warning] b::add method not found.

這裡模擬了參數型別來判斷處理方式的多載,傳入兩個數字給add時,他會相加,傳入兩個字串時,會把字串接起來。但是傳給一個數字及一個字串,就會出現警告:找不到方法。

__callStatic跟__call類似,只是作用在使用::呼叫靜態方法時,這是從PHP5.3才有的魔術方法。

*用途???

PHP的多載,可以讓需要動態產生大量不同屬性或方法的物件時,處理更簡單。例如要開發ORM,使用物件對應到不同資料表的列時,我們實際上並不需要根據不同資料表的不同欄位來定義不同的類別,只要利用PHP的多載...然後在__get()、__call()等方法裡面檢查查詢的結果,並根據傳入的參數判斷要回傳的資料,操作起來感覺就好像不同的類別實例。(感謝大神的教導)

但是如果不是可以發揮這類集中處理的長處時,恐怕就需要評估一下了。因為這樣反而會讓一個方法太複雜,比較難維護與理解。

總之,PHP的多載跟別人不太一樣,但是用對地方的話就很對頭。


上一篇
逐步提昇PHP技術能力 - 緣起
下一篇
逐步提昇PHP技術能力 - PHP的語言特性 : 匿名函數 / Closure / Callable
系列文
逐步提昇PHP技術能力30

1 則留言

我要留言

立即登入留言