如何學習 PHP 原始碼 – 從編譯開始

Scholer發表於2019-02-16

PHP Mailing Lists 上這兩天有個好玩兒的問題:Introduction to the PHP source code,大概就是有人想知道如何學習 PHP 原始碼,可是這種事情不是應該自己去發掘的嗎?

上面是玩笑話,現在我也說說如何學習 PHP 直譯器的原始碼。

首選你要知道的是 PHP 直譯器原始碼的 github 地址:https://github.com/php/php-src ,話說回來還有人不知道嗎?這裡有幾乎所有 PHP 的程式碼提交記錄、pull requests 和一些 issue 等。

建立編譯指令碼或者釋出包

從 Branch 中選擇一個版本 tag,和每次 PHP 釋出出來的版本就是一致的。也許你會發現你想編譯的的時候缺找不到 configure 檔案,但是有 configure.in 檔案。這時候需要先執行的是 buildconf(如果是在 Windows 下面可以執行 buildconf.bat,不過我從來沒有嘗試過在 Windows 下面編譯 PHP,所以具體的步驟我就不清楚了)。buildconf 本身是個簡單的 shell 指令碼,你可以用記事本開啟看看它(最終的執行檔案在 build 目錄裡,這個目錄裡有一些與編譯有關的檔案)。

這裡面涉及到一個系列的編譯工具:Autotools。如果你有興趣,可以簡單的瞭解一下,沒有興趣的話也不用多考慮,因為這些工具絕大多數 Linux 系統上都是已經存在的。

如果你想將 Github 上的 PHP 原始碼做成一個可釋出的原始碼包,你可以看看 makedist 這個檔案,它也是一個 shell 指令碼(實際上原始碼裡幾乎所有跟編譯相關的指令碼都是 shell 指令碼)。但是如果想直接執行者這個指令碼,你可能會收到缺少以下元件的提示:re2cBison。仔細看 makedist 的檔案,裡面有呼叫 genfiles 這個指令碼的語句,上面兩個工具就是在 genfiles 的指令碼里被呼叫的。

re2c 和 Bison 分別是 PHP 用到的詞法解析器和語法分析器。在 genfiles 這個檔案中可以看到它們的呼叫其實是在 Makefile.frag 中寫著,分別通過 zend_language_scanner.lzend_language_parser.y 生成相應的 C 語言檔案(這個應該很多地方都有提到過)。

編譯直譯器並初始化

到了編譯環節,編譯之前需要先通過 configure 檔案生成 Makefile 然後執行 make,所以 gcc 自然是必不可少的。configure 檔案本身也是一個 shell 指令碼,你也可以簡單閱讀一下它的內容。不過既然它是由 autoconfconfigure.in 中生成的,也許直接檢視 configure.in 會更輕鬆一些。

到這裡總結一下就是:拋開一些核心擴充套件額依賴(比如 xml,ssl 等),編譯 PHP 的先決條件是機器上有 Autotools 的工具(automake,autoconf 等),需要安裝 re2c 和 Bison,當然還有編譯工具(gcc)。

也許大家都知道,使用 configure 生成 Makefile 的時候可以通過 --prefix 引數指定目錄,同時也可以選擇編譯哪些核心模組。至於哪些模組會被預設整合而哪些不會,這些本身是寫在每個擴充套件的 config.m4 (也有幾個是被命名為 config0.m4 或 config9.m4)檔案裡的的,全都通過一些 --enable--disable--with--without 的選項來控制。

編譯的也與你採用的 Web 伺服器有關,這涉及到你需要使用哪個 sapi,如果是 Apache,也許需要指定 --with-apxs2 的引數,如果是 Nginx,php-fpm 在預設條件下是會被編譯的,但可以指定 php-fpm 的執行組和使用者,不過這個是可以在編譯完成後在配置中修改的。

編譯完成之後還有一些事情需要考慮,最基本的問題是 PHP 的配置檔案的問題,還有一個是如果使用的是 php-fpm,如何更便捷的控制它的啟動、停止以及重啟等。

在 PHP 原始碼根中已經準備了兩份配置檔案的模板:php.ini-developmentphp.ini-production。顯然是分別用於開發環境和生產環境的,將其中一個複製到配置檔案目錄並重新命名為 php.ini 即可(如果你不知道配置檔案的目錄在哪裡,可以使用 php --ini 命令檢視)。然後也可以根據你的需要修改它。

至於 php-fpm 的控制指令碼,原始碼中本身也是有提供的,在 sapi/fpm 目錄下。這個目錄下的幾個檔案中有 php-fpm 配置檔案的模板,也有稍微修改即可放到伺服器 /etc/init.d 目錄下用於控制 php-fpm 的 startstoprestartreload 動作的指令碼,現在的版本中也提供了用於 systemd 的 service 檔案。

擴充套件編譯

如果 PHP 編譯完成之後,發現還需要一些沒有編譯進去的核心擴充套件或者第三方擴充套件,你可以單獨編譯它們。

擴充套件編譯的整個過程一共四句命令:

  1. phpize

  2. ./configure

  3. make

  4. make install

phpize 命令是用來準備 PHP 擴充套件庫的編譯環境的。在執行 phpize 的時候,如果有多個版本的 PHP,用哪個就要選哪個。這個命令和編譯後的 php 的二進位制檔案在同一個目錄中,也是一個 shell 指令碼。

執行 configure 的時候,如果當前 $PATH 中找不到 php-config 或者有多個版本的 PHP 時,也需要通過 --with-php-config 的指令來指定 php-config 的目錄。php-config 是一個用於獲取所安裝的 PHP 配置的資訊,它也一樣是和 php 的二進位制檔案在同一個目錄的 shell 指令碼。

phpize 和 php-config 的原始碼生成檔案都是在 scripts 目錄下。

所有工作完成之後,就可以愉快的使用你自己定製的 PHP 了。

原文地址:http://0x1.im

相關文章