$PHP-SRC/run-test.php
- 由於若是在同一個進程中執行, 測試就會中止,後面的測試也將沒法執行,php中有不少將腳本隔離的方法好比: system(),exec()等函數,這樣可使用主測試進程服務調度被測腳本和檢測測試結果,經過這些外部調用執行測試。 php測試使用了proc_open()函數, 這樣就能夠保證測試腳本和被測試腳本之間能隔離開。
若是隻須要簡單的進程單向進程通道 可使用 popenphp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
<?php function foo() { echo date('Y-m-d H:i:s')."\n"; echo shell_exec('php -r \'sleep(1); echo date("Y-m-d H:i:s")." by shell_exec:blocking\n";\''); //阻塞 $pipe1 = popen('php -r \'sleep(2); echo date("Y-m-d H:i:s")." by popen1:non-blocking\n";\'', 'r'); //非阻塞(管道) $pipe2 = popen('php -r \'sleep(1); echo date("Y-m-d H:i:s")." by popen2:non-blocking\n";\'', 'r'); //非阻塞(管道) echo date('Y-m-d H:i:s')."\n"; register_shutdown_function(function() use ($pipe1, $pipe2) { //事件驅動(腳本結束事件),異步回調 echo stream_get_contents($pipe1); //輸出子進程返回的數據 echo stream_get_contents($pipe2); //輸出子進程返回的數據 pclose($pipe1); pclose($pipe2); }); } foo();
|
可是若是須要雙方向的功能更多的進程控制 可使用proc_open()打開新的子進程非阻塞的執行PHP腳本html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
function foo() { $proc = proc_open( //task.php 內容爲 $arr = unserialize(stream_get_contents(STDIN)); $arr['time'] = date('Y-m-d H:i:s', $arr['time']); echo serialize($arr); '/png/php/5.4.45/bin/php /home/eechen/task.php', array( 0 => array('pipe','r'), //stdin (用fwrite寫入數據給管道) 1 => array('pipe','w'), //stdout(用stream_get_contents獲取管道輸出) 2 => array('pipe','w'), //stderr(用stream_get_contents獲取管道輸出) //2 => array('file','/tmp/err.txt','a') //stderr(寫入到文件) ), $pipes, //管道(stdin/stdout/stderr) '/tmp', //當前PHP進程的工做目錄 array('foo' => 'bar') //php.ini 配置 variables_order = "EGPCS" 其中E表示$_ENV,不然$_ENV輸出爲空 要執行腳本所須要的環境變量 ); //var_dump($pipes); //resource of type (stream) if(is_resource($proc)) { //stdin $stdin = serialize(array('time' => time())); fwrite($pipes[0], $stdin); //把參數傳給腳本task.php fclose($pipes[0]); //fclose關閉管道後proc_close才能退出子進程,不然會發生死鎖 register_shutdown_function(function() use($pipes, $proc) { //事件驅動(腳本結束事件),異步回調 //stdout $stdout = stream_get_contents($pipes[1]); fclose($pipes[1]); //stderr $stderr = stream_get_contents($pipes[2]); fclose($pipes[2]); //exit code (返回進程的終止狀態碼,若是發生錯則返回-1) $status = proc_close($proc); $data = array( 'stdout' => $stdout, 'stderr' => $stderr, 'status' => $status, ); var_export($data); //echo json_encode($data); }); } } foo();
|
宏定義 C
PHP代碼中的宏定義包含 # 和 ##node
‘##’表明 鏈接符 用來把兩個語言符號(Token)組合成單個語言符號。 這裏的語言符號不必定是宏的變量。而且雙井號不能做爲第一個或最後一個元素存在。sql
1 2 3 4 5 6
|
#define PHP_FUNCTION ZEND_FUNCTION #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) #define ZEND_FN(name) zif_##name #define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS) #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, \
|
「#」是一種預處理運算符,它的功能是將其後面的宏參數進行 字符串化操做shell
PHP中代碼的do-while宏
1 2 3 4 5
|
#define ALLOC_ZVAL(z) \ do { \ (z) = (zval*)emalloc(sizeof(zval_gc_info)); \ GC_ZVAL_INIT(z); \ } while (0)
|
顯而易見代碼只執行一次,經常使用用例舉例apache
1 2 3 4 5 6
|
#define TEST(a, b) a++;b++; if (expr) TEST(a, b); else do_else();
|
若是直接編譯會形成if 後面的單句規則被破壞。編程
以及須要考慮到平臺移植,空操做也使用 do{}while(0)來進行宏定義json
PHP運行時的全局參數 PHP CORE GLOBALS
首先會根據ZTS 或者NTS (線程安全) 來有一些變化數組
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
struct _php_core_globals { zend_bool magic_quotes_gpc; // 是否對輸入的GET/POST/Cookie數據使用自動字符串轉義。 zend_bool magic_quotes_runtime; //是否對運行時從外部資源產生的數據使用自動字符串轉義 zend_bool magic_quotes_sybase; // 是否採用Sybase形式的自動字符串轉義 zend_bool safe_mode; // 是否啓用安全模式 zend_bool allow_call_time_pass_reference; //是否強迫在函數調用時按引用傳遞參數 引用傳值 值複製傳值 zend_bool implicit_flush; //是否要求PHP輸出層在每一個輸出塊以後自動刷新數據 long output_buffering; //輸出緩衝區大小(字節) char *safe_mode_include_dir; //在安全模式下,該組目錄和其子目錄下的文件被包含時,將跳過UID/GID檢查。 zend_bool safe_mode_gid; //在安全模式下,默認在訪問文件時會作UID比較檢查 zend_bool sql_safe_mode; zend_bool enable_dl; //是否容許使用dl()函數。dl()函數僅在將PHP做爲apache模塊安裝時纔有效。 char *output_handler; // 將全部腳本的輸出重定向到一個輸出處理函數。 char *unserialize_callback_func; // 若是解序列化處理器須要實例化一個未定義的類,這裏指定的回調函數將以該未定義類的名字做爲參數被unserialize()調用, long serialize_precision; //將浮點型和雙精度型數據序列化存儲時的精度(有效位數)。 char *safe_mode_exec_dir; //在安全模式下,只有該目錄下的可執行程序才容許被執行系統程序的函數執行。 long memory_limit; //一個腳本所可以申請到的最大內存字節數(可使用K和M做爲單位)。 long max_input_time; // 每一個腳本解析輸入數據(POST, GET, upload)的最大容許時間(秒)。 zend_bool track_errors; //是否在變量$php_errormsg中保存最近一個錯誤或警告消息。 zend_bool display_errors; //是否將錯誤信息做爲輸出的一部分顯示。 zend_bool display_startup_errors; //是否顯示PHP啓動時的錯誤。 zend_bool log_errors; // 是否在日誌文件裏記錄錯誤,具體在哪裏記錄取決於error_log指令 long log_errors_max_len; //設置錯誤日誌中附加的與錯誤信息相關聯的錯誤源的最大長度。 zend_bool ignore_repeated_errors; // 記錄錯誤日誌時是否忽略重複的錯誤信息。 zend_bool ignore_repeated_source; //是否在忽略重複的錯誤信息時忽略重複的錯誤源。 zend_bool report_memleaks; //是否報告內存泄漏。 char *error_log; //將錯誤日誌記錄到哪一個文件中。 char *doc_root; //PHP的」根目錄」。 char *user_dir; //告訴php在使用 /~username 打開腳本時到哪一個目錄下去找 char *include_path; //指定一組目錄用於require(), include(), fopen_with_path()函數尋找文件。 char *open_basedir; // 將PHP容許操做的全部文件(包括文件自身)都限制在此組目錄列表下。 char *extension_dir; //存放擴展庫(模塊)的目錄,也就是PHP用來尋找動態擴展模塊的目錄。 char *upload_tmp_dir; // 文件上傳時存放文件的臨時目錄 long upload_max_filesize; // 容許上傳的文件的最大尺寸。 char *error_append_string; // 用於錯誤信息後輸出的字符串 char *error_prepend_string; //用於錯誤信息前輸出的字符串 char *auto_prepend_file; //指定在主文件以前自動解析的文件名。 char *auto_append_file; //指定在主文件以後自動解析的文件名。 arg_separators arg_separator; //PHP所產生的URL中用來分隔參數的分隔符。 char *variables_order; // PHP註冊 Environment, GET, POST, Cookie, Server 變量的順序。 HashTable rfc1867_protected_variables; // RFC1867保護的變量名,在main/rfc1867.c文件中有用到此變量 short connection_status; // 鏈接狀態,有三個狀態,正常,中斷,超時 short ignore_user_abort; // 是否即便在用戶停止請求後也堅持完成整個請求。 unsigned char header_is_being_sent; // 是否頭信息正在發送 zend_llist tick_functions; // 僅在main目錄下的php_ticks.c文件中有用到,此處定義的函數在register_tick_function等函數中有用到。 zval *http_globals[6]; // 存放GET、POST、SERVER等信息 zend_bool expose_php; // 是否展現php的信息 zend_bool register_globals; // 是否將 E, G, P, C, S 變量註冊爲全局變量。 zend_bool register_long_arrays; // 是否啓用舊式的長式數組(HTTP_*_VARS)。 zend_bool register_argc_argv; // 是否聲明$argv和$argc全局變量(包含用GET方法的信息)。 zend_bool auto_globals_jit; // 是否僅在使用到$_SERVER和$_ENV變量時才建立(而不是在腳本一啓動時就自動建立)。 zend_bool y2k_compliance; //是否強制打開2000年適應(可能在非Y2K適應的瀏覽器中致使問題)。 char *docref_root; // 若是打開了html_errors指令,PHP將會在出錯信息上顯示超鏈接, char *docref_ext; //指定文件的擴展名(必須含有’.')。 zend_bool html_errors; //是否在出錯信息中使用HTML標記。 zend_bool xmlrpc_errors; long xmlrpc_error_number; zend_bool activated_auto_globals[8]; zend_bool modules_activated; // 是否已經激活模塊 zend_bool file_uploads; //是否容許HTTP文件上傳。 zend_bool during_request_startup; //是否在請求初始化過程當中 zend_bool allow_url_fopen; //是否容許打開遠程文件 zend_bool always_populate_raw_post_data; //是否老是生成$HTTP_RAW_POST_DATA變量(原始POST數據)。 zend_bool report_zend_debug; // 是否打開zend debug,僅在main/main.c文件中有使用。 int last_error_type; // 最後的錯誤類型 char *last_error_message; // 最後的錯誤信息 char *last_error_file; // 最後的錯誤文件 int last_error_lineno; // 最後的錯誤行 char *disable_functions; //該指令接受一個用逗號分隔的函數名列表,以禁用特定的函數。 char *disable_classes; //該指令接受一個用逗號分隔的類名列表,以禁用特定的類。 zend_bool allow_url_include; //是否容許include/require遠程文件。 zend_bool exit_on_timeout; // 超時則退出 #ifdef PHP_WIN32 zend_bool com_initialized; #endif long max_input_nesting_level; //最大的嵌套層數 zend_bool in_user_include; //是否在用戶包含空間 char *user_ini_filename; // 用戶的ini文件名 long user_ini_cache_ttl; // ini緩存過時限制 char *request_order; // 優先級比variables_order高,在request變量生成時用到,我的以爲是歷史遺留問題 zend_bool mail_x_header; // 僅在ext/standard/mail.c文件中使用, char *mail_log; zend_bool in_error_log; };
|
PHP 生命週期
PHPINIT 調用模塊前的初始化:瀏覽器
初始化若干全局變量 大都設置爲NULL
初始化若干常量 PHP_Version等
初始化zend Engine 和 核心組件
- 包括內存管理初始化、 全局使用的函數指針初始化(如前面所說的zend_printf等),對PHP源文件進行詞法分析、語法分析、 中間代碼執行的函數指針的賦值,初始化若干HashTable(好比函數表,常量表等等),爲ini文件解析作準備, 爲PHP源文件解析作準備,註冊內置函數(如strlen、define等),註冊標準常量(如E_ALL、TRUE、NULL等)、註冊GLOBALS全局變量等。
解析PHPini到PHP core globals
全局操做函數初始化
初始化靜態構建的模塊和共享模塊MINIT 模塊初始化 擴展模塊的函數初始化 註冊常量 定義模塊的使用 Web Server啓動或者腳本開始執行
- 經過php_register_internal_extensions_func函數用來註冊靜態構建的模塊,也就是默認加載的模塊, 咱們能夠將其認爲內置模塊
RINIT 模塊激活階段 開始使用模塊
解析
RSHUTDOWN 停用模塊
MSHUTDOWN WebServer關閉或者腳本中止使用
關於擴展重用
由於PHP的實現是和zend引擎實現緊耦合,因此PHP的擴展並不具備很好的移植性,好比移植到JVM虛擬機,移植到HHVM等,若是實現解耦合,那麼擴展的重用將會容易。
PHP的運行模式
FastCGI、CGI、SAPI模塊形式、嵌入式
SAPI:Server Application Program Interface
CGI:「通用網關接口」(Common Gateway Interface)
FastCGI:long-live 常駐內存型升級版高性能CGI 防止CGI的fork-and-execute模式。
PHP腳本運行
目前編程語言能夠分爲兩大類:
PHP完成基本準備工做
啓動PHP以及Zend引擎
加載註冊擴展模塊
Zend引擎進行
lex生成的 詞法分析:將代碼轉換爲一個個標記token
- 不少編程語言都使用lex/yacc或他們的變體(flex/bison)來做爲語言的詞法語法分析生成器
語法分析 bison生成 語法分析器
轉換爲opcode
opcode結構體
1 2 3 4 5 6 7 8 9
|
struct _zend_op { // 這裏是struct實現 opcode_handler_t handler; // 執行該opcode時調用的處理函數 znode result; znode op1; znode op2; ulong extended_value; // 腳本執行時候須要的其餘信息 uint lineno; zend_uchar opcode; // opcode代碼 };
|
函數是在編譯器遇到echo語句的時候進行編譯的函數:
1 2 3 4 5 6 7 8 9 10
|
void zend_do_echo(const znode *arg TSRMLS_DC) { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_ECHO; opline->op1 = *arg; SET_UNUSED(opline->op2); }
|
編譯器遇到print語句的時候進行編譯的函數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
void zend_do_print(znode *result,const znode *arg TSRMLS_DC) { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->result.op_type = IS_TMP_VAR; opline->result.u.var = get_temporary_variable(CG(active_op_array)); opline->opcode = ZEND_PRINT; opline->op1 = *arg; SET_UNUSED(opline->op2); *result = opline->result; }
|
【這裏能夠看到print和echo的根本區別 print有返回值 echo並無 二者都是語法結構,返回null也是有返回值!】