Home  >  Article  >  Backend Development  >  Learning the PHP kernel--PHP life cycle

Learning the PHP kernel--PHP life cycle

WBOY
WBOYOriginal
2016-08-08 09:23:50972browse

The beginning of everything: SAPI interface

SAPI (Server Application Programming Interface) refers to the programming interface for specific PHP applications. Just like a PC, no matter which operating system is installed, as long as it meets the PC interface specifications, it can work normally on the PC Running, PHP scripts can be executed in many ways, through a web server, directly from the command line, or embedded in other programs.

Usually, we use web servers such as Apache or Nginx to test PHP scripts, or execute them through a PHP interpreter program on the command line. After the script is executed, the web server responds and the browser displays the response information or displays the content on the standard output of the command line.

We rarely care where the PHP interpreter is. Although executing scripts through a web server and a command-line program looks very different, the workflow is actually the same. The command line parameters are passed to the script to be executed by the PHP interpreter, which is equivalent to requesting a PHP page through the URL. After the script is executed, the response result is returned, but the response result from the command line is displayed on the terminal.

The script execution starts with the implementation of the SAPI interface. It's just that different SAPI interface implementations will complete their specific work. For example, Apache's mod_php SAPI implementation needs to initialize some information obtained from Apache, and return the content to Apache when outputting content. Other SAPI implementations are similar.

The following sections will provide a more in-depth introduction to some common SAPI implementations.

Start and end

After PHP starts executing, it will go through two main phases: the starting phase before processing the request and the ending phase after the request. There are two processes in the beginning phase: the first process is the module initialization phase (MINIT). This process is only performed once during the entire SAPI life cycle (such as the entire life cycle after Apache starts or the entire execution process of the command line program). . The second process is the module activation phase (RINIT), which occurs in the request phase. For example, if a page is requested through a URL, module activation (RINIT request starts) will be performed before each request. For example, if PHP registers some extension modules, the MINIT function of all modules will be called back during the MINIT stage. The module can perform some initialization work at this stage, such as registering constants, defining classes used by the module, etc. When the module is implemented, these callback functions can be implemented through the following macros:

<span>PHP_MINIT_FUNCTION(myphpextension)
{
    </span><span>//</span><span> 注册常量或者类等初始化操作</span><span>return</span><span> SUCCESS; 
}</span>

After the request arrives, PHP initializes the basic environment for executing the script, such as creating an execution environment, including a symbol table that saves the variable names and value contents during PHP running. , as well as the symbol table of all current functions, classes and other information. Then PHP will call the RINIT function of all modules. At this stage, each module can also perform some related operations. The RINIT function of the module is similar to the MINIT callback function:

<span>PHP_RINIT_FUNCTION(myphpextension)
{
    </span><span>//</span><span> 例如记录请求开始时间
    </span><span>//</span><span> 随后在请求结束的时候记录结束时间。这样我们就能够记录下处理请求所花费的时间了</span><span>return</span><span> SUCCESS; 
}</span>

After the request is processed, it enters the end stage. General scripts PHP will enter the end phase when it is executed to the end or by calling the exit() or die() function. Corresponding to the beginning stage, the end stage is also divided into two stages. One is to deactivate the module after the request is completed (RSHUTDOWN, corresponding to RINIT), and the other is to close the module when the SAPI life cycle ends (the web server exits or the command line script is executed and exits). (MSHUTDOWN, corresponding to MINIT).

<span>PHP_RSHUTDOWN_FUNCTION(myphpextension)
{
    </span><span>//</span><span> 例如记录请求结束时间,并把相应的信息写入到日至文件中。</span><span>return</span><span> SUCCESS; 
}</span>

Single-process SAPI life cycle

CLI/CGI mode PHP belongs to the single-process SAPI mode. This type of request is closed after processing the request once. That is to say, it will only go through the following links: Start - Request starts - Request closes - End The SAPI interface implementation completes its life cycle. As shown in the figure below:

Single-process SAPI life cycle

The above figure is very simple and easy to understand. It's just that PHP also does a lot of work between the various stages. Here are some additions:

Startup

Before calling the module initialization of each module, there will be an initialization process, which includes:

  • Initialize several global variables

