下面是一份對(duì)PHP5.1.4底層的研究,是一位從事PHP很長時(shí)間并有較深入研究的來自PHPChina.com上的PHPer。在此向他表示感謝!
PHP源代碼分析(針對(duì)版本PHP5.1.4)
PHP源代碼分析 1
1. 目錄結(jié)構(gòu) 1
2. PHP使用Lex和Yacc對(duì)語法進(jìn)行解析。 1
3. PHP如何使用Mysql? 2
4. 安全模式? 2
5. 那些是 PHP 的標(biāo)準(zhǔn)函數(shù),那些是擴(kuò)展函數(shù)? 2
6. PHP 源代碼中的PHP_FUNCTION(xx) 宏。 2
7. 那些函數(shù)集是標(biāo)準(zhǔn)的? 2
8. 一些函數(shù)的實(shí)現(xiàn)過程 2
9. PHP 函數(shù)集注冊(cè)過程 3
10. 有趣的Zend LOGO圖片 3
11. PHP的語法樹? 3
12. 從 CVS 獲取 PHP 源代碼 5

1. 目錄結(jié)構(gòu)
1. build 和編譯有關(guān)的目錄。
2. ext 擴(kuò)展庫代碼,例如 Mysql、zlib、iconv 等我們熟悉的擴(kuò)展庫。
3. main 主目錄。
4. sapi 和各種服務(wù)器的接口調(diào)用,例如apache、IIS等,也包含一般的fastcgi、cgi等。
5. win32 和 Windows 下編譯 PHP 有關(guān)的腳本。用了 WSH。
6. Zend 文件夾核心的引擎。
7. scripts Linux 下的腳本目錄。
8. tests 測試腳本目錄
9. sapi 各類 Web 服務(wù)器的接口。
2. PHP使用Lex和Yacc對(duì)語法進(jìn)行解析。
在 Zend 目錄下有兩個(gè)文件 zend_language_parser.y 與 zend_language_scanner.l 他們是Lex和Yacc的腳本文件,通過這兩個(gè)腳本文件生成對(duì)應(yīng)的.c和.h文件,實(shí)際上這在 linux 下非常普遍,gcc 也使用它們產(chǎn)生語樹。
3. PHP如何使用Mysql?
ext 目錄下有一個(gè) mysql 子目錄,這個(gè)目錄中的php_mysql.c 和 php_mysql.h 負(fù)責(zé) PHP 與 Mysql 操作。使用了 Mysql 手冊(cè)中的 C 語言 API。
4. 安全模式?
main 文件夾下的safe_mode.h 和 safe_mode.c 文件負(fù)責(zé)PHP的安全模式。
5. 那些是 PHP 的標(biāo)準(zhǔn)函數(shù),那些是擴(kuò)展函數(shù)?
ext 目錄下英文意思是擴(kuò)展,而在 ext 下還是有一個(gè) standard 文件夾,存放著 PHP 中的標(biāo)準(zhǔn)函數(shù),例如 explode 這個(gè)函數(shù)是在./ext/standard/string.c 下定義的。
6. PHP 源代碼中的PHP_FUNCTION(xx) 宏。
這個(gè)宏用來檢驗(yàn)一個(gè)函數(shù)名稱是否合法。合法的函數(shù)名稱應(yīng)該由小寫字母及下劃線組成。
7. 那些函數(shù)集是標(biāo)準(zhǔn)的?
通過 ./ext/standard/ 目錄我們可以看到以下常用函數(shù)集是標(biāo)準(zhǔn)的。字符串函數(shù)集、數(shù)組函數(shù)集、文件及目錄操作函數(shù)集、md5算法等。
8. 一些函數(shù)的實(shí)現(xiàn)過程
1. fsockopen, pfsockopen 的實(shí)現(xiàn)
這兩個(gè)函數(shù)的實(shí)現(xiàn)離不開 ./ext/standard/fsock.c 文件中的 php_fsockopen_stream 函數(shù)。具體的socket都在./main/network.c 中實(shí)
現(xiàn)。
9. PHP 函數(shù)集注冊(cè)過程
在./main/internal_functi****.c 中有一個(gè)數(shù)組 php_builtin_extensi**** 默認(rèn)下有以下成員:
1. phpext_bcmath_ptr
2. phpext_calendar_ptr
3. phpext_com_dotnet_ptr
4. phpext_ctype_ptr
5. phpext_date_ptr
6. phpext_ftp_ptr
7. phpext_hash_ptr
8. phpext_odbc_ptr
9. phpext_pcre_ptr
10. phpext_reflection_ptr
11. phpext_session_ptr
12. phpext_spl_ptr
13. phpext_standard_ptr
14. phpext_tokenizer_ptr
15. phpext_zlib_ptr
接著 php_register_extensi****(php_builtin_extensi****, EXTCOUNT TSRMLS_CC) 進(jìn)行注冊(cè)
10. 有趣的Zend LOGO圖片
./main/logos.h 文件中,用 zend_logo 與 php_logo 數(shù)組保存了 PHP 標(biāo)志和 Zend 標(biāo)志。所以你根本在發(fā)行包里找不到zend.gif。
【小知識(shí):Zend 公司創(chuàng)建于 1999 年,之所以命名為 Zend,是取其公司兩位始創(chuàng)者Zeev Suraski 和Andi Gutmans 姓名的近似合成發(fā)音(Zeev & Andi),Zend 作為 PHP 語言的締造者和延續(xù)著在 PHP 社區(qū)中發(fā)揮著極為重要的作用,Zend公司一直具備PHP技術(shù)的設(shè)想和創(chuàng)新能力,并因此保持PHP獨(dú)一無二的技術(shù)領(lǐng)先地位!】
11. PHP的語法樹?
1. Lex與Yacc
市面上有這本書。大家可以買來看看,包括GCC都是用它們兄弟生成的語法樹。如果對(duì)編譯器感興趣。可以翻閱市面上關(guān)于這方面的書,并不多就幾本。
2. y語法樹文件
./Zend/zend_language_scanner.l與./Zend/zend_language_parser.y 規(guī)定了PHP的語法。從字面意義上scanner表示語法初步掃描, parser表示語法解析。根據(jù)這兩個(gè)文件lex與yacc可以生成對(duì)應(yīng)的c代碼。所以相對(duì)來說生成語法是很方便的。
3. 如何定義一個(gè)符號(hào)
例如 if($language='php') 這一句中的if 就是一個(gè)token 語法中我們用T_IF表示。具體在.l文件中如下定義了:
<ST_IN_SCRIPTING>"if" {
return T_IF;
}
這樣.php文件中的if就會(huì)被翻譯成內(nèi)置符號(hào)T_IF。’(單引號(hào))被如下定義:
<ST_SINGLE_QUOTE>['] {
BEGIN(ST_IN_SCRIPTING);
return '\'';
}
4. 復(fù)合符號(hào)例如最常見的變量命名$discuz_user, $submit 等。
<ST_IN_SCRIPTING,ST_DOUBLE_QUOTES,ST_HEREDOC,ST_BACKQUOTE>"$"{LABEL} {
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
zendlval->type = IS_STRING;
return T_VARIABLE;
}
5. 一個(gè)有效的if語句過程
這個(gè)定義在zend_language_parser.y 189行:
T_IF '(' expr ')' {
zend_do_if_cond(&$3, &$4 TSRMLS_CC);
} statement {
zend_do_if_after_statement(&$4, 1 TSRMLS_CC);
} elseif_list else_single {
zend_do_if_end(TSRMLS_C);
}
  T_IF '(' expr ')' ':' {
zend_do_if_cond(&$3, &$4 TSRMLS_CC);
} inner_statement_list {
zend_do_if_after_statement(&$4, 1 TSRMLS_CC);
} new_elseif_list new_else_single T_ENDIF ';' {
zend_do_if_end(TSRMLS_C);
}
if 后面必須存在(),圓括弧里面是表達(dá)式 expr 表達(dá)式在734行被定義:
expr:
r_variable { $$ = $1; }
  expr_without_variable { $$ = $1; }
