prepare
php 5.6 and php 7.0.12
Using vscode, configure( vscode replaces source insight)
phpstudy is convenient to switch various versions of test code.
CentOS 7 virtual machine, convenient for subsequent use.
The flow chart uses https://www.processon.com
php 7 changes
Abstract syntax tree
php5.x
The PHP code directly generates the ZendVM instruction in the syntax parsing phase. The opline instruction is generated in zend_language_parse.y
Disadvantages: the compiler is coupled with the actuator
php7
Parse the php code into an abstract syntax tree and compile the abstract syntax tree into ZendVM instructions
Advantages: the php compiler is well separated from the executor. The compiler does not need to care about the generation rules of instructions, and then the executor compiles the abstract syntax tree into corresponding instructions according to its own rules.
Native TLS
php 5.x :
Multithreading environment can not be simply realized through global variables. In order to adapt to multithreading application environment.
php provides a thread safe resource manager to isolate global resources from each other, and different threads do not interfere with each other
php 7
Use Native TLS (thread local storage) to save the resource pool of threads, and _tread identifies a global variable. The global variable is exclusive to threads, and the modification of different threads will not affect it
Specify function parameters and return value types
Structural change of zval
php 5.x
zend.h
zval
struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; };
value
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; zend_ast *ast; } zvalue_value;
shortcoming
The reference count of php5.x is in zval instead of value. To copy variables, you need to copy two structures. Zval and value are always bound together
php 7
zend_types.h
zval
struct _zval_struct { zend_value value; /* value */ union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar type, /* active type */ zend_uchar type_flags, zend_uchar const_flags, zend_uchar reserved) /* call info for EX(This) */ } v; uint32_t type_info; } u1; union { uint32_t var_flags; uint32_t next; /* hash collision chain */ uint32_t cache_slot; /* literal cache slot */ uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ uint32_t fe_iter_idx; /* foreach iterator index */ } u2; };
value
typedef union _zend_value { zend_long lval; /* long value */ double dval; /* double value */ zend_refcounted *counted; zend_string *str; zend_array *arr; zend_object *obj; zend_resource *res; zend_reference *ref; zend_ast_ref *ast; zval *zv; void *ptr; zend_class_entry *ce; zend_function *func; struct { uint32_t w1; uint32_t w2; } ww; } zend_value;
advantage:
Reference count in the specific value (element zend_refcounted), zval is only the carrier, and value is the real value
Copying and passing between php variables is more concise and easy to understand
The size of zval structure is reduced from 24 bytes to 16 bytes, which is also an optimization point for php7 to reduce system resources
exception handling
php5.x
Many operations throw an error
php7
Change most errors to exception throwing, so that they can be caught through try catch
Calling an undefined function. Example code:
try { call(); } catch (Throwable $e) { echo $e->getMessage(); }
php5.6: Fatal error: Call to undefined function call() in E:\isoftbox\phpstudy\WWW\1.php on line 4
php7 output: Call to undefined function call()
HashTable
php7, the size of hashtable structure is reduced from 72 bytes to 56 bytes, and the bucket of array element is reduced from 72 bytes to 32 bytes
Composition of php
Composition of php
SAPI layer adapts to different execution scenarios. Common are as follows:
apache2handler cgi cli embed #Embedded fpm litespeed #LiteSpeed is a commercial web server specially designed for large websites. One advantage is that it can directly read Apache configuration information. It can easily combine its existing products to replace Apache. This server is lightweight, as its name implies that it is very fast.
ZendVM
ZendVM consists of two parts: compiler and executor.
Extension
Extensions are divided into PHP extensions and Zend extensions (applied to Zend VM, such as Opcache)
catalogue
build/ ext/ main/ netware/ pear/ sapi/ scripts/ tests/ travis/ TSRM/ win32/ Zend/
life cycle
Module initialization, request initialization, script execution phase, request shutdown phase, module shutdown phase
php declaration cycle
You can see the corresponding function definition in the main/main.c file.
Different sapi scenarios use different methods.
image.png
Take fpm for example
main(),, you can see the call in the file / sapi/fpm/fpm/fpm_main.c.
Module initialization phase
php_module_startup function (. / main/main.c)
Unnamed file (3).jpg
Mainly:
- Activate SAPI: sapi_activate() (the function is defined in. / main/SAPI.c, and then find sapi.c at the beginning of SAPI)
Initialization request information
...... /*Initialization request information*/ SG(sapi_headers).send_default_content_type = 1; /* SG(sapi_headers).http_response_code = 200; */ SG(sapi_headers).http_status_line = NULL; SG(sapi_headers).mimetype = NULL; SG(headers_sent) = 0; ZVAL_UNDEF(&SG(callback_func)); SG(read_post_bytes) = 0; SG(request_info).request_body = NULL; ......
Process requests, read post data, and read cookie s.
/* Handle request method Processing request method*/ if (SG(server_context)) { if (PG(enable_post_data_reading) && SG(request_info).content_type && SG(request_info).request_method && !strcmp(SG(request_info).request_method, "POST")) { /* HTTP POST may contain form data to be processed into variables * depending on given content type * HTTP POST May contain form data to be processed as variables based on a given content type * */ sapi_read_post_data(); } else { SG(request_info).content_type_dup = NULL; } /* Cookies Read cookies*/ SG(request_info).cookie_data = sapi_module.read_cookies(); if (sapi_module.activate) { sapi_module.activate(); } } if (sapi_module.input_filter_init) { sapi_module.input_filter_init(); }
- Start PHP output: php_output_startup().
PHPAPI void php_output_startup(void) { ZEND_INIT_MODULE_GLOBALS(output, php_output_init_globals, NULL); zend_hash_init(&php_output_handler_aliases, 8, NULL, NULL, 1); zend_hash_init(&php_output_handler_conflicts, 8, NULL, NULL, 1); zend_hash_init(&php_output_handler_reverse_conflicts, 8, NULL, reverse_conflict_dtor, 1); php_output_direct = php_output_stdout; }
Analysis reference Learn PHP7 kernel from the factory director (V): system analysis life cycle
- Initialize the garbage collector: gc_globals_ctor() (file. / Zend/zend_gc.c, containing the beginning of gc_, you know), allocate zend_gc_globals memory
ZEND_API void gc_globals_ctor(void) { #ifdef ZTS / * thread safely executes ts_allocate_id. if you continue to trace this function, you can see tsrm_mutex_lock and tsrm_mutex_unlock*/ ts_allocate_id(&gc_globals_id, sizeof(zend_gc_globals), (ts_allocate_ctor) gc_globals_ctor_ex, (ts_allocate_dtor) root_buffer_dtor); #else gc_globals_ctor_ex(&gc_globals); #endif }
More about memory allocation references PHP's new garbage collection mechanism: Zend GC explanation
- Start Zend engine: zend_startup()
...... start_memory_manager(); /*Start memory pool*/ /* Set up utility functions and values Set some util function handles */ zend_error_cb = utility_functions->error_function; zend_printf = utility_functions->printf_function; zend_write = (zend_write_func_t) utility_functions->write_function; zend_fopen = utility_functions->fopen_function; /* Set zend_compile_file and zend_execute_ex function handles of Zend virtual machine compiler and executor */ #if HAVE_DTRACE /* build with dtrace support */ zend_compile_file = dtrace_compile_file; zend_execute_ex = dtrace_execute_ex; zend_execute_internal = dtrace_execute_internal; #else zend_compile_file = compile_file; zend_execute_ex = execute_ex; zend_execute_internal = NULL; #endif /* HAVE_SYS_SDT_H */ zend_compile_string = compile_string; zend_throw_exception_hook = NULL; /* Set up the default garbage collection implementation. Set function handle for garbage collection */ gc_collect_cycles = zend_gc_collect_cycles; /*Allocation function symbol table (CG(function_table)), class symbol table (CG(class_table)), constant symbol table (EG(zend_constants))Etc. This CG is a macro definition # define global_function_table CG (function_table) under non thread safety*/ GLOBAL_FUNCTION_TABLE = (HashTable *) malloc(sizeof(HashTable)); GLOBAL_CLASS_TABLE = (HashTable *) malloc(sizeof(HashTable)); GLOBAL_AUTO_GLOBALS_TABLE = (HashTable *) malloc(sizeof(HashTable)); GLOBAL_CONSTANTS_TABLE = (HashTable *) malloc(sizeof(HashTable)); #ifdef ZTS /*If it is multithreaded, it will also allocate the global variables of the compiler and executor amount*/ ts_allocate_id(&executor_globals_id, sizeof(zend_executor_globals), (ts_allocate_ctor) executor_globals_ctor, (ts_allocate_dtor) executor_globals_dtor); ...... /* Register the Zend core extension. The extension is provided by the kernel. This process will register the functions provided by the Zend core extension, such as strlen, define, func_get_args, class_exists, etc*/ zend_startup_builtin_functions(); /*Register the standard constants defined by Zend: zend_register_standard_constants(), such as E_ERROR E_WARNING,E_ALL,TRUE,FALSE etc.*/ zend_register_standard_constants(); /* Register the get handler of $GLOBALS super global variable */ zend_register_auto_global(zend_string_init("GLOBALS", sizeof("GLOBALS") - 1, 1), 1, php_auto_globals_create_globals); /*Allocate the storage symbol table of php.ini configuration:*/ zend_ini_startup();
- Register php defined constants
REGISTER_MAIN_STRINGL_CONSTANT("PHP_VERSION", PHP_VERSION, sizeof(PHP_VERSION)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_MAJOR_VERSION", PHP_MAJOR_VERSION, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_MINOR_VERSION", PHP_MINOR_VERSION, CONST_PERSISTENT | CONST_CS); ......
- Parsing php.ini: after parsing, all php.ini configurations are saved in the configuration_hash hash table (php_init_config(), in php_ini.c)
if (php_init_config() == FAILURE) { return FAILURE; }
*Map php.ini configuration of PHP and Zend core: obtain the corresponding configuration value according to the parsed php.ini, and The final configuration is inserted into the EG(ini_directives) hash table.
/* Register PHP core ini entries Register php core ini entry*/ REGISTER_INI_ENTRIES();
php_startup_auto_globals();/* Specifically defined in. / main/php_variables.c*/
- Register statically compiled extensions: php_register_internal_extensions_func()
- Register dynamically loaded extensions: php_ini_register_extensions()
- Callback the module starup hook function defined by each extension, that is, the function defined through PHP_MINIT_FUNCTION().
- Disable functions and classes configured by php.ini. The default is disable_functions =, disable_classes =.
php_disable_functions(); php_disable_classes();
Request initialization
int php_request_startup(void)
Request initialization
Main treatment:
- Activate output: php_output_activate()
- Activate Zend engine: zend_activate()
ZEND_API void zend_activate(void) /* {{{ */ { #ifdef ZTS virtual_cwd_activate(); #endif gc_reset();/* Reset garbage collector */ init_compiler(); /* Initialize compiler */ init_executor(); /* Initialize actuator */ startup_scanner();/* Initialize lexical scanner*/ }
- Activate SAPI: sapi_activate() (Note: This is also done during module initialization)
- Callback a request startup hook function defined by extension: zend_activate_modules() (file. / Zend/zend_API.c)
ZEND_API void zend_activate_modules(void) /* {{{ */ { zend_module_entry **p = module_request_startup_handlers; while (*p) { zend_module_entry *module = *p; if (module->request_startup_func(module->type, module->module_number)==FAILURE) { zend_error(E_WARNING, "request_startup() for %s module failed", module->name); exit(1); } p++; } }
Execute script phase
php_execute_script(), including two core stages of PHP code compilation and execution (the most important function of zend engine). In the compilation stage, PHP script goes through the transformation process from PHP source code to abstract syntax tree and then to opline instruction, generating the execution instruction (opline instruction) recognized by zend engine. The instruction is executed by the actuator, and PHP explains the execution process.
Execute script phase
ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...) /* {{{ */ { va_list files; int i; zend_file_handle *file_handle; zend_op_array *op_array; va_start(files, file_count); for (i = 0; i < file_count; i++) { file_handle = va_arg(files, zend_file_handle *); if (!file_handle) { continue; } /* Compile opcodes (lexical and syntactic analysis -)*/ op_array = zend_compile_file(file_handle, type); if (file_handle->opened_path) { zend_hash_add_empty_element(&EG(included_files), file_handle->opened_path); } zend_destroy_file_handle(file_handle); if (op_array) { /* Instruction execution */ zend_execute(op_array, retval); zend_exception_restore(); zend_try_exception_handler(); if (EG(exception)) { zend_exception_error(EG(exception), E_ERROR); } destroy_op_array(op_array); efree_size(op_array, sizeof(zend_op_array)); } else if (type==ZEND_REQUIRE) { va_end(files); return FAILURE; } } va_end(files); return SUCCESS; }
ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type) { zend_lex_state original_lex_state; zend_op_array *op_array = NULL; zend_save_lexical_state(&original_lex_state); if (open_file_for_scanning(file_handle)==FAILURE) { if (type==ZEND_REQUIRE) { zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); zend_bailout(); } else { zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); } } else { zend_bool original_in_compilation = CG(in_compilation); CG(in_compilation) = 1; CG(ast) = NULL; CG(ast_arena) = zend_arena_create(1024 * 32); /* yacc Constantly call re2cc to scan token s to generate abstract syntax trees*/ if (!zendparse()) { zval retval_zv; zend_file_context original_file_context; zend_oparray_context original_oparray_context; zend_op_array *original_active_op_array = CG(active_op_array); op_array = emalloc(sizeof(zend_op_array)); init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE); CG(active_op_array) = op_array; ZVAL_LONG(&retval_zv, 1); if (zend_ast_process) { zend_ast_process(CG(ast)); } zend_file_context_begin(&original_file_context); zend_oparray_context_begin(&original_oparray_context); /* Generate op_array from abstract syntax tree*/ zend_compile_top_stmt(CG(ast)); zend_emit_final_return(&retval_zv); op_array->line_start = 1; op_array->line_end = CG(zend_lineno); pass_two(op_array); zend_oparray_context_end(&original_oparray_context); zend_file_context_end(&original_file_context); CG(active_op_array) = original_active_op_array; } zend_ast_destroy(CG(ast)); zend_arena_destroy(CG(ast_arena)); CG(in_compilation) = original_in_compilation; } zend_restore_lexical_state(&original_lex_state); return op_array; }
Request close phase
php_request_shutdown() This stage will flush the output content, send HTTP response header header, clean up global variables, close the compiler, close the actuator, etc. It also calls back the request shutdown hook function of each extension. This stage is the opposite operation of requesting initialization, which corresponds to the processing in the initialization stage one by one.
Request close phase
/* 1. Call all possible shutdown functions registered with register_shutdown_function() */ if (PG(modules_activated)) zend_try { php_call_shutdown_functions(); } zend_end_try(); /* 2. Call all possible __destruct() functions */ zend_try { zend_call_destructors(); } zend_end_try(); /* 3. Flush all output buffers */ zend_try { zend_bool send_buffer = SG(request_info).headers_only ? 0 : 1; if (CG(unclean_shutdown) && PG(last_error_type) == E_ERROR && (size_t)PG(memory_limit) < zend_memory_usage(1) ) { send_buffer = 0; } if (!send_buffer) { php_output_discard_all(); } else { php_output_end_all(); } } zend_end_try(); /* 4. Reset max_execution_time (no longer executing php code after response sent) */ zend_try { zend_unset_timeout(); } zend_end_try(); /* 5. Call all extensions RSHUTDOWN functions */ if (PG(modules_activated)) { zend_deactivate_modules(); } /* 6. Shutdown output layer (send the set HTTP headers, cleanup output handlers, etc.) */ zend_try { php_output_deactivate(); } zend_end_try(); /* 7. Free shutdown functions */ if (PG(modules_activated)) { php_free_shutdown_functions(); } /* 8. Destroy super-globals */ zend_try { int i; for (i=0; i<NUM_TRACK_VARS; i++) { zval_ptr_dtor(&PG(http_globals)[i]); } } zend_end_try(); /* 9. free request-bound globals */ php_free_request_globals(); /* 10. Shutdown scanner/executor/compiler and restore ini entries */ zend_deactivate(); /* 11. Call all extensions post-RSHUTDOWN functions */ zend_try { zend_post_deactivate_modules(); } zend_end_try(); /* 12. SAPI related shutdown (free stuff) */ zend_try { sapi_deactivate(); } zend_end_try(); /* 13. free virtual CWD memory */ virtual_cwd_deactivate(); /* 14. Destroy stream hashes */ zend_try { php_shutdown_stream_hashes(); } zend_end_try(); /* 15. Free Willy (here be crashes) */ zend_interned_strings_restore(); zend_try { shutdown_memory_manager(CG(unclean_shutdown) || !report_memleaks, 0); } zend_end_try(); /* 16. Reset max_execution_time */ zend_try { zend_unset_timeout(); } zend_end_try();
Module shutdown phase
php_module_shutdown()
This stage corresponds to the module initialization stage, which is mainly used to clean up resources, close php modules, and call back the extended module shutdown hook function.
php_module_shutdown()
sapi_flush(); zend_shutdown();/* Cleaning up persistent symbol tables */ /* Destroys filter & transport registries too */ php_shutdown_stream_wrappers(module_number); UNREGISTER_INI_ENTRIES(); /* Cleaning up ini hashTable elements*/ /* close down the ini config */ php_shutdown_config(); #ifndef ZTS zend_ini_shutdown(); shutdown_memory_manager(CG(unclean_shutdown), 1); #else zend_ini_global_shutdown(); /* Destroy EG(ini_directive)*/ #endif php_output_shutdown(); /* Close output */ module_initialized = 0; #ifndef ZTS core_globals_dtor(&core_globals); /*Release PG*/ gc_globals_dtor(); #else ts_free_id(core_globals_id); #endif
reference material:
- Analysis of php kernel Qin Ming
- TIPI insight into php kernel http://www.php-internals.com
- vscode replaces source insight https://blog.csdn.net/dtw11502/article/details/80798167
- <__declspec(dllexport) & __declspec(dllimport)>https://www.cnblogs.com/xd502djj/archive/2010/09/21/1832493.html
- Implementing Opcodes viewer with PHP embedded SAPI http://www.laruence.com/2008/09/23/539.html
- Learning PHP7 kernel from factory director (V): system analysis life cycle https://www.cnblogs.com/enochzzg/p/9595417.html
- New garbage collection mechanism for PHP: Zend GC explanation https://www.cnblogs.com/orlion/p/5350844.html *CG and EG of PHP7 source code analysis https://www.bo56.com/php7%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E4%B9%8Bcg%E5%92%8Ceg