PHPインタープリタエンジン実行処理

WBOY
リリース: 2016-06-23 13:17:32
オリジナル
1061 人が閲覧しました

カタログ

1. SAPI接口2. PHP CLI模式解释执行脚本流程3. PHP Zend Complile/Execute函数接口化(Hook Call架构基础)
ログイン後にコピー

1. SAPI インターフェイス

PHP の SAPI 層は、上位層インターフェイスのカプセル化を実装しているため、PHP を多くのモード シナリオ (Apache、ningx、cgi、fastcgi、cli など) で使用できます。 to cli SAPI では、PHP インタプリタ エンジンが PHP ユーザー モード ソース コード ファイルを処理する方法を学習します。Cli (コマンド ライン インターフェイス) は、PHP をサーバーにインストールした後、デフォルトでインストールされます。 , 通常、実行可能ファイルを生成します

スクリプト実行の開始は、SAPI インターフェイスの実装から始まります。たとえば、Apache の mod_php SAPI 実装は、Apache から取得した一部の情報を初期化し、その内容を出力コンテンツで Apache に返す必要があるというだけです。

0x1: sapi_module_struct。

SAPI を定義するには、まず sapi_module_structPHP-SRC/sapi/cli/php_cli.c を定義します。

/* {{{ sapi_module_struct cli_sapi_module */static sapi_module_struct cli_sapi_module = {    "cli",                            /* name php_info()的时候被使用 */    "Command Line Interface",        /* pretty name */    php_cli_startup,                /* startup */    php_module_shutdown_wrapper,    /* shutdown */    NULL,                            /* activate */    sapi_cli_deactivate,            /* deactivate */    sapi_cli_ub_write,                /* unbuffered write */    sapi_cli_flush,                    /* flush */    NULL,                            /* get uid */    NULL,                            /* getenv */    php_error,                        /* error handler */    sapi_cli_header_handler,        /* header handler */    sapi_cli_send_headers,            /* send headers handler */    sapi_cli_send_header,            /* send header handler */    NULL,                            /* read POST data */    sapi_cli_read_cookies,          /* read Cookies */    sapi_cli_register_variables,    /* register server variables */    sapi_cli_log_message,            /* Log message */    NULL,                            /* Get request time */    NULL,                            /* Child terminate */        STANDARD_SAPI_MODULE_PROPERTIES};/* }}} */
ログイン後にコピー

この構造には、php_info() を呼び出すときに使用される名前などのいくつかの定数が含まれています。一部の初期化関数、終了関数、および一部の関数ポインターは、Zend にデータの取得および出力方法を指示するために使用されます。以下のプロセスの紹介で 1 つずつ説明します

関連リンク:

PHP CLI モードの説明

2.スクリプトプロセス

0x1: プロセスの起動

メインプロセス main が必要な初期化作業を実行した後、SAPI 論理プロセスに入り、いくつかの環境変数を初期化します。これは SAPI ライフサイクル全体を通して有効になります

0x2: MIT

http://www.nowamagic.net/librarys/veda/detail/1285
ログイン後にコピー

MINIT は「モジュールの初期化」を意味します。各モジュールは、他のリクエストを処理するための一連の関数、クラス ライブラリなどを定義します。典型的な MINIT メソッドは次のとおりです。

int main(int argc, char *argv[]){    ..    sapi_module_struct *sapi_module = &cli_sapi_module;    ..    sapi_module->ini_defaults = sapi_cli_ini_defaults;    sapi_module->php_ini_path_override = ini_path_override;    sapi_module->phpinfo_as_text = 1;    sapi_module->php_ini_ignore_cwd = 1;    sapi_startup(sapi_module);    sapi_started = 1;    ..
ログイン後にコピー

0x3: RINIT

ページ リクエストが発生すると、SAPI 層は PHP 層に制御を渡します。 。したがって、PHP は、このリクエストに応答するために必要な環境変数を設定します。同時に、実行中に生成された変数名と値を保存する変数テーブルも作成します。 PHP は各モジュールの RINIT メソッド、つまり「初期化の要求」を呼び出します

典型的な例は、セッション モジュールの RINIT です。セッション モジュールが php.ini で有効になっている場合、RINIT の実行時に $_SESSION 変数が初期化されます。モジュールの が呼び出され、関連する内容が

に読み込まれます

RINIT メソッドは、プログラムが実行される前に自動的に開始される準備プロセスとみなすことができます。典型的な RINIT メソッドは次のとおりです

static int php_cli_startup(sapi_module_struct *sapi_module) /* {{{ */{    if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {        return FAILURE;    }    return SUCCESS;}
ログイン後にコピー

PHP は、リクエストごとにいくつかの初期化およびリソース割り当てトランザクションを処理します。この部分は、activate フィールドが定義される部分です。上記の構造体から、CGI の場合、上記の cli に対応する cli_sapi_module 構造体が初期化処理ハンドルを提供していないことがわかります。 mod_php の場合は異なります。Apache プールにリソース デストラクターを登録し、スペースを適用し、環境変数を初期化する必要があります。

0x4: SCRIPT

PHP は、php_execute_script(&file_handle TSRMLS_CC) スクリプト php-5.6 を通じて PHP を実行します。 17mainmain.c

/* {{{ php_module_startup */int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additional_modules){    ..    zend_module_entry *module;    ..    module_shutdown = 0;    module_startup = 1;    sapi_initialize_empty_request(TSRMLS_C);    sapi_activate(TSRMLS_C);    ..    /* start additional PHP extensions */    php_register_extensions_bc(additional_modules, num_additional_modules TSRMLS_CC);    /* load and startup extensions compiled as shared objects (aka DLLs)       as requested by php.ini entries       theese are loaded after initialization of internal extensions       as extensions *might* rely on things from ext/standard       which is always an internal extension and to be initialized       ahead of all other internals     */    php_ini_register_extensions(TSRMLS_C);    zend_startup_modules(TSRMLS_C);    /* start Zend extensions */    zend_startup_extensions();    ..
ログイン後にコピー

php_execute_script は最終的に zend_execute_scripts{PHPSRC}/Zend/zend.c

PHP_MINIT_FUNCTION(extension_name){ /* Initialize functions, classes etc */ }
ログイン後にコピー

1. コンパイル コンパイル プロセス

zend_compile_file は関数ポインタであり、/ 内の {PHPSRC}/Zend

PHP_RINIT_FUNCTION(extension_name) { /* Initialize session variables,pre-populate variables, redefine global variables etc */ }
ログイン後にコピー

で宣言されます。 zend_compile.c

エンジンが初期化されると、compile_file 関数のアドレスが zend_compile_file に割り当てられます。 1)

