PHP调试技术手册

1内置api输出调试

1.1基本调试api

  • echo
  • printf
  • print_r
    /*
    %% - 返回百分比符号
    %b - 二进制数
    %c - 依照 ASCII 值的字符
    %d - 带符号十进制数
    %e - 可续计数法(比如 1.5e+3)
    %u - 无符号十进制数
    %f - 浮点数(local settings aware)
    %F - 浮点数(not local settings aware)
    %o - 八进制数
    %s - 字符串
    %x - 十六进制数(小写字母)
    %X - 十六进制数(大写字母)
    */
    
    $str = "Hello";
    $number = 123;
    $txt = sprintf("%s world. Day number %u", $str, $number);
    echo $txt;
    
  • var_dump
  • var_export
    • 所有的数据是可以作为组织好的变量输出的,都是能够作为直接赋值使用
    • 对资源型变量输出NULL
  • debug_zval_dump
    • 跟 var_dump 类似,唯一增加的一个值是 refcount,就是记录一个变量被引
      用了多少次
    • PHP COW机制(copy on write)
    foreach($arr as &$v){
        $count++;
        $v='bbbbbb';// 无论是否注释这行, 内存变化不会太大
    }
    
    foreach($arr as $v){
        $count++;
        $v='bbbbbb';// 不注释这行,内存使用上升,因为COW机制
    }    
    
    /*
    在复制一个对象的时候并不是真正的把原先的对象复制到内存的另外一个位置上,
    而是在新对象的内存映射表中设置一个指针,指向源对象的位置,
    并把那块内存的Copy-On-Write位设置为1.这样,在对新的对象执行读操作的时候,
    内存数据不发生任何变动,直接执行读操作;而在对新的对象执行写操作时,
    将真正的对象复制到新的内存地址中,并修改新对象的内存映射表指向这个新的位置,
    并在新的内存位置上执行写操作    
    */
    

1.2错误控制与日志记录调试

  • error_reporting

  • display_errors

  • log_errors 是否记录错误日志

  • error_log 指定错误日志位置

  • trigger_error(主要是能够触
    发的是 E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE 三种级别的错误)

function e($num) {
    if (!is_int($num)) {
        trigger_error('num nust a int',E_USER_WARNING);
        return false;
    }
    echo 'ok';
}
e('test');
  • set_error_handler
    • 以下级别的错误不能由用户定义的函数来处理: E_ERROR 、 E_PARSE 、 E_CORE_ERROR 、 E_CORE_WARNING 、 E_COMPILE_ERROR 、 E_COMPILE_WARNING