The initialized global variables here are in most cases Set it to NULL, except for some, such as setting zuf (zend_utility_functions). Taking zuf.printf_function = php_printf as an example, php_printf here will be assigned to zend_printf in the zend_startup function as a global function pointer, and the zend_printf function is usually used as Regular string output is used, such as debug_print_backtrace that displays the program call stack, which prints relevant information.

  • Initialize several constants

The constants here are some of PHP's own constants. These constants are either hard-coded in the program, such as PHP_VERSION, or written in the configuration header file, such as PEAR_EXTENSION_DIR. These are written in config. in the w32.h file.

  • 初始化Zend引擎和核心组件

前面提到的zend_startup函数的作用就是初始化Zend引擎,这里的初始化操作包括内存管理初始化、 全局使用的函数指针初始化(如前面所说的zend_printf等),对PHP源文件进行词法分析、语法分析、 中间代码执行的函数指针的赋值,初始化若干HashTable(比如函数表,常量表等等),为ini文件解析做准备, 为PHP源文件解析做准备,注册内置函数(如strlen、define等),注册标准常量(如E_ALL、TRUE、NULL等)、注册GLOBALS全局变量等。

  • 解析php.ini

php_init_config函数的作用是读取php.ini文件,设置配置参数,加载zend扩展并注册PHP扩展函数。此函数分为如下几步: 初始化参数配置表,调用当前模式下的ini初始化配置,比如CLI模式下,会做如下初始化:

INI_DEFAULT(<span>"</span><span>report_zend_debug</span><span>"</span>, <span>"</span><span>0</span><span>"</span><span>);
INI_DEFAULT(</span><span>"</span><span>display_errors</span><span>"</span>, <span>"</span><span>1</span><span>"</span>);

不过在其它模式下却没有这样的初始化操作。接下来会的各种操作都是查找ini文件:

  1. 判断是否有php_ini_path_override,在CLI模式下可以通过-c参数指定此路径(在php的命令参数中-c表示在指定的路径中查找ini文件)。
  2. 如果没有php_ini_path_override,判断php_ini_ignore是否为非空(忽略php.ini配置,这里也就CLI模式下有用,使用-n参数)。
  3. 如果不忽略ini配置,则开始处理php_ini_search_path(查找ini文件的路径),这些路径包括CWD(当前路径,不过这种不适用CLI模式)、 执行脚本所在目录、环境变量PATH和PHPRC和配置文件中的PHP_CONFIG_FILE_PATH的值。
  4. 在准备完查找路径后,PHP会判断现在的ini路径(php_ini_file_name)是否为文件和是否可打开。 如果这里ini路径是文件并且可打开,则会使用此文件, 也就是CLI模式下通过-c参数指定的ini文件的优先级是最高的, 其次是PHPRC指定的文件,第三是在搜索路径中查找php-%sapi-module-name%.ini文件(如CLI模式下应该是查找php-cli.ini文件), 最后才是搜索路径中查找php.ini文件。
  • 全局操作函数的初始化

php_startup_auto_globals函数会初始化在用户空间所使用频率很高的一些全局变量,如:$_GET、$_POST、$_FILES等。 这里只是初始化,所调用的zend_register_auto_global函数也只是将这些变量名添加到CG(auto_globals)这个变量表。

php_startup_sapi_content_types函数用来初始化SAPI对于不同类型内容的处理函数, 这里的处理函数包括POST数据默认处理函数、默认数据处理函数等。

  • 初始化静态构建的模块和共享模块(MINIT)

php_register_internal_extensions_func函数用来注册静态构建的模块,也就是默认加载的模块, 我们可以将其认为内置模块。在PHP5.3.0版本中内置的模块包括PHP标准扩展模块(/ext/standard/目录, 这里是我们用的最频繁的函数,比如字符串函数,数学函数,数组操作函数等等),日历扩展模块、FTP扩展模块、 session扩展模块等。这些内置模块并不是一成不变的,在不同的PHP模板中,由于不同时间的需求或其它影响因素会导致这些默认加载的模块会变化, 比如从代码中我们就可以看到mysql、xml等扩展模块曾经或将来会作为内置模块出现。

