php是弱型別語言,向方法傳遞引數時候也並不嚴格檢查資料型別。不過有時需要判斷傳遞到方法中的引數,為此php中提供了一些函式,來判斷資料的型別。比如is_numeric(),判斷是否是乙個數值或者可轉換為數值的字串,比如用於判斷物件的型別運算子:instanceof。 instanceof 用來測定乙個給定的物件是否來自指定的物件類。instanceof 運算子是 php 5 引進的。在此之前是使用的is_a(),不過現在已經不推薦使用。
為了避免物件型別不規範引起的問題,php5中引入了型別提示這個概念。在定義方法引數時,同時定義引數的物件型別。如果在呼叫的時候,傳入引數的型別與定義的引數型別不符,則會報錯。這樣就可以過濾物件的型別,或者說保證了資料的安全性。
php中的型別提示功能只能用於引數為物件的提示,而無法用於為整數,字串,浮點等型別提示。在php5.1之後,php支援對陣列的型別提示。
要使用型別提示,只要在方法(或函式)的物件型引數前加乙個已存在的類的名稱,當使用型別提示時,你不僅可以指定物件型別,還可以指定抽象類和介面。
乙個陣列的型別提示示例:
function
array_print
(array $arr)
array_print(1);
以上的這段**有一點問題,它觸發了我們這次所介紹的型別提示,這段**在php5.1之後的版本執行,會報錯如下:
catchable fatal error: argument 1 passed to array_print() must be an array,
integer given, called in
...
當我們把函式引數中的整形變數變為陣列時,程式會正常執行,呼叫print_r函式輸出陣列。那麼這個型別提示是如何實現的呢?不管是在類中的方法,還是我們呼叫的函式,都是使用function關鍵字作為其宣告的標記,而型別提示的實現是與函式的宣告相關的,在宣告時就已經確定了引數的型別是哪些,但是需要在呼叫時才會顯示出來。這裡,我們從兩個方面說明型別提示的實現:
引數宣告時的型別提示
函式或方法呼叫時的型別提示
將剛才的那個例子修改一下:
function
array_print
(array $arr = 1)
array_print(array(1));
這段**與前面的那個示例相比,函式的引數設定了乙個預設值,但是這個預設值是乙個整形變數,它與引數給定的型別提示array不一樣,因此,當我們執行這段**時會很快看到程式會報錯如下:
fatal error: default value
for parameters with
array
type
hint
can only be an array
or null
為什麼為很快看到報錯呢?因為預設值的檢測過程發生在中間**生成階段,與執行時的報錯不同,它還沒有生成中間**,也沒有執行中間**的過程。在zend/zend_language_parser.y檔案中,我們找到函式的引數列表在編譯時都會呼叫zend_do_receive_arg函式。而在這個函式的引數列表中,第5個引數( znode *class_type)與我們這節所要表述的型別提示密切相關。這個引數的作用是宣告型別提示中的型別,這裡的型別有三種:
空,即沒有型別提示
類名,使用者定義或php自定義的類、介面等
陣列,編譯期間對應的token是t_array,即array字串
在zend_do_receive_arg函式中,針對class_type引數做了一系列的操作,基本上是針對上面列出的三種型別,其中對於類名,程式並沒有判斷這個類是否存在,即使你使用了乙個不存在的類名,程式在報錯時,顯示的也會是實參所給的物件並不是給定類的例項。
以上是宣告型別提示的過程以及在宣告過程中對引數預設值的判斷過程,下面我們看下在函式或方法呼叫時型別提示的實現。
從上面的宣告過程我們知道php在編譯型別提示的相關**時呼叫的是zend/zend_complie.c檔案中的zend_do_receive_arg函式,在這個函式中將型別提示的判斷的opcode被賦值為zend_recv。根據opcode的對映計算規則得出其在執行時呼叫的是zend_recv_spec_handler。其**如下:
static int zend_fastcall zend_recv_spec_handler(zend_opcode_handler_args)
...//省略
} else
...//省略
}
如上所示:在zend_recv_spec_handler中最後呼叫的是zend_verify_arg_type。其**如下:
static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval *arg, ulong fetch_type tsrmls_dc)
if (z_type_p(arg) == is_object)
} else
if(z_type_p(arg) != is_null || !cur_arg_info->allow_null)
} else
if(cur_arg_info->array_type_hint)
if(z_type_p(arg) != is_array && (z_type_p(arg) != is_null || !cur_arg_info->allow_null))
}return 1;
}
zend_verify_arg_type的整個流程如圖所示:
如果型別提示報錯,zend_verify_arg_type函式最後都會呼叫 zend_verify_arg_class_kind 生成報錯資訊,並且呼叫 zend_verify_arg_error 報錯。如下所示**:
static inline char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, ulong fetch_type, const char **class_name, zend_class_entry **pce tsrmls_dc)
else
}static inline int zend_verify_arg_error(const zend_function *zf, zend_uint arg_num, const zend_arg_info *cur_arg_info, const char *need_msg, const char *need_kind, const char *given_msg, char *given_kind tsrmls_dc)
else
if (ptr && ptr->op_array) else
return
0;}
在上面的**中,我們可以找到前面的報錯資訊中的一些關鍵字argument、 passed to、called in等。這就是我們在呼叫函式或方法時型別提示顯示錯誤資訊的最終執行位置。
明天我們講一下變數的生命週期。 第三章 第五節
pop ax 指令的執行過程 1 將ss sp指向的記憶體單元處的資料送入ax中。2 sp sp 2,ss sp指向當前棧頂下面的單元,以當前棧頂下面的單元為新的棧頂。注意 1 出棧後,ss sp指向新的棧頂,1000eh,pop操作前的棧頂元素,1000ch處的2266h依然存在,但是,它已經不在...
第三章 第五節 集合
集合是乙個無需不重複的序列,用set來表示 零 建立集合 建立集合的方式有兩種,分別是 和set 下面我們來具體講解一下。形式 形式用來建立乙個非空集合,語法如下 set 下面我們通過例子來看一下具體怎麼使用 例子 一 定義乙個包含字串的集合 name 例子 二 定義乙個包含多種型別的集合 pers...
第三章第五節 相似 仿射 射影變換
引言 除了歐式變換,空間中還有其他的變換,只不過歐式變換是最簡單的變換,他不改變物體的形狀,而其他的變換則會改變物體的外形。和歐式變換相似,其他變換均有類似的矩陣表示。變換性質 相似變換在歐式變換的基礎上多了乙個自由度,為7自由度,允許物體進行均勻縮放。相似變換矩陣tsts t s srt 0t1 ...