官术网_书友最值得收藏!

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這里輸出的opcode均省略了ZEND_前綴,例如ASSIGN的實際定義為ZEND_ASSIGN,其值為38。

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)試后,下面介紹幾種不同平臺上的代碼閱讀工具,基于它們,可以有效地提高源碼閱讀的效率。

主站蜘蛛池模板: 彭阳县| 定兴县| 天长市| 蒙山县| 静乐县| 乌苏市| 泰顺县| 抚宁县| 张家川| 博客| 色达县| 盐亭县| 乌拉特后旗| 南充市| 旬阳县| 建宁县| 信丰县| 松江区| 浦江县| 香河县| 郯城县| 铜川市| 门头沟区| 泉州市| 承德市| 醴陵市| 平舆县| 马山县| 土默特右旗| 民权县| 汉川市| 灵台县| 洛隆县| 桂林市| 桃源县| 敦煌市| 安溪县| 鄂伦春自治旗| 德令哈市| 霍山县| 聊城市|