模块初始化会执行两个操作: 1. 将这些模块注册到已注册模块列表(module_registry),如果注册的模块已经注册过了,PHP会报Module XXX already loaded的错误。 1. 将每个模块中包含的函数注册到函数表( CG(function_table) ),如果函数无法添加,则会报 Unable to register functions, unable to load。

在注册了静态构建的模块后,PHP会注册附加的模块,不同的模式下可以加载不同的模块集,比如在CLI模式下是没有这些附加的模块的。

在内置模块和附加模块后,接下来是注册通过共享对象(比如DLL)和php.ini文件灵活配置的扩展。

在所有的模块都注册后,PHP会马上执行模块初始化操作(zend_startup_modules)。 它的整个过程就是依次遍历每个模块,调用每个模块的模块初始化函数, 也就是在本小节前面所说的用宏PHP_MINIT_FUNCTION包含的内容。

  • 禁用函数和类

php_disable_functions函数用来禁用PHP的一些函数。这些被禁用的函数来自PHP的配置文件的disable_functions变量。 其禁用的过程是调用zend_disable_function函数将指定的函数名从CG(function_table)函数表中删除。

php_disable_classes函数用来禁用PHP的一些类。这些被禁用的类来自PHP的配置文件的disable_classes变量。 其禁用的过程是调用zend_disable_class函数将指定的类名从CG(class_table)类表中删除。

ACTIVATION

After processing the file-related content, PHP will call php_request_startup to perform the request initialization operation. The request initialization operation, in addition to calling the request initialization function of each module shown in the figure, also does a lot of other work. The main contents are as follows:

  • Activate Zend engine

gc_reset function is used to reset garbage collection Mechanism, of course, this is only available after PHP5.3.

The init_compiler function is used to initialize the compiler, such as clearing the array where the opcode is placed during the compilation process, preparing the data structure used for compilation, etc.

The init_executor function is used to initialize the intermediate code execution process. During the compilation process, function lists, class lists, etc. are stored in global variables at compile time. When preparing for the execution process, these lists will be assigned to the executed global variables, such as: EG(function_table) = CG(function_table) ; The intermediate code is executed in PHP's execution virtual stack, and these stacks will be initialized together during initialization. In addition to the stack, the symbol table (EG (symbol_table)) that stores variables is initialized to a hashtable of 50 elements, and the EG (objects_store) that stores objects is initialized with 1024 elements. In addition to some of the variables above, PHP's execution environment also has error handling, exception handling, etc., which are all initialized here. The zend_extensions configured through php.ini are also traversed and the activate function is called here.

  • Activate SAPI

sapi_activate function is used to initialize SG (sapi_headers) and SG (request_info), and set some content for the HTTP request method, such as when the request method is HEAD, set SG (request_info).headers_only=1 ; The most important operation of this function is to process the requested data, which will eventually call sapi_module.default_post_reader. The sapi_module.default_post_reader was registered through the php_startup_sapi_content_types function in the previous module initialization. The default processing function is the php_default_post_reader function in the main/php_content_types.c file. This function will write the raw data of POST into the $HTTP_RAW_POST_DATA variable.

After processing the post data, PHP will read the cookie value through sapi_module.read_cookies. In CLI mode, the implementation of this function is sapi_cli_read_cookies, but there is only one return NULL in the function body;

If in the current mode If the activate function is set, run this function to activate SAPI. In CLI mode, this function pointer is set to NULL.

  • Environment initialization

Environment initialization here refers to the initialization of some environment variables that need to be used in user space. The environment here includes the server environment, request data environment, etc. The actual variables we use are $_POST, $_GET, $_COOKIE, $_SERVER, $_ENV, and $_FILES. Like sapi_module.default_post_reader, the value of sapi_module.treat_data is also registered through the php_startup_sapi_content_types function during module initialization. The default data processing function is the php_default_treat_data function in the main/php_variables.c file.

Taking $_COOKIE as an example, the php_default_treat_data function will split all cookies based on the delimiter and assign them to the corresponding variables.

  • Module request initialization

PHP implements module request initialization through the zend_activate_modules function, that is, we see Call each extension's RINIT in the picture. This function implements the module's request initialization operation by traversing all modules registered in the module_registry variable and calling its RINIT method.

