- PHP 7底層設(shè)計與源碼實現(xiàn)
- 陳雷等
- 784字
- 2019-01-04 16:53:41
1.2 PHP 7安裝和調(diào)試
學(xué)習(xí)了PHP 7的新特性后,再來了解PHP 7的編譯安裝和調(diào)試方式。
1.2.1 編譯安裝
以Linux環(huán)境為例來進行安裝。
首先下載PHP 7。在http://php.net/releases/上能夠獲取各個版本的PHP源碼和修改記錄(建議對PHP源碼感興趣的讀者關(guān)注一下修改記錄,以了解PHP源碼開發(fā)者的開發(fā)思路)。本書以7.1.0版本為例,下載源碼包并編譯安裝(源碼包URL為http://cn2.php.net/distributions/php-7.1.0.tar.gz)。
$ wget http://cn2.php.net/distributions/php-7.1.0.tar.gz $ tar -zxvf php-7.1.0.tar.gz $ cd php-7.1.0 $ ./configure --prefix=$HOME/php7/book/php-7.1.0/output --enable-fpm
注意:默認情況下,make install命令會把執(zhí)行文件和庫文件安裝到/usr/local/bin和/usr/local/lib目錄。為了后續(xù)研究方便,我們使用--prefix將PHP 7安裝到當(dāng)前目錄的output目錄下,同時安裝php-fpm。
執(zhí)行make命令:
$ make && make install $ cd output $ ls bin etc include lib php sbin var
到此,完成了php-7.1.0的編譯安裝,生成的可執(zhí)行文件php-fpm在sbin中,其他部分在bin目錄下:
pear peardev pecl phar phar.phar php php-cgi php-config phpdbg phpize
其中,php是CLI模式下的PHP腳本執(zhí)行程序。
PEAR(PHP Extension and Application Repository, PHP擴展與應(yīng)用庫),是PHP官方開源類庫,可以使用pear list列出所有已經(jīng)安裝的包。通過pear install可以安裝需要的包。
PECL是PHP的擴展庫,可以通過PEAR的Package Manager的管理方式來下載和安裝擴展代碼。
以安裝yaconf為例:
$ ./pecl install yaconf ... install ok: channel://pecl.php.net/yaconf-1.0.6 configuration option "php_ini" is not set to php.ini location You should add "extension=yaconf.so" to php.ini
php-config是輸出PHP編譯信息的輔助命令。
phpdbg是一個輕量級,具有豐富功能的調(diào)試平臺。PHP 5.4以上版本支持,比如可以使用它查看opcode:
$ phpdbg -p* t.php function name: (null) L1-5 {main}() L2 #0 ASSIGN $a 1 L3 #1 ECHO $a L5 #2 RETURN 1
phpdbg的其他功能可以通過phpdbg --help查看。
phpize命令用來動態(tài)安裝擴展,如果在安裝PHP時沒有安裝某個擴展,可以通過這個命令隨時安裝。
1.2.2 使用GDB調(diào)試PHP 7
GDB是一個由GNU開源組織發(fā)布的、UNIX/Linux操作系統(tǒng)下的、基于命令行的、功能強大的程序調(diào)試工具。當(dāng)程序發(fā)生coredump,通過GDB可以從core文件中復(fù)現(xiàn)場景,定位問題。
這里演示一下如何通過GDB來調(diào)試PHP程序。首先編寫一段簡單的代碼test.php:
<? php $a = '1'; echo $a;
下面開始進行GDB調(diào)試,運行g(shù)db php:
$ gdb php (gdb)
使用b命令在main函數(shù)入口增加斷點:
(gdb) b main Breakpoint 1 at 0x797df0: file /home/vagrant/php7/php-7.1.0/sapi/cli/php_cli.c, line 1181.
使用r命令運行test.php:
(gdb) r test.php Starting program: /home/vagrant/php7/php-7.1.0/output/bin/php test.php [Thread debugging using libthread_db enabled] Breakpoint 1, main (argc=2, argv=0x7fffffffe1b8) at /home/vagrant/php7/php-7.1.0/ sapi/cli/php_cli.c:1181 1181 {
從上面的輸出中可以看到,代碼執(zhí)行在main函數(shù)處停止。接下來,使用n命令執(zhí)行下一步:
(gdb) n 1288 memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
使用p命令查看某個變量的信息:
(gdb) p ini_entries $1 = 0x10c2150 "html _errors=0\nregister_argc_argv=1\nimplicit_flush=1\noutput_buffering=0\nmax_ execution_time=0\nmax_input_time=-1\n" (gdb)
如果出現(xiàn)<value optimized out>,是由于GCC編譯器在編譯過程中默認使用-O2優(yōu)化選項所致,使用-O0選項可以關(guān)閉編譯器的優(yōu)化。在這里,通過修改MakeFile禁止編譯器優(yōu)化。查找CFLAGS_CLEAN:
CFLAGS_CLEAN = -I/usr/include -g -O2-fvisibility=hidden -DZEND_SIGNALS $(PROF_ FLAGS)
將其中的-O2改為-O0,然后執(zhí)行make clean && make && make install。
另外,對于在php-fpm下運行的PHP程序如何調(diào)試呢?
在本地建立一個名為www.local的本地項目,來演示php-fpm運行模式下的調(diào)試:
$ mkdir /data/htdocs/www.local $ touch /data/htdocs/www.local/index.php
略過Nginx的配置過程,著重看一下php-fpm的配置:
$ vim ~/php7/book/php-7.1.0/output/conf/php-fpm.conf // 添加以下配置項 [www.local] pm=static pm.max_children=1 pm.start_servers=1 pm.min_spare_servers=1 pm.max_spare_servers=1
這段配置設(shè)定php-fpm的運行模式為static,其最大進程數(shù)為1、啟動進程數(shù)為1、最大和最小的空余進程數(shù)為1。為什么要這么設(shè)定呢?這是為了保證GDB調(diào)試的進程一定是我們當(dāng)前訪問的進程。
完成Nginx和php-fpm的配置以后,重啟這兩個服務(wù),可以看到www.local的項目只有一個進程,其pid為4459(后文還會用到該pid):
$ systemctl restart php-fpm.service $ systemctl restart nginx.service $ ps aux|grep php-fpm root 4458 0.0 0.8343816 4056 ? Ss 13:11 0:00 php-fpm: master process (/home/vagrant/php7/book/php-7.1.0/output/conf/php-fpm.conf) www 4459 0.0 1.0344012 5140 ? S 13:11 0:00 php-fpm: pool www.local
接下來開始調(diào)試,執(zhí)行如下命令:
$ gdb php (gdb) attach 4459 Attaching to process 4459 Reading symbols ...
如果沒有報錯,當(dāng)前GDB已經(jīng)attach到www.local的php-fpm進程上了。新開一個終端2執(zhí)行“curl www.local”或者使用瀏覽器訪問www.local,然后回到終端1,就可以和CLI模式一樣進行調(diào)試了。
在學(xué)習(xí)和研究PHP 7的過程中,經(jīng)常需要查看opcodes,除了上文提到的phpdbg可以查看,另外還有一個vld擴展也非常好用,下面介紹下vld擴展。
1.2.3 vld擴展
PHP代碼的執(zhí)行實際上是在執(zhí)行代碼解析后的各種opcode。通過vld擴展可以很方便地看到執(zhí)行過程中的opcode。擴展可以從https://github.com/derickr/vld下載安裝,下面是安裝示例:
$ git clone https://github.com/derickr/vld.git $ cd vld $ /home/vagrant/php7/book/php-7.1.0/output/phpize $ ./configure --with-php-config=/home/vagrant/php7/book/php-7.1.0/output/php- config --enable-vld $ make && make install
到這里,擴展就安裝完成了,接下來只需要在PHP的配置文件php.ini中啟用該擴展即可:
extension=vld.so
然后執(zhí)行下邊的命令:
$ php -m | grep vld
看到有vld的輸出,即表示擴展啟用成功。
現(xiàn)在來寫一段簡單的PHP代碼,看看生成的opcode:
<? php $str = 'hello php7'; var_dump($str);
保存這段代碼為vld.php,然后在命令行執(zhí)行:
$ php -dvld.active=1 vld.php Finding entry points Branch analysis from position: 0 Jump found. (Code = 62) Position 1 = -2 filename: /home/vagrant/vld.php function name: (null) number of ops: 5 compiled vars: !0 = $str line #* E I O op fetch ext return operands ------------------------------------------------------------------- 3 0 E > ASSIGN !0, 'hello+php7' 4 1 INIT_FCALL 'var_dump' 2 SEND_VAR !0 3 DO_ICALL 6 4 > RETURN 1 branch: # 0; line: 3- 6; sop: 0; eop: 4; out1: -2 path #1: 0, string(10) "hello php7"
從上邊的輸出可以看到這段代碼一共有5個opcode。
vld擴展有下邊幾個參數(shù)。
1)vld.active:是否在執(zhí)行PHP的同時激活vld——1激活,0不激活(默認不激活)。
2)vld.execute:是否輸出程序的執(zhí)行結(jié)果——1輸出,0不輸出(默認輸出)。
3)vld.verbosity:顯示更詳細的opcode信息,開啟后可以顯示每個opcode的操作數(shù)的類型等信息。
例如:
3 0 E > ASSIGN OP1[IS_CV !0 ] OP2[IS_CONST (0) 'hello+php7' ]
4)vld.skip_prepend:是否跳過php.ini配置文件中auto_prepend_file配置項指定的文件,默認為0,即不跳過包含的文件。vld.execute為0時有效;
5)vld.skip_append:是否跳過php.ini配置文件中auto_append_file指定的文件,默認為0,即不跳過包含的文件。vld.execute為0時有效;
6)vld.format:是否啟用自定義輸出格式——1啟用,0不啟用(默認不啟用);
7)vld.col_sep:自定義輸出格式間隔符,vld.format為1時有效;
8)vld.save_dir:指定文件輸出的路徑,默認路徑為/tmp;
9)vld.save_paths:控制是否輸出dot語言文件,默認為0,表示不輸出;
10)vld.dump_paths:控制是否輸出分支及路徑信息——1輸出,0不輸出(默認輸出)。
小知識
dot是一種描述圖形的語言,可以由Graphviz工具包來繪制dot描述的圖形。vld擴展可以直接通過命令來生成dot腳本,現(xiàn)以下面的代碼來演示一下:
$ vim vld.php <? php class Test{ public $num; public function __construct($num){ $this->num = $num; } public function increase(){ return $this->num + 1; } } $a = new Test(10); var_dump($a->increase());
在命令行執(zhí)行以下命令:
$ php -dvld.active=1-dvld.save_paths=1 vld.php $ ll /tmp -rw-rw-r--1 vagrant vagrant 791 11月 30 02:41 paths.dot $ dot -Tpng /tmp/paths.dot -o paths.png
這樣就可以生成一張調(diào)用圖片,如圖1-1所示。

圖1-1 vld生成的圖片
介紹完P(guān)HP 7的安裝和調(diào)試后,下面介紹幾種不同平臺上的代碼閱讀工具,基于它們,可以有效地提高源碼閱讀的效率。
- C++ Primer習(xí)題集(第5版)
- 移動UI設(shè)計(微課版)
- Programming ArcGIS 10.1 with Python Cookbook
- 區(qū)塊鏈:以太坊DApp開發(fā)實戰(zhàn)
- TypeScript圖形渲染實戰(zhàn):基于WebGL的3D架構(gòu)與實現(xiàn)
- Swift語言實戰(zhàn)精講
- Angular開發(fā)入門與實戰(zhàn)
- PHP+MySQL+Dreamweaver動態(tài)網(wǎng)站開發(fā)從入門到精通(第3版)
- Android項目實戰(zhàn):手機安全衛(wèi)士開發(fā)案例解析
- Ext JS 4 Plugin and Extension Development
- Java EE輕量級解決方案:S2SH
- HTML5+CSS3+jQuery Mobile+Bootstrap開發(fā)APP從入門到精通(視頻教學(xué)版)
- Flutter從0基礎(chǔ)到App上線
- Serverless工程實踐:從入門到進階
- Access 2016數(shù)據(jù)庫應(yīng)用與開發(fā):實戰(zhàn)從入門到精通(視頻教學(xué)版)