zend_execute は関数ポインター (コンパイル プロセスを使用して取得されるオペコード配列) でもあり、これは {PHPSRC}/Zend/zend_execute.c で宣言されます

/* {{{ php_execute_script */PHPAPI int php_execute_script(zend_file_handle *primary_file TSRMLS_DC){    //file_handle的类型为zend_file_handle,这个是zend对文件句柄的一个封装,里面的内容和待执行脚本相关    zend_file_handle *prepend_file_p, *append_file_p;    zend_file_handle prepend_file = {0}, append_file = {0};    ..    //php_execute_script最终是调用的zend_execute_scripts    retval = (zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, NULL, 3, prepend_file_p, primary_file, append_file_p) == SUCCESS);    ..
ログイン後にコピー

エンジンが初期化されると、実行関数のアドレスがzend_execute に割り当てられ、実行 {PHPSRC}/Zend/zend_vm_execute.h で定義されます

//此函数具有可变参数,可以一次执行多个PHP文件ZEND_API int zend_execute_scripts(int type TSRMLS_DC, zval **retval, int file_count, ...) /* {{{ */{    ..    EG(active_op_array) = zend_compile_file(file_handle, type TSRMLS_CC);    ..    if (EG(active_op_array))     {        EG(return_value_ptr_ptr) = retval ? retval : NULL;        zend_execute(EG(active_op_array) TSRMLS_CC);        ..
ログイン後にコピー

0x5: RSHUTDOWN

ページが実行されると (ファイルの最後に到達するか、exit または die 関数で中止されるかに関係なく)、 PHP がクリーンアップ プログラムを開始します。各モジュールの RSHUTDOWN メソッドを順番に呼び出します。 RSHUTDOWN は、プログラムの実行時に生成されたシンボル テーブルをクリアするために使用されます。つまり、各変数の unset 関数を呼び出します

ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);  
ログイン後にコピー

0x6: MSHUTDOWN

最後に、すべてのリクエストが処理され、SAPI を閉じる準備ができ、PHP が実行されます。 2 番目のステップの実行を開始します: PHP は各拡張機能の MSHUTDOWN メソッドを呼び出します。これが各モジュールがメモリを解放する最後のチャンスです

//函数以zend_file_handle指针作为参数,返回一个指向zend_op_array的指针ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type TSRMLS_DC){    ..    //Lex词法解析过程    ..
ログイン後にコピー

/main/main.c

ZEND_API extern void (*zend_execute)(zend_op_array *op_array TSRMLS_DC);  
ログイン後にコピー

関連リンク:

//zend_execute以一个指向zend_op_array结构的指针作为参数,这个指针即前面zend_compile_file的返回值,zend_execute就开始执行op_array中的op code,在执行op code的过程中,就实现了PHP语言的各种功能ZEND_API void zend_execute(zend_op_array *op_array TSRMLS_DC){    if (EG(exception)) {        return;    }     zend_execute_ex(i_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC) TSRMLS_CC);}
ログイン後にコピー

3.関数インターフェイスの実行 (フックコールアーキテクチャの基本)

PHP内核在设计架构实现的时候,除了提供了扩展机制,还在Zend的两个关键流程(compile、execute)提供了Hook机制,PHP扩展开发人员可以Hook劫持Zend的编译/解释执行流程,在Zend编译执行之前先执行自定义的代码逻辑,然后再交还控制权给Zend。在引擎初始化(zend_startup)的时候

1. end_execute指向了默认的execute2. zend_compile_file指向了默认的compile_file
ログイン後にコピー

我们可以在实际编译和执行之前(RINIT阶段中)将zend_execute和zend_compile_file重写为其他的编译和执行函数,这样就为我们扩展引擎留下了钩子,比如一个比较有名的查看PHP的op code的扩展vld,此扩展就是在每次请求初始化的钩子函数(PHP_RINIT_FUNCTION)中,将zend_execute和zend_compile_file替换成自己的vld_execute和vld_compile_file,这两个函数其实是对原始函数进行了封装,添加了输出opcode信息的附加功能,因为引擎初始化是发生在模块请求初始化之前,而模块请求初始化又是在编译和执行之前,所以这样的覆盖能达到目的

Relevant Link:

Copyright (c) 2016 LittleHann All rights reserved

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート