在前幾節中我們已深入探討 zval 結構體,以及函數的定義與參數解析。本節將聚焦於 PHP 擴展開發的關鍵環節——函數回傳值。由於 C 語言擴展中的函數回傳類型均為 zval*,因此理解 return_value 的運作機制至關重要。
需求:編寫 calculate(num1, num2, opt) 函數,支援加減乘除四則運算,其中 opt 為運算子字元(+、-、*、/)。
PHP_FUNCTION(calculate)
{
long num1;
long num2;
char *opt;
int opt_len;
// 解析參數:兩個長整數 + 字串
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lls", &num1, &num2, &opt, &opt_len) == FAILURE) {
php_printf("param error. example: calculate(123, 456, '+')\n");
RETURN_NULL();
}
// 驗證運算子長度必須為 1
if (1 != opt_len) {
php_printf("param error. example: calculate(123, 456, '+')\n");
RETURN_NULL();
}
// 根據運算子執行對應運算
switch (opt[0]) {
case '+':
return_value->type = IS_LONG;
return_value->value.lval = num1 + num2;
break;
case '-':
return_value->type = IS_LONG;
return_value->value.lval = num1 - num2;
break;
case '*':
return_value->type = IS_LONG;
return_value->value.lval = num1 * num2;
break;
case '/':
return_value->type = IS_DOUBLE;
return_value->value.dval = num1 * 1.0 / num2; // 注意:除法回傳 double
break;
default:
return_value->type = IS_LONG;
return_value->value.lval = 0;
break;
}
}
許多開發者初見此代碼會疑惑:return_value 變數未聲明卻可直接使用,這是因為它由 PHP_FUNCTION 宏自動聲明。讓我們層層剖析宏定義:
#define ZEND_NAMED_FUNCTION(name) \
void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FUNCTION(name) \
ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define INTERNAL_FUNCTION_PARAMETERS \
int ht, \
zval *return_value, \
zval **return_value_ptr, \
zval *this_ptr, \
int return_value_used \
TSRMLS_DC
展開後,PHP_FUNCTION(calculate) 實際簽名為:
void zif_calculate(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC)
結論:return_value 是 Zend 引擎預先分配的 zval* 指標,指向一個初始化為 NULL 的 zval 結構體,專門用於承載函數回傳值。
手動設定 type 與 value 雖然可行,但 Zend 提供了更便利的宏:
| 宏名稱 | 用途 | 範例 |
|---|---|---|
RETVAL_LONG(l) |
回傳長整數 | RETVAL_LONG(42) |
RETVAL_DOUBLE(d) |
回傳雙精度浮點數 | RETVAL_DOUBLE(3.14) |
RETVAL_STRING(s, duplicate) |
回傳字串 | RETVAL_STRING("hello", 1) |
RETVAL_BOOL(b) |
回傳布林值 | RETVAL_BOOL(1) |
RETVAL_NULL() |
回傳 NULL | RETVAL_NULL() |
對應的 RETURN_* 宏則包含 return 語句,直接結束函數執行:
#define RETURN_LONG(l) { RETVAL_LONG(l); return; }
#define RETURN_STRING(s, duplicate) { RETVAL_STRING(s, duplicate); return; }
PHP_FUNCTION(calculate)
{
long num1, num2;
char *opt;
int opt_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lls", &num1, &num2, &opt, &opt_len) == FAILURE) {
RETURN_NULL();
}
if (opt_len != 1) {
RETURN_NULL();
}
switch (opt[0]) {
case '+': RETURN_LONG(num1 + num2);
case '-': RETURN_LONG(num1 - num2);
case '*': RETURN_LONG(num1 * num2);
case '/': RETURN_DOUBLE(num1 * 1.0 / num2);
default: RETURN_LONG(0);
}
}
type 與 value.lval/dval/str 等欄位RETVAL_* / RETURN_* 宏更安全、可讀性更高掌握回傳值機制,是編寫高效能 PHP 擴展的基石。下一節我們將探討陣列操作與 HashTable 內部實現。