型別是一個語言的基本...不過因為PHP是動態語言,所以往往不太會去深究,因為很少出問題。記得幾年前Rasmus Lerdorf來演講,當天晚上的party遊戲,他出的題目就是型別轉換規則。可見對於動態語言來說,自動型別轉換其實是非常重要的規則,有些地方即使不一定合理(像Javascript...)。根據程式的操作,PHP可能會自動轉換使用的型別,這個過程就叫做Type Juggling。
PHP5加入了一個Type Hinting機制,讓開發者可以指定函數的參數型別以及回傳的型別(純量之外)。PHP5其實已經將近十年了,不過還真的沒用過Type Hinting。
參考:
* PHP: Types - Manual
* PHP: Type Juggling - Manual
* PHP: PHP type comparison tables - Manual
* PHP: Type Hinting - Manual
* PHP的型別系統
手冊中把PHP的型別分成幾個類型:
如果開發過PHP的extension,有一個資料結構應該會很熟悉:
typedef struct _zval_struct zval;
......
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value;
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};
與外部Library介接時,通常要做的事情就是把C的型別與zval做轉換。純量主要是用long、double跟str來存放,陣列使用的是HashTable,其他物件則是zend_object_value。型別的資訊,則放在zval.type(_zval_struct.type)。
偽型別並不是真正的型別,通常是用來定義函數參數以及回傳值的一些狀況用的。mixed表示可以有多種型別,number表示可能是integer或float。PHP5.4定義了callable,在此之前是使用callback,來代表傳入的函數或方法。void則代表函數沒有返回值。...代表函數有不定數目的參數。
* Type Juggling
PHP有定義在程式中型別轉換的規則。例如字串轉換成數字:
$a = 1;
$a += "10 is a number."; //$a的值變成11
$a = "total customers: "+$a;//$a的值還是11
$a = "conver to string: ".$a;//變成字串
PHP手冊中對於Type Juggling有相當完整的解說,另外對於變數的比較與邏輯運算,有一個完整的對照表,建議參考一下,我就不多說。
* Type Hinting
從PHP5開始,可以強制函數及方法的參數必須使用的型別。可以用來當做Type Hint的有:Class及Interface名稱、array(PHP5.1)或是callable(PHP5.4),但是不能使用Resource以及純量作為Type Hint。
一個簡單的例子:
<?php
class a {
public $name = 'fillano';
function show() {
echo $this->name."\n";
}
}
class b {
public $name = 'hildegard';
function show() {
echo $this->name."\n";
}
}
function testa(a $a) {
$a->show();
}
testa(new a);
testa(new b);
testa函數使用Type Hint強制定義參數必須是class a的實例,如果丟給他的是class b就會出錯。執行結果:
Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ php 1-7a.php
fillano
PHP Catchable fatal error: Argument 1 passed to testa() must be an instance of a, instance of b given, called in /Users/fillano/builds/ironman6/1-7a.php on line 22 and defined in /Users/fillano/builds/ironman6/1-7a.php on line 17
再來試一下陣列:
<?php
function testarr(array $a) {
echo $a[0]."\n";
}
testarr(array('fillano'));
testarr(array('k'=>'v'));
testarr('string');
執行結果:
Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ php 1-7b.php
fillano
PHP Notice: Undefined offset: 0 in /Users/fillano/builds/ironman6/1-7b.php on line 3
PHP Catchable fatal error: Argument 1 passed to testarr() must be an array, string given, called in /Users/fillano/builds/ironman6/1-7b.php on line 7 and defined in /Users/fillano/builds/ironman6/1-7b.php on line 2
第一次執行傳給函數陣列,可以正常執行。第二次傳給他HashTable,會因為找不到key出現警告,但是第三次傳給他字串,就會出現錯誤。
如果使用PHP5.4,就可以使用callable:
<?php
class a {
var $name = "fillano\n";
function show() {
echo $this->name;
}
}
function testcb(callable $f) {
$f();
}
function show() {echo "fillano\n";}
testcb(function(){echo "fillano\n";});
testcb(array(new a, 'show'));
testcb('show');
testcb('string');
前三次是三種不同的callable的使用方式傳入函數,第四次傳入的字串,PHP找不到同名的函數,就會出錯。執行結果:
Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ phpbrew use php-5.4.20
Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ php 1-7c.php
fillano
fillano
fillano
PHP Catchable fatal error: Argument 1 passed to testcb() must be callable, string given, called in /Users/fillano/builds/ironman6/1-7c.php on line 15 and defined in /Users/fillano/builds/ironman6/1-7c.php on line 8
Catchable fatal error: Argument 1 passed to testcb() must be callable, string given, called in /Users/fillano/builds/ironman6/1-7c.php on line 15 and defined in /Users/fillano/builds/ironman6/1-7c.php on line 8
不過即使定義了Type Hint,如果把預設值設為NULL,那傳給他NULL並不會出錯:
<?php
class a {
}
function testnull(a $a=NULL) {
var_dump($a);
}
testnull(new a);
testnull(NULL);
執行結果:
Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ php 1-7d.php
object(a)#1 (0) {
}
NULL
另外,使用Interface或是Class來定義Type Hint時,傳給他子類別的實例也沒問題:
<?php
interface a {
function show();
}
class b implements a {
var $name = "fillano\n";
function show() {
echo $this->name;
}
}
class c {
function show(a $c) {
$c->show();
}
}
$b = new b;
$c = new c;
$c->show($b);
執行結果:
Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ php 1-7e.php
fillano
使用Type Hint,可以讓物件的關係更明確,也比較不會誤用。在組織比較嚴謹的類別架構時,應該是個不錯的幫手...雖然沒用過
好文,雖然很少用型別提示,不過針對本文想做一個建議,類別中變數宣告的 var 可以拿掉了,或改成 public, var 這是很久很久以前的寫法,現在不需要這樣做了,寫 var 和 public 是一樣的。
也是,var是PHP4的寫法XD,我來改一下,感謝...