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

1. 目錄結構
1. build 和編譯有關的目錄。
2. ext 擴展庫代碼,例如 Mysql、zlib、iconv 等我們熟悉的擴展庫。
3. main 主目錄。
4. sapi 和各種服務器的接口調用,例如apache、IIS等,也包含一般的fastcgi、cgi等。
5. win32 和 Windows 下編譯 PHP 有關的腳本。用了 WSH。
6. Zend 文件夾核心的引擎。
7. scripts Linux 下的腳本目錄。
8. tests 測試腳本目錄
9. sapi 各類 Web 服務器的接口。
2. PHP使用Lex和Yacc對語法進行解析。
在 Zend 目錄下有兩個文件 zend_language_parser.y 與 zend_language_scanner.l 他們是Lex和Yacc的腳本文件,通過這兩個腳本文件生成對應的.c和.h文件,實際上這在 linux 下非常普遍,gcc 也使用它們產生語樹。
3. PHP如何使用Mysql?
ext 目錄下有一個 mysql 子目錄,這個目錄中的php_mysql.c 和 php_mysql.h 負責 PHP 與 Mysql 操作。使用了 Mysql 手冊中的 C 語言 API。
4. 安全模式?
main 文件夾下的safe_mode.h 和 safe_mode.c 文件負責PHP的安全模式。
5. 那些是 PHP 的標準函數,那些是擴展函數?
ext 目錄下英文意思是擴展,而在 ext 下還是有一個 standard 文件夾,存放著 PHP 中的標準函數,例如 explode 這個函數是在./ext/standard/string.c 下定義的。
6. PHP 源代碼中的PHP_FUNCTION(xx) 宏。
這個宏用來檢驗一個函數名稱是否合法。合法的函數名稱應該由小寫字母及下劃線組成。
7. 那些函數集是標準的?
通過 ./ext/standard/ 目錄我們可以看到以下常用函數集是標準的。字符串函數集、數組函數集、文件及目錄操作函數集、md5算法等。
8. 一些函數的實現過程
1. fsockopen, pfsockopen 的實現
這兩個函數的實現離不開 ./ext/standard/fsock.c 文件中的 php_fsockopen_stream 函數。具體的socket都在./main/network.c 中實
現。
9. PHP 函數集注冊過程
在./main/internal_functi****.c 中有一個數組 php_builtin_extensi**** 默認下有以下成員:
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) 進行注冊
10. 有趣的Zend LOGO圖片
./main/logos.h 文件中,用 zend_logo 與 php_logo 數組保存了 PHP 標志和 Zend 標志。所以你根本在發行包里找不到zend.gif。
【小知識:Zend 公司創建于 1999 年,之所以命名為 Zend,是取其公司兩位始創者Zeev Suraski 和Andi Gutmans 姓名的近似合成發音(Zeev & Andi),Zend 作為 PHP 語言的締造者和延續著在 PHP 社區中發揮著極為重要的作用,Zend公司一直具備PHP技術的設想和創新能力,并因此保持PHP獨一無二的技術領先地位!】
11. PHP的語法樹?
1. Lex與Yacc
市面上有這本書。大家可以買來看看,包括GCC都是用它們兄弟生成的語法樹。如果對編譯器感興趣。可以翻閱市面上關于這方面的書,并不多就幾本。
2. y語法樹文件
./Zend/zend_language_scanner.l與./Zend/zend_language_parser.y 規定了PHP的語法。從字面意義上scanner表示語法初步掃描, parser表示語法解析。根據這兩個文件lex與yacc可以生成對應的c代碼。所以相對來說生成語法是很方便的。
3. 如何定義一個符號
例如 if($language='php') 這一句中的if 就是一個token 語法中我們用T_IF表示。具體在.l文件中如下定義了:
<ST_IN_SCRIPTING>"if" {
return T_IF;
}
這樣.php文件中的if就會被翻譯成內置符號T_IF。’(單引號)被如下定義:
<ST_SINGLE_QUOTE>['] {
BEGIN(ST_IN_SCRIPTING);
return '\'';
}
4. 復合符號例如最常見的變量命名$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. 一個有效的if語句過程
這個定義在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 后面必須存在(),圓括弧里面是表達式 expr 表達式在734行被定義:
expr:
r_variable { $$ = $1; }
  expr_without_variable { $$ = $1; }
;
if 后面可以跟 elseif 語句及 else 語句。
從語法樹里面我們看出 if () 后面是可以跟 : 的,這一般很少被使用吧。
6. 優先級和左右結合性
一般情況下.y文件中最先定義的操作符優先級相對低,并且可以使用%left、%right 進行描述左右結合性,例如:
%left '+' '-' '.'
%left '*' '/' '%'
%right '!'
這說明'!'在 PHP 語法中是右結合的, '*' '/' '%' '+' '-' '.' 是左結合的,并且'!'的優先級更高
例如語法 !$a + $b 要先計算 !$a 在進行加法操作%left ',' 被放在最上面定義,說明他的優先級最低,因為我們知道','可以等同一個語句。
7. php.ini的解析
1. 如果規定數值正負?
<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 后面必須存在(),圓括弧里面是表達式 expr 表達式在734行被定義:
expr:
r_variable { $$ = $1; }
  expr_without_variable { $$ = $1; }
;
if 后面可以跟 elseif 語句及 else 語句。
從語法樹里面我們看出 if () 后面是可以跟 : 的,這一般很少被使用吧。