Zend Engine изнутри Дмитрий Стогов
Немного истории Zend Engine была разработана в качестве ядра для PHP 4 Andi Gutmans и Zeev Suraski в 1999 PHP 5.0 основан на Zend Engine 2 с новой объектной моделью PHP 5.1 основан на Zend Engine 2.1 со специализированной VM PHP 5.2 основан на Zend Engine 2.2 с новым менеджером памяти PHP 5.3 основан на Zend Engine 2.3 которая включает большинство улучшений и нововведений из PHP6, за исключением Unicode, (namespace-ы, сборщик мусора, LSB, оператор goto, ленивая инициализация таблиц символов, новый сканнер основанный на re2c) PHP 6 основан на Zend Engine 3 с поддержкой Unicode
Подсистемы ZE Менеджер памяти API для доступа к внутренним структурам данных Компилятор PHP Виртуальная машина PHP API для ресурсов (файлы, DB коннекшены) API для внешних расширений PHP Набор внутренних функций Сборщик мусора (5.3)
Стадии работы PHP
Thread Safe Resource Manager non-ZTS-build (single-thread) ZTS-build (thread-safe) Каждый thread работает со своими глобальными данными ZE использует compiler_globals (CG) и executor_globals (EG) Любое расширение PHP может определить свои глобальные данные, которые должны быть уникальными для разных thread-ов
TSRM макросы void some_function(void) { process(EG(symbol_table)); // compilation error }
TSRM макросы void some_function(void) { int some_local_variable; TSRMLS_FETCH(); process(EG(symbol_table)); } void some_function(TSRMLS_D) { process(EG(symbol_table)); } some_function(TSRMLS_C); void some_function(int some_paremeter TSRMLS_DC) { process(EG(symbol_table)); } some_function(0 TSRMLS_CC);
Менеджер памяти emalloc() efree() erealloc() estrdup() estrndup() ecalloc() $ USE_ZEND_ALLOC=0 valgrind php test.php
Значения (zval-коетейнер) typedef struct _zval_struct { zend_uchar type; zvalue_value value; zend_uchar is_ref; zebd_uint refcount; } zval;
Значения (zval-коетейнер) typedef struct _zval_struct { zend_uchar type; zvalue_value value; zend_uchar is_ref; zebd_uint refcount; } zval; IS_NULL IS_LONG IS_DOUBLE IS_BOOL IS_ARRAY IS_OBJECT IS_STRING IS_RESOURCE IS_CONSTANT IS_CONSTANT_ARRAY
Значения (zval-коетейнер) typedef struct _zval_struct { zend_uchar type; zvalue_value value; zend_uchar is_ref; zebd_uint refcount; } zval; typedef union _zvalue_value { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } zvalue_value;
Ссылки <?php $a = 10; $b =& $b; $a $b … EG(symbol_table) type value is_ref refcount IS_LONG zval HashTable
Присваивание и копирование при записи <?php $a = 1; $b = $a; $b = 2; $b =& $a; $b = 3; $b =& $c; // $a = {1, refcount=1, is_ref=0} // $a = {1, refcount=2, is_ref=0} // $b = {1, refcount=2, is_ref=0} // $a = {1, refcount=1, is_ref=0} // $b = {2, refcount=1, is_ref=0} // $a = {1, refcount=2, is_ref=1} // $b = {1, refcount=2, is_ref=1} // $a = {3, refcount=2, is_ref=1} // $b = {3, refcount=2, is_ref=1} // $a = {3, refcount=1, is_ref=0} // $b = {?, refcount=?, is_ref=1}
Компилятор Основан на flex/bison based (основан на re2c/bison начиная с 5.3) Однопроходная компиляция (на самом деле два прохода) – AST не создается – Прямая компиляция в байт-кодVM – Быстрая компиляция – Оптимизация практически не выполняется
Глобальные данные компилятора (CG) struct _zend_compiler_globals { … HashTable *function_table; HashTable *class_table; zend_class_entry *active_class_entry; zend_op_array *active_op_array; … }; CG(function_table)
Функции PHP (op_array) typedef struct _zend_op_array { zend_uchar type; char *function_name; zend_class_entry *scope; zend_uint fn_flags; zend_op *opcodes; zend_compiled_variables *vars; zend_uint last, lat_var, T; HashTable *static_variables; … } zend_op_array;
Инструкции VM (zend_op) typedef struct _zend_op { zend_uchar opcode; ulong extended_value; znode op1; znode op2; znode result; uint lineno; opcode_handler_t handler; } zend_op;
Инструкции VM (zend_op) typedef struct _zend_op { zend_uchar opcode; ulong extended_value; znode op1; znode op2; znode result; uint lineno; opcode_handler_t handler; } zend_op; ZEND_NOP ZEND_ADD ZEND_SUB ZEND_IS_EQUAL ZEND_JMP ZEND_JMPZ ZEND_ASSIGN ZEND_DO_FCALL ZEND_RETURN ~150 opcodes in zend_vm_opcodes.h
Инструкции VM (zend_op) typedef struct _zend_op { zend_uchar opcode; ulong extended_value; znode op1; znode op2; znode result; uint lineno; opcode_handler_t handler; } zend_op; typedef struct _znode { int op_type; union { zval constant; zend_uint var; zend_uint opline_num; zend_op *jmp_addr; struct { zend_uint var; zend_uint type; } EA; } u; } znode;
Операнды (znode) typedef struct _znode { int op_type; union { zval constant; zend_uint var; zend_uint opline_num; zend_op *jmp_addr; struct { zend_uint var; zend_uint type; } EA; } u; } znode; IS_CONST IS_CV IS_TMP_VAR IS_VAR IS_UNUSED
Пример компиляции (5.0) <?php $a = “Hello”; $b = “World”; echo $a.” “. $b; ?> // FETCH_WC(“a”)-> V(0) // ASSIGNV(0), C(“Hello”) // FETCH_WC(“b”)-> V(1) // ASSIGNV(1), C(“World”) // FETCH_RC(“a”)-> V(2) // CONCATV(2), C(“ “)-> T(3) // FETCH_RC(“b”)-> V(4) // CONCATT(3), V(4)-> T(5) // ECHOT(5) // RETURNC(NULL)
Пример компиляции (5.1) <?php $a = “Hello”; $b = “World”; echo $a.” “. $b; ?> // // ASSIGNCV(0)[“a”], C(“Hello”) // // ASSIGNCV(1)[“b”], C(“World”) // // CONCATCV(0)[“a”], C(“ “)-> T(0) // CONCATT(0), CV(1)[“b”]-> T(1) // ECHOT(1) // // RETURNC(NULL)
Глобальные данные VM (EG) struct _zend_executor_globals { … HashTable *active_symbol_table; HashTable symbol_table;// $GLOBALS[] HashTable *function_table; HashTable *class_table; HashTable *zend_constants; zval *This; zend_class_entry *scope; zend_op_array *active_op_array; zend_op **opline_ptr; struct _zend_execute_data *current_execute_data; … }; EG(symbol_table)
Switch-threaded Executor (4.*) void execute(zend_op_array *op_array TSRMLS_DC) { zend_execute_data execute_data; // initialization EX(opline) = op_array->opcodes; while (1) { switch (EX(opline)->opcode) { … case ZEND_RETURN; … return; }
Call-threaded Executor (5.*) void execute(zend_op_array *op_array TSRMLS_DC) { zend_execute_data execute_data; // initialization EX(opline) = op_array->opcodes; while (1) { if (EX(opline)->handler(&execute_data TSRMLS_CC)) { return; }
Call-threaded Executor (5.0) int zend_concat_handler(ZEND_OPCODE_HANDLER_ARGS) { zend_op *opline = EX(opline); concat_function(&EX_T(opline->result.u.var).tmp_var, get_zval_ptr(&opline->op1, EX(Ts), &EG(free_op1), BP_VAR_R), get_zval_ptr(&opline->op2, EX(Ts), &EG(free_op2), BP_VAR_R)); FREE_OP1(EX(Ts), &opline->op1, EG(free_op1)); FREE_OP2(EX(Ts), &opline->op2, EG(free_op2)); EX(opline)++; return 0; }
Специализация в VM (5.1) ZEND_VM_HANDLER(8, ZEND_CONCAT, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV) { zend_op *opline = EX(opline); zend_free_op free_op1, free_op2; concat_function(&EX_T(opline->result.u.var).tmp_var, GET_OP1_ZVAL_PTR(BP_VAR_R), GET_OP2_ZVAL_PTR(BP_VAR_R)); FREE_OP1(); FREE_OP2(); EX(opline)++; return 0; }
Специализация в VM (5.1) int ZEND_CONCAT_SPEC_CV_CONST_HANDLER( ZEND_OPCODE_HANDLER_ARGS) { zend_op *opline = EX(opline); concat_function(&EX_T(opline->result.u.var).tmp_var, _get_zval_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R), &opline->op2.u.constant); EX(opline)++; return 0; }
Классы typrdef struct _zend_class_entry { char type; char *name; zend_class_entry *parent; zend_uint ce_flags; HashTable function_table; HashTable default_properties; HashTable properties_info; HashTable *static_members; HashTable constants_table; zend_class_entry **interfaces; … } zend_class_entry;
Объекты typedef struct _zend_object_value { zend_uint handle;//Z_OBJ_HANDLE(zval) zend_object_handlers *handlers;//Z_OBJ_HT(zval) } zend_object_value; typedef struct _zend_object { zend_class_entry *ce;//Z_OBJCE(zval) HashTable *properties;//Z_OBJPROP(zval) } zend_object;
Вопросы?