;
if 后面可以跟 elseif 語句及 else 語句。
從語法樹里面我們看出 if () 后面是可以跟 : 的,這一般很少被使用吧。
6. 優(yōu)先級(jí)和左右結(jié)合性
一般情況下.y文件中最先定義的操作符優(yōu)先級(jí)相對(duì)低,并且可以使用%left、%right 進(jìn)行描述左右結(jié)合性,例如:
%left '+' '-' '.'
%left '*' '/' '%'
%right '!'
這說明'!'在 PHP 語法中是右結(jié)合的, '*' '/' '%' '+' '-' '.' 是左結(jié)合的,并且'!'的優(yōu)先級(jí)更高
例如語法 !$a + $b 要先計(jì)算 !$a 在進(jìn)行加法操作%left ',' 被放在最上面定義,說明他的優(yōu)先級(jí)最低,因?yàn)槲覀冎?,'可以等同一個(gè)語句。
7. php.ini的解析
1. 如果規(guī)定數(shù)值正負(fù)?
<INITIAL>[ ]*("true" "on" "yes")[ ]* {
ini_lval->value.str.val = zend_strndup("1", 1);
ini_lval->value.str.len = 1;
ini_lval->type = IS_STRING;
return CFG_TRUE;
}
<INITIAL>[ ]*("false" "off" "no" "none")[ ]* {
ini_lval->value.str.val = zend_strndup("", 0);
ini_lval->value.str.len = 0;
ini_lval->type = IS_STRING;
return CFG_FALSE;
}
  
  T_IF '(' expr ')' ':' {
zend_do_if_cond(&$3, &$4 TSRMLS_CC);
} inner_statement_list {
zend_do_if_after_statement(&$4, 1 TSRMLS_CC);
} new_elseif_list new_else_single T_ENDIF ';' {
zend_do_if_end(TSRMLS_C);
}
if 后面必須存在(),圓括弧里面是表達(dá)式 expr 表達(dá)式在734行被定義:
expr:
r_variable { $$ = $1; }
  expr_without_variable { $$ = $1; }
;
if 后面可以跟 elseif 語句及 else 語句。
從語法樹里面我們看出 if () 后面是可以跟 : 的,這一般很少被使用吧。