PHP源碼解析筆記1-源碼中的常用代碼


基於PHP源碼分析的git倉庫:深入理解PHP內核

常用代碼

雙井號(##)

在C語言的宏中,”##”被稱為 連接符(concatenator),它是一種預處理運算符, 用來把兩個語言符號(Token)組合成單個語言符號。 這里的語言符號不一定是宏的變量。並且雙井號不能作為第一個或最后一個元素存在。示例如下:

#define ZEND_FN(name) zif_##name

宏ZEND_FN(name)中有一個”##”,它的作用一如之前所說,是一個連接符,將zif和宏的變量name的值連接起來。

總結:以這種連接的方式以基礎,多次使用這種宏形式,可以將它當作一個代碼生成器,這樣可以在一定程度上減少代碼密度, 我們也可以將它理解為一種代碼重用的手段,間接地減少不小心所造成的錯誤。

單井號(#)

“#”是一種預處理運算符,它的功能是將其后面的宏參數進行 字符串化操作 , 簡單說就是在對它所引用的宏變量通過替換后在其左右各加上一個雙引號, 用比較官方的話說就是將語言符號(Token)轉化為字符串。

#define STR(x) #x

int main(int argc char** argv)
{
printf("%s\n", STR(It's a long string)); // 輸出 It's a long str
return 0;
}

如前文所說,It’s a long string 是宏STR的參數,在展開后被包裹成一個字符串了。所以printf函數能直接輸出這個字符串, 當然這個使用場景並不是很適合,因為這種用法並沒有實際的意義,實際中在宏中可能會包裹其他的邏輯,比如對字符串進行封裝等等。

關於宏定義中的do-while循環

PHP源碼中大量使用了宏操作,比如PHP5.3新增加的垃圾收集機制中的一段代碼:

#define ALLOC_ZVAL(z) 
do {
(z) = (zval*)emalloc(sizeof(zval_gc_info));
GC_ZVAL_INIT(z);
} while (0)

只執行一次的代碼為什么要放在do-while語句里呢? 這種方式適用於宏定義中存在多語句的情況。

line 預處理

#line 838 "Zend/zend_language_scanner.c"

line預處理用於改變當前的行號(LINE)和文件名(FILE)。 如上所示代碼,將當前的行號改變為838,文件名Zend/zend_language_scanner.c 它的作用體現在編譯器的編寫中,我們知道編譯器對C 源碼編譯過程中會產生一些中間文件,通過這條指令, 可以保證文件名是固定的,不會被這些中間文件代替,有利於進行調試分析。

PHP中的全局變量宏(基於PHP7+)

在PHP代碼中經常能看到一些類似PG(), EG()之類的函數,他們都是PHP中定義的宏,這系列宏主要的作用是解決線程安全所寫的全局變量包裹宏, 如$PHP_SRC/main/php_globals.h文件中就包含了很多這類的宏。例如PG這個PHP的核心全局變量的宏。 如下所示代碼為其定義。

#ifdef ZTS
# define PG(v) ZEND_TSRMG(core_globals_id, php_core_globals *, v)
extern PHPAPI int core_globals_id;
#else
# define PG(v) (core_globals.v)
extern ZEND_API struct _php_core_globals core_globals;
#endif

如上,ZTS是線程安全的標記,這個在以后的章節會詳細介紹,這里就不再說明。下面簡單說說,PHP運行時的一些全局參數, 這個全局變量為如下的一個結構體,各字段的意義如字段后的注釋:

struct _php_core_globals {
zend_bool implicit_flush;//是否要求PHP輸出層在每個輸出塊之后自動刷新數據

zend_long output_buffering;//輸出緩沖區大小(字節)

zend_bool enable_dl; //是否允許使用dl()函數。dl()函數僅在將PHP作為apache模塊安裝時才有效。

char *output_handler;//將所有腳本的輸出重定向到一個輸出處理函數。

char *unserialize_callback_func; // 如果解序列化處理器需要實例化一個未定義的類,這里指定的回調函數將以該未定義類的名字作為參數被unserialize()調用,
zend_long serialize_precision;//將浮點型和雙精度型數據序列化存儲時的精度(有效位數)。

zend_long memory_limit;//一個腳本所能夠申請到的最大內存字節數(可以使用K和M作為單位)。
zend_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指令
zend_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 *php_binary;//php二進制程序位置?
char *sys_temp_dir;//系統臨時文件夾?

char *upload_tmp_dir;// 文件上傳時存放文件的臨時目錄
zend_long upload_max_filesize;// 允許上傳的文件的最大尺寸。

char *error_append_string;// 用於錯誤信息后輸出的字符串
char *error_prepend_string;//用於錯誤信息前輸出的字符串

char *auto_prepend_file;//指定在主文件之前自動解析的文件名。
char *auto_append_file;//指定在主文件之后自動解析的文件名。

char *input_encoding;//輸入編碼
char *internal_encoding;//內部編碼
char *output_encoding;//輸出編碼

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_argc_argv;// 是否聲明$argv和$argc全局變量(包含用GET方法的信息)。
zend_bool auto_globals_jit;// 是否僅在使用到$_SERVER和$_ENV變量時才創建(而不是在腳本一啟動時就自動創建)。

char *docref_root;// 如果打開了html_errors指令,PHP將會在出錯信息上顯示超連接
char *docref_ext; //指定文件的擴展名(必須含有’.')。

zend_bool html_errors;//是否在出錯信息中使用HTML標記。
zend_bool xmlrpc_errors;//是否在出錯信息中使用xmlrpc標記。?

zend_long xmlrpc_error_number;//是否在出錯信息中xmlrpc標記的編號?

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 enable_post_data_reading;//是否能獲取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 *php_sys_temp_dir;//PHP對應的系統臨時文件目錄

char *disable_functions; //該指令接受一個用逗號分隔的函數名列表,以禁用特定的函數。
char *disable_classes;//該指令接受一個用逗號分隔的類名列表,以禁用特定的類。
zend_bool allow_url_include;//是否允許include/require遠程文件。
#ifdef PHP_WIN32
zend_bool com_initialized;
#endif
zend_long max_input_nesting_level;//最大的嵌套層數
zend_long max_input_vars;//最大的輸入變量數?
zend_bool in_user_include; //是否在用戶包含空間

char *user_ini_filename;// 用戶的ini文件名
zend_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;//

#ifdef PHP_WIN32
zend_bool windows_show_crt_warning;
#endif
};

上面的字段很大一部分是與php.ini文件中的配置項對應的。 在PHP啟動並讀取php.ini文件時就會對這些字段進行賦值, 而用戶空間的ini_get()及ini_set()函數操作的一些配置也是對這個全局變量進行操作的。

附:ticks的使用示例

<?php
/**
* Created by sunny.
* User: sunny
* Have a nice day!
* Date: 2017/2/15
* Time: 14:30
*/

function profile()
{

echo "profile function is called\n";
}

// Set up a tick handler
register_tick_function("profile");

// Initialize the function before the declare block
profile();

// Run a block of code, throw a tick every 2nd statement
declare(ticks=3) {
for ($x = 0; $x < 10; ++$x) {
echo "hello world\n";
}
}

ticks主要用來按照規定的輸出次數之后再次執行tick函數,示例輸出如下:
這里寫圖片描述


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com