1. Quá trình định nghĩa hàm
Hàm người dùng được bắt đầu bằng từ khóa function. Ví dụ:
function bar($param) {
echo $param;
}
a. Phân tích từ vựng
Trong file Zend/zend_language_scanner.l, dòng mã sau xác định từ khóa function:
<ST_IN_SCRIPTING>"function" {
return T_FUNCTION;
}
Ký hiệu T_FUNCTION được tạo ra khi gặp từ khóa này, bắt đầu quá trình phân tích cú pháp.
b. Phân tích cú pháp
Trong file Zend/zend_language_parser.y, cấu trúc khai báo hàm được mô tả như sau:
function:
T_FUNCTION { $$.u.opline_num = CG(zend_lineno); }
;
is_reference:
/* empty */ { $$.op_type = ZEND_RETURN_VAL; }
| '&' { $$.op_type = ZEND_RETURN_REF; }
;
c. Tạo mã trung gian
Hàm zend_do_begin_function_declaration trong file Zend/zend_compile.c chịu trách nhiệm biên dịch:
void zend_do_begin_function_declaration(...) {
opline->opcode = ZEND_DECLARE_FUNCTION;
// Xử lý tên hàm và tham số
}
Tên hàm luôn được chuyển về chữ thường, chứng minh qua ví dụ:
function T() { echo 1; }
function t() { echo 2; } // Lỗi trùng tên
d. Thực thi mã trung gian
Hàm do_bind_function trong file Zend/zend_vm_execute.h kiểm tra trùng lặp tên hàm:
do_bind_function(EX(opline), EG(function_table), 0);
2. Xử lý tham số hàm
a. Tham số hàm người dùng
Thông tin tham số được lưu trong cấu trúc zend_arg_info:
typedef struct _zend_arg_info {
const char *name; // Tên tham số
zend_bool pass_by_reference; // Truyền tham chiếu?
zend_bool allow_null; // Cho phép NULL?
} zend_arg_info;
Ví dụ xử lý tham số:
CG(active_op_array)->num_args++;
currentArgInfo = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-1];
b. Tham số hàm nội bộ
Hàm count minh họa xử lý tham số:
PHP_FUNCTION(count) {
zval *input;
long mode = COUNT_NORMAL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &input, &mode) == FAILURE) {
return;
}
}
Hàm zend_parse_parameters tự động chuyển đổi kiểu dữ liệu đầu vào.
3. Xử lý giá trị trả về
Câu lệnh return được xử lý qua hàm zend_do_return:
void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) {
opline->opcode = ZEND_RETURN;
if (expr) {
opline->op1 = *expr;
} else {
opline->op1.op_type = IS_CONST;
}
}
Giá trị trả về được lưu trong *EG(return_value_ptr_ptr). Ví dụ xử lý trả về:
static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(...) {
if (EG(active_op_array)->return_reference == ZEND_RETURN_REF) {
// Xử lý trả về tham chiếu
} else {
// Xử lý trả về giá trị
}
return zend_leave_helper_SPEC(...);
}
Hàm zend_leave_helper_SPEC dọn dẹp biến sau khi trả về.