Run

php_execute_script function includes the entire process of running PHP scripts.

When a PHP file needs to be parsed and executed, it may need to execute three files, including a pre-execution file, the main file that currently needs to be executed, and a post-execution file. The two non-current files can be set in the php.ini file through the auto_prepend_file parameter and auto_append_file parameter. If these two parameters are set to empty, the corresponding executable file is disabled.

For files that need to be parsed and executed, lexical analysis, syntax analysis and intermediate code generation operations are performed through zend_compile_file (compile_file function), and all intermediate codes of this file are returned. If the parsed file generates valid intermediate code, call zend_execute (execute function) to execute the intermediate code. If exceptions occur during execution and the user has defined handling of these exceptions, these exception handling functions are called. After all operations are processed, PHP returns the result through EG (return_value_ptr_ptr).

DEACTIVATION

The process of PHP closing a request is a set of several closing operations. This set exists in the php_request_shutdown function. This collection includes the following:

  1. Call all functions registered through register_shutdown_function(). These functions called on shutdown were added in user space. As a simple example, we can call a unified function when a script error occurs to give the user a more friendly page. This is somewhat similar to the 404 page on the web.
  2. Execute all available __destruct functions. The destructor here includes the destructor of all objects in the object pool (EG (objects_store)) and the destructor method of each element in EG (symbol_table).
  3. Flush all output.
  4. Send HTTP response header . This is also a process of outputting a string, but this string may conform to certain specifications.
  5. Traverse the shutdown request method of each module and perform the module's request shutdown operation. This is what we see in the figure. Call each extension's RSHUTDOWN.
  6. Destroy the global variable table (PG(http_globals))
  7. Turn off the lexer, parser and intermediate code executor through the zend_deactivate function.
  8. Just call the post-RSHUTDOWN function. Basically, the post_deactivate_func function pointer of each extension is NULL.
  9. Close SAPI, destroy the contents of SG (sapi_headers), SG (request_info), etc.
  10. Close the stream wrapper and close the stream filter. Memory management.
  11. Reset the maximum execution time
  12. End

Finally it’s time to finish.

flush
  • sapi_flush is called sapi_module.flush, in CLI mode. The following is equivalent to the fflush function.

Close the Zend engine
  • zend_shutdown will shut down the Zend engine.

At this time, we should perform the shutdown module operation of each module. Here there is only one zend_hash_graceful_reverse_destroy function. The module_registry is destroyed. Of course, it eventually calls the method of closing the module. The root cause is that the ZEND_MODULE_DTOR macro is set when the module_registry is initialized, and the ZEND_MODULE_DTOR macro corresponds to the module_destructor function. The module_shutdown_func method of the module will be called, which is the function generated by the PHP_RSHUTDOWN_FUNCTION macro.

After closing all modules, PHP continues to destroy the global function table, destroy the global class table, sell the global variable table, etc. It traverses all elements of zend_extensions through zend_shutdown_extensions. Call the shutdown function of each extension.

Multi-process SAPI life cycle

Usually PHP is compiled as a module of apache to handle PHP requests. Apache generally adopts multi-process mode. After Apache is started, it will fork multiple child processes. Each process has an independent memory space. Each child process will go through the start and end stages. However, the beginning stage of each process only occurs after the process forks. To proceed, multiple requests may be processed throughout the lifetime of the process. The shutdown phase occurs only after Apache is shut down or the process is terminated. In between these two phases, the request start-request shutdown phase is repeated with each request. As shown in the figure below:

Multi-process SAPI life cycle

Multi-threaded SAPI life cycle

Multi-thread mode is similar to a certain process in multi-process, but the difference is that during the life cycle of the entire process The request start-request closing process will be repeated in parallel

Multi-threaded SAPI life cycle

Excerpted from: http://www.php-internals.com/book/?p=chapt02/02-01- php-life-cycle-and-zend-engine

The above introduces the learning of PHP kernel-PHP life cycle, including aspects of it. I hope it will be helpful to friends who are interested in PHP tutorials.

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:ajax3—php(29)Next article:ajax3—php(29)