## PHP源碼目錄結構
俗話講:重劍無鋒,大巧不工。PHP的源碼在結構上非常清晰。下面先簡單介紹一下PHP源碼的目錄結構。
- **根目錄: /** 這個目錄包含的東西比較多,主要包含一些說明文件以及設計方案。 其實項目中的這些README文件是非常值得閱讀的例如:
- /README.PHP4-TO-PHP5-THIN-CHANGES 這個文件就詳細列舉了PHP4和PHP5的一些差異。
- 還有有一個比較重要的文件/CODING_STANDARDS,如果要想寫PHP擴展的話,這個文件一定要閱讀一下,不管你個人的代碼風格是什么樣,怎么樣使用縮進和花括號,既然來到了這樣一個團體里就應該去適應這樣的規范,這樣在閱讀代碼或者別人閱讀你的代碼是都會更輕松。
- **build** 顧名思義,這里主要放置一些和源碼編譯相關的一些文件,比如開始構建之前的buildconf腳本等文件,還有一些檢查環境的腳本等。
- **ext** 官方擴展目錄,包括了絕大多數PHP的函數的定義和實現,如array系列,pdo系列,spl系列等函數的實現,都在這個目錄中。個人寫的擴展在測試時也可以放到這個目錄,方便測試和調試。
- **main** 這里存放的就是PHP最為核心的文件了,主要實現PHP的基本設施,這里和Zend引擎不一樣,Zend引擎主要實現語言最核心的語言運行環境。
- **Zend** Zend引擎的實現目錄,比如腳本的詞法語法解析,opcode的執行以及擴展機制的實現等等。
- **pear** “PHP 擴展與應用倉庫”,包含PEAR的核心文件。
- **sapi** 包含了各種服務器抽象層的代碼,例如apache的mod_php,cgi,fastcgi以及fpm等等接口。
- **TSRM** PHP的線程安全是構建在TSRM庫之上的,PHP實現中常見的*G宏通常是對TSRM的封裝,TSRM(Thread Safe Resource Manager)線程安全資源管理器。
- **tests** PHP的測試腳本集合,包含PHP各項功能的測試文件
- **win32** 這個目錄主要包括Windows平臺相關的一些實現,比如sokcet的實現在Windows下和*Nix平臺就不太一樣,同時也包括了Windows下編譯PHP相關的腳本。
PHP的測試比較有意思,它使用PHP來測試PHP,測試php腳本在/run-tests.php,這個腳本讀取tests目錄中phpt文件。讀者可以打開這些看看,php定義了一套簡單的規則來測試,例如以下的這個測試腳本/tests/basic/001.phpt:
~~~
--TEST--
Trivial "Hello World" test
--FILE--
<?php echo "Hello World"?>
--EXPECT--
Hello World
~~~
這段測試腳本很容易看懂,執行--FILE--下面的PHP文件,如果最終的輸出是--EXPECT--所期望的結果則表示這個測試通過,可能會有讀者會想,如果測試的腳本不小心觸發Fatal Error,或者拋出未被捕獲的異常了,因為如果在同一個進程中執行,測試就會停止,后面的測試也將無法執行,php中有很多將腳本隔離的方法比如:system(),exec()等函數,這樣可以使用主測試進程服務調度被測腳本和檢測測試結果,通過這些外部調用執行測試。php測試使用了[proc_open()函數](http://www.php.net/manual/en/function.proc-open.php),這樣就可以保證測試腳本和被測試腳本之間能隔離開。phpt文件的編寫詳細信息可參考 附錄[E phpt文件的編寫](#)。如果你真的那么感興趣,那么研究下`$PHP_SRC/run-tests.php`腳本的實現也是不錯的選擇。這個測試框架剛開始由PHP的發明者Rasmus Lerdorf編寫,后來進行了很多的改進。后面可能會引入[并行測試](http://marc.info/?l=php-internals&m=133609491526265)的支持。
## PHP源碼閱讀工具
### 使用VIM + Ctags
通常在Linux或其他*Nix環境我們都使用[VIM](http://www.vim.org/)作為代碼編輯工具,在純命令終端下,它幾乎是無可替代的。它具有非常強大的擴展機制,在文字編輯方面基本上無所不能。不過Emacs用戶請不要激動,筆者還沒有真正使用Emacs,雖然我知道它甚至可以[煮咖啡](http://people.ku.edu/~syliu/shredderyin/emacs_power.html),還是等筆者有時間了或許會試試煮杯咖啡邊喝邊寫。
推薦在Linux下編寫代碼的讀者或多或少的試一試[ctags](http://ctags.sourceforge.net/)。ctags支持非常多的語言,可以將源代碼中的各種符號(如:函數、宏類等信息)抽取出來做上標記并保存到一個文件中,供其他文本編輯工具(VIM,EMACS等)進行檢索。它保存的文件格式符合[UNIX的哲學(小即是美)](http://zh.wikipedia.org/zh/Unix%E5%93%B2%E5%AD%A6),使用也比較簡潔:
~~~
#在PHP源碼目錄(假定為/server/php-src)執行:
$ cd /server/php-src
$ ctags -R
?
#小技巧:在當前目錄生成的tags文件中使用的是相對路徑,
#若改用 ctags -R /server/ ,可以生成包含完整路徑的ctags,就可以隨意放到任意文件夾中了。
?
#在~/.vimrc中添加:
set tags+=/server/php-src/tags
#或者在vim中運行命令:
:set tags+=/server/php-src/tags
~~~
上面代碼會在/sever/php-src目錄下生成一個名為tags的文件,這個文件的[格式如下](http://ctags.sourceforge.net/FORMAT):
~~~
{tagname}<Tab>{tagfile}<Tab>{tagaddress}
?
EG Zend/zend_globals_macros.h /^# define EG(/;" d
~~~
它的每行是上面的這樣一個格式,第一列是符號名(如上例的EG宏),第二列是該符號的文件位置以及這個符號所在的位置。VIM可以讀取tags文件,當我們在符號上(可以是變量名之類)使用**CTRL+]**時VIM將嘗試從tags文件中檢索這個符號。如果找到則根據該符號所在的文件以及該符號的位置打開該文件,并將光標定位到符號定義所在的位置。 這樣我們就能快速的尋找到符號的定義。
使用 **Ctrl+]** 就可以自動跳轉至定義,**Ctrl+t** 可以返回上一次查看位置。這樣就可以快速的在代碼之間“游動”了。
習慣這種瀏覽代碼的方式之后,大家會感覺很方便的。不過若你不習慣使用VIM這類編輯器,也可以看看下面介紹的[IDE](http://zh.wikipedia.org/wiki/%E9%9B%86%E6%88%90%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83)。
> 如果你使用的Mac OS X,運行ctags程序可能會出錯,因為Mac OS X自帶的ctags程序有些[問題](http://adamyoung.net/Exuberant-Ctags-OS-X), 所以需要自己下載安裝ctags,筆者推薦使用[homebrew](https://github.com/mxcl/homebrew)來安裝。 如果執行還是會出錯,請執行下`ctags -v 或著 which ctags`確保你執行的是新安裝的ctags。
### 使用IDE查看代碼
如果不習慣使用VIM來看代碼,也可以使用一些功能較豐富的IDE,比如Windows下可以使用Visual Studio 2010 Express。或者使用跨平臺的[Netbeans](http://www.netbeans.org/)、[Eclipse](http://www.eclipse.org/)來查看代碼,當然,這些工具都相對較**重量級**一些,不過這些工具不管是調試還是查看代碼都相對較方便,
在Eclipse及Netbeans下查看符號定義的方式通常是將鼠標移到符號上,同時按住**CTRL**,然后單擊,將會跳轉到符號定義的位置。
而如果使用VS的話, 在win32目錄下已經存在了可以直接打開的工程文件,如果由于版本原因無法打開,可以在此源碼目錄上新建一個基于現有文件的Win32 Console Application工程。
**常用快捷鍵**:
~~~
F12 轉到定義
CTRL + F12轉到聲明
?
F3: 查找下一個
Shift+F3: 查找上一個
?
Ctrl+G: 轉到指定行
?
CTRL + -向后定位
CTRL + SHIFT + -向前定位
~~~
對于一些搜索類型的操作,可以考慮使用Editplus或其它文本編輯工具進行,這樣的搜索速度相對來說會快一些。 如果使用Editplus進行搜索,一般是選擇 【搜索】 中的 【在文件中查找...】
- 第一章 準備工作和背景知識
- 第一節 環境搭建
- 第二節 源碼結構、閱讀代碼方法
- 第三節 常用代碼
- 第四節 小結
- 第二章 用戶代碼的執行
- 第一節 生命周期和Zend引擎
- 第二節 SAPI概述
- Apache模塊
- 嵌入式
- FastCGI
- 第三節 PHP腳本的執行
- 詞法分析和語法分析
- opcode
- opcode處理函數查找
- 第四節 小結
- 第三章 變量及數據類型
- 第一節 變量的結構和類型
- 哈希表(HashTable)
- PHP的哈希表實現
- 鏈表簡介
- 第二節 常量
- 第三節 預定義變量
- 第四節 靜態變量
- 第五節 類型提示的實現
- 第六節 變量的生命周期
- 變量的賦值和銷毀
- 變量的作用域
- global語句
- 第七節 數據類型轉換
- 第八節 小結
- 第四章 函數的實現
- 第一節 函數的內部結構
- 函數的內部結構
- 函數間的轉換
- 第二節 函數的定義,傳參及返回值
- 函數的定義
- 函數的參數
- 函數的返回值
- 第三節 函數的調用和執行
- 第四節 匿名函數及閉包
- 第五節 小結
- 第五章 類和面向對象
- 第一節 類的結構和實現
- 第二節 類的成員變量及方法
- 第三節 訪問控制的實現
- 第四節 類的繼承,多態及抽象類
- 第五節 魔術方法,延遲綁定及靜態成員
- 第六節 PHP保留類及特殊類
- 第七節 對象
- 第八節 命名空間
- 第九節 標準類
- 第十節 小結
- 第六章 內存管理
- 第一節 內存管理概述
- 第二節 PHP中的內存管理
- 第三節 內存使用:申請和銷毀
- 第四節 垃圾回收
- 新的垃圾回收
- 第五節 內存管理中的緩存
- 第六節 寫時復制(Copy On Write)
- 第七節 內存泄漏
- 第八節 小結
- 第七章 Zend虛擬機
- 第一節 Zend虛擬機概述
- 第二節 語法的實現
- 詞法解析
- 語法分析
- 實現自己的語法
- 第三節 中間代碼的執行
- 第四節 PHP代碼的加密解密
- 第五節 小結
- 第八章 線程安全
- 第二節 線程,進程和并發
- 第三節 PHP中的線程安全
- 第九章 錯誤和異常處理
- 第十章 輸出緩沖
- 第十六章 PHP語言特性的實現
- 第一節 循環語句
- foreach的實現
- 第二十章 怎么樣系列(how to)
- 附錄
- 附錄A PHP及Zend API
- 附錄B PHP的歷史
- 附錄C VLD擴展使用指南
- 附錄D 怎樣為PHP貢獻
- 附錄E phpt測試文件說明
- 附錄F PHP5.4新功能升級解析
- 附錄G:re2c中文手冊