set_error_handler('custom_error_handler');
function custom_error_handler($err_no,$err_str,$err_file,$err_line) {
    // 定义日志路径(区分ERROR,WARNING,NOTICE)
    $log_path = 'php_log_%s'.date('Ymd').'.log';
    // 错误信息模版(区分ERROR,WARNING,NOTICE)
    $log_template = date('Y-m-d H:i:s') . "Erro Type:%s,on $err_file->$err_line:$err_str";
    switch($err_no) {
        case E_USER_ERROR:
        $log_path = sprintf($log_path,'error');
        $log_str = sprintf($log_template,'E_USER_ERROR');
        break;
        case E_NOTICE:
        case E_USER_NOTICE:
        $log_path = sprintf($log_path,'notice');
        $log_str = sprintf($log_template,'E_NOTICE|E_USER_NOTICE');        
        break;
        case E_WARNING:
        case E_USER_WARINING:
        $log_path = sprintf($log_path,'warning');
        $log_str = sprintf($log_template,'E_WARNING|E_USER_WARNING');  
        break;
    }
    // file_put_contents 文件不存在则创建
    file_put_contents($log_path,$log_str,FILE_APPEND|LOCK_EX);
}
  • set_exception_handler : 处理出现未捕获的异
    常之后需要调用的处理方法

  • 日志记录

    • 日志记录除了PHP解析级别的错误,更多的是我们程序在执行过程中的一些错误,比如 文件资源打开错误(文件不存在,没有权限,文件格式不正确),远程服务资源访问失败(网络错误,协议不正确,用户/密码错误)等等, 任何你认为不会出错的地方都输出LOG
    • 简单日志记录类
    class MyLog 
    {
        const LOG_ERROR = 1;
        const LOG_WARNING = 2;
        const LOG_NOTICE = 3;
        private $str = '';
        private $log_path = 'php_log'.date('Ymd').'.log';
        
        public function log($msg,$level) 
        {
            switch ($level) {
                case self::LOG_ERROR:
                    $this->str .= date('Y-m-d H:i:s') . ' ERROR:' . $msg . "\n";
                break;
                case self::LOG_WARNING:
                    $this->str .= date('Y-m-d H:i:s') . ' WARNING:' . $msg . "\n";
                break;
                case self::LOG_NOTICE:
                    $this->str .= date('Y-m-d H:i:s') . ' NOTICE:' . $msg . "\n";
                break;
                default:
                    return;
            }
        }
        public function __destruct() 
        {
            if ($this->str) {
                file_put_contents($this->str,$this->log_path,FILE_APPEND|LOCK_EX);            
            }
        }
    }
    
    • 使用error_log作日志记录
    // 如果无法连接到数据库,发送通知到服务器日志
    if (! Ora_Logon ( $username ,  $password )) {
         error_log ( "Oracle database not available!" ,  0 );
    }
    
    // 如果用尽了 FOO,通过邮件通知管理员
    if (!( $foo  =  allocate_new_foo ())) {
         error_log ( "Big trouble, we're all out of FOOs!" ,  1 , "operator@example.com" );
    }
    
    // 调用 error_log() 的另一种方式:
    error_log ( "You messed up!" ,  3 ,  "/var/tmp/my-errors.log" );
    

2浏览器调试

2.1页面输出调试

...各种ide+调试

4PHP性能调试技术

4.1基本时间占用监测

4.2XDEBUG性能监测

  • 配置

    • xdebug.trace_output_dir trace 数据输出目录
    • xdebug.profiler_output_dir 输出监控结果文件目录
    • xdebug.profiler_output_name 如果设置本选项,那么每次结果都只会输出到一个文件
    • xdebug.auto_trace 是否打开 trace
    • xdebug.auto_profile 是否打开性能监测
  • 打开xdebug的性能监测后, 在config.php中指定目录

4.4 使用 Xhprof 进行性能分析

  • 安装xhprof

  • php-config 是一个简单的命令行脚本用于获取所安装的 PHP 配置的信息。

    • 在编译扩展时,如果安装有多个 PHP 版本,可以在配置时用 --with-php-config 选项来指定使用哪一个版本编译,该选项指定了相对应的 php-config 脚本的路径
    wget http://pecl.php.net/get/xhprof-0.9.3.tgz
    
    tar zxf xhprof-0.9.3.tgz
    
    cd xhprof-0.9.3/extension/
    
    phpize
    
    ./configure --with-php-config=/usr/local/php/bin/php-config
    
    make && make install
    
  • 配置

[xhprof]
extension=xhprof.so
;
; directory used by default implementation of the iXHProfRuns
; interface (namely, the XHProfRuns_Default class) for storing
; XHProf runs.
;
xhprof.output_dir=<directory_for_storing_xhprof_runs>

// 确认web进程对目录具有写权限
  • 分析代码
// start profiling
xhprof_enable();

// run program
function test1() {
    sleep(3);

    return;
}

function test2() {
    test1();
}

function test3() {
    test2();
}

echo "<h3>Xdebug profiler test</h3>";
test3();

// stop profiler
$data = xhprof_disable();

include_once "path/to/xhprof_lib/utils/xhprof_lib.php";
include_once "path/to/xhprof_lib/utils/xhprof_runs.php";
$xhprof_runs = new XHProfRuns_Default();
$run_id = $xhprof_runs->save_run($data,'test');
var_dump($run_id);

// 访问 http://119.29.149.18/www/xhprof_html/?run=570d00e3007a0&source=test

5 使用php扩展taint(防止xss,sql inject)

+ https://github.com/laruence/taint

6. SocketLog -- 微信调试、API调试和AJAX的调试的工具

+ https://github.com/luofei614/SocketLog

标签: none