ThinkPHP3.2应用调试

Published on 2016 - 11 - 28

调试模式

ThinkPHP有专门为开发过程而设置的调试模式,开启调试模式后,会牺牲一定的执行效率,但带来的方便和除错功能非常值得。

我们强烈建议ThinkPHP开发人员在开发阶段始终开启调试模式(直到正式部署后关闭调试模式),方便及时发现隐患问题和分析、解决问题。
开启调试模式很简单,只需要在入口文件中增加一行常量定义代码:

<?php
 // 开启调试模式
 define('APP_DEBUG', true);
 // 定义应用目录
 define('APP_PATH', './Application/');
 // 加载框架入口文件
 require './ThinkPHP/ThinkPHP.php';

在完成开发阶段部署到生产环境后,只需要关闭调试模式或者删除调试模式定义代码即可切换到部署模式。

<?php
// 关闭调试模式
define('APP_DEBUG', false);
// 定义应用目录
define('APP_PATH', './Application/');
// 加载框架入口文件
require './ThinkPHP/ThinkPHP.php';

调试模式的优势在于:

  • 开启日志记录,任何错误信息和调试信息都会详细记录,便于调试;
  • 关闭模板缓存,模板修改可以即时生效;
  • 记录SQL日志,方便分析SQL;
  • 关闭字段缓存,数据表字段修改不受缓存影响;
  • 严格检查文件大小写(即使是Windows平台),帮助你提前发现Linux部署可能导致的隐患问题;
  • 通过页面Trace功能更好的调试和发现错误;

在开启调试模式的状态下,系统会首先导入框架默认的调试模式配置文件,该文件位于系统目录的Conf\debug.php。

通常情况下,调试配置文件里面可以进行一些开发模式所需要的配置。例如,配置额外的数据库连接用于调试,开启日志写入便于查找错误信息、开启页面Trace输出更多的调试信息等等。

如果检测到应用的配置目录中有存在debug.php文件,则会自动加载该配置文件,并且和系统项目配置文件以及系统调试配置文件合并,也就是说,debug.php配置文件只需要配置和项目配置文件以及系统调试配置文件不同的参数或者新增的参数。

由于调试模式没有任何缓存,因此涉及到较多的文件IO操作和模板实时编译,所以在开启调试模式的情况下,性能会有一定的下降,但不会影响部署模式的性能。另外需要注意的是,一旦关闭调试模式,项目的调试配置文件即刻失效。

一旦关闭调试模式,发生错误后不会提示具体的错误信息,如果你仍然希望看到具体的错误信息,那么可以如下设置:

'SHOW_ERROR_MSG'        =>  true,    // 显示错误信息

异常处理

和PHP默认的异常处理不同,ThinkPHP抛出的不是单纯的错误信息,而是一个人性化的错误页面,如下图所示:

只有在调试模式下面才能显示具体的错误信息,如果在部署模式下面,你可能看到的是一个简单的提示文字,例如:

如果你试图在部署模式下访问一个不存在的模块或者操作,会发送404错误。

调试模式下面一旦系统发生严重错误会自动抛出异常,也可以用ThinkPHP内置的E方法手动抛出异常。

E('新增失败');

也可以支持异常代码(默认为0),例如:

E('信息录入错误',25);

同样也可以使用throw 关键字来抛出异常,下面的写法是等效的:

throw new \Think\Exception('新增失败');

我们可以自定义异常页面的显示,系统内置的异常模板在系统目录的Tpl/think_exception.tpl,可以通过修改系统模板来修改异常页面的显示。也通过设置TMPL_EXCEPTION_FILE配置参数来修改系统默认的异常模板文件, 例如:

'TMPL_EXCEPTION_FILE' => APP_PATH.'/Public/exception.tpl'

异常模板中可以使用的异常变量有:

$e['file']异常文件名
$e['line'] 异常发生的文件行数
$e['message'] 异常信息
$e['trace'] 异常的详细Trace信息

因为异常模板使用的是原生PHP代码,所以还可以支持任何的PHP方法和系统变量使用。

抛出异常后通常会显示具体的错误信息,如果不想让用户看到具体的错误信息,可以设置关闭错误信息的显示并设置统一的错误提示信息,例如:

'SHOW_ERROR_MSG' =>    false,
'ERROR_MESSAGE'  =>    '发生错误!'

设置之后,所有的异常页面只会显示“发生错误!”这样的提示信息,但是日志文件中仍然可以查看具体的错误信息。

系统的默认情况下,调试模式是开启错误信息显示的,部署模式则关闭错误信息显示。
另外一种方式是配置ERROR_PAGE参数,把所有异常和错误都指向一个统一页面,从而避免让用户看到异常信息,通常在部署模式下面使用。ERROR_PAGE参数必须是一个完整的URL地址,例如:

'ERROR_PAGE' =>'/Public/error.html'

如果不在当前域名,还可以指定域名:

'ERROR_PAGE' =>'http://www.myDomain.com/Public/error.html'

注意ERROR_PAGE所指向的页面不能再使用异常的模板变量了。

日志记录

日志的处理工作是由系统自动进行的,在开启日志记录的情况下,会记录下允许的日志级别的所有日志信息。

其中,为了性能考虑,SQL日志级别必须在调试模式开启下有效,否则就不会记录。 系统的日志记录由核心的Think\Log类及其驱动完成,提供了多种方式记录了不同的级别的日志信息。

默认情况下只是在调试模式记录日志,要在部署模式开启日志记录,必须在配置中开启LOG_RECORD参数,以及可以在应用配置文件中配置需要记录的日志级别,例如:

'LOG_RECORD' => true, // 开启日志记录
'LOG_LEVEL'  =>'EMERG,ALERT,CRIT,ERR', // 只记录EMERG ALERT CRIT ERR 错误

日志的记录并非实时保存的,只有当当前请求完成或者异常结束后才会实际写入日志信息,否则只是记录在内存中。

日志级别

ThinkPHP对系统的日志按照级别来分类,包括:

  • EMERG 严重错误,导致系统崩溃无法使用
  • ALERT 警戒性错误, 必须被立即修改的错误
  • CRIT 临界值错误, 超过临界值的错误
  • ERR 一般性错误
  • WARN 警告性错误, 需要发出警告的错误
  • NOTICE 通知,程序可以运行但是还不够完美的错误
  • INFO 信息,程序输出信息
  • DEBUG 调试,用于调试信息
  • SQL SQL语句,该级别只在调试模式开启时有效

记录方式

日志的记录方式默认是文件方式,可以通过驱动的方式来扩展支持更多的记录方式。

记录方式由LOG_TYPE参数配置,例如:

'LOG_TYPE'              =>  'File', // 日志记录类型 默认为文件方式

File方式记录,对应的驱动文件位于系统的Library/Think/Log/Driver/File.class.php。

手动记录

一般情况下,系统的日志记录是自动的,无需手动记录,但是某些时候也需要手动记录日志信息,Log类提供了3个方法用于记录日志。

方法 描述
Log::record() 记录日志信息到内存
Log::save() 把保存在内存中的日志信息(用指定的记录方式)写入
Log::write() 实时写入一条日志信息

由于系统在请求结束后会自动调用Log::save方法,所以通常,你只需要调用Log::record记录日志信息即可。

record方法用法如下:

Think\Log::record('测试日志信息');

默认的话记录的日志级别是ERR,也可以指定日志级别:

Think\Log::record('测试日志信息,这是警告级别','WARN');

record方法只会记录当前配置允许记录的日志级别的信息,如果应用配置为:

'LOG_LEVEL'  =>'EMERG,ALERT,CRIT,ERR', // 只记录EMERG ALERT CRIT ERR 错误

那么上面的record方法记录的日志信息会被直接过滤,或者你可以强制记录:

Think\Log::record('测试日志信息,这是警告级别','WARN',true);

采用record方法记录的日志信息不是实时保存的,如果需要实时记录的话,可以采用write方法,例如:

Think\Log::write('测试日志信息,这是警告级别,并且实时写入','WARN');

write方法写入日志的时候 不受配置的允许日志级别影响,可以实时写入任意级别的日志信息。

页面Trace

调试模式并不能完全满足我们调试的需要,有时候我们需要手动的输出一些调试信息。除了本身可以借助一些开发工具进行调试外,ThinkPHP还提供了一些内置的调试工具和函数。例如,页面Trace功能就是ThinkPHP提供给开发人员的一个用于开发调试的辅助工具。可以实时显示当前页面的操作的请求信息、运行情况、SQL执行、错误提示等,并支持自定义显示。

页面Trace功能对调试模式和部署模式都有效,不过只能用于有页面输出的情况(如果你的操作没有任何输出,那么可能页面Trace功能对你帮助不大,你可能需要使用后面的调试方法)。

在部署模式下面,显示的调试信息没有调试模式完整,通常我们建议页面Trace配合调试模式一起使用。

要开启页面Trace功能,需要在项目配置文件中设置:

// 显示页面Trace信息
'SHOW_PAGE_TRACE' =>true, 

该参数默认为关闭,开启后并且你的页面有模板输出的话,页面右下角会显示ThinkPHP的LOGO:

我们看到的LOGO后面的数字就是当前页面的执行时间(单位是秒) 点击该图标后,会展开详细的页面Trace信息,如图:

页面Trace框架有6个选项卡,分别是基本、文件、流程、错误、SQL和调试,点击不同的选项卡会切换到不同的Trace信息窗口。

选项卡 描述
基本 当前页面的基本摘要信息,例如执行时间、内存开销、文件加载数、查询次数等等。
文件 详细列出当前页面执行过程中加载的文件及其大小。
流程 会列出当前页面执行到的行为和相关流程(待完善)。
错误 当前页面执行过程中的一些错误信息,包括警告错误。
SQL 当前页面执行到的SQL语句信息。
调试 开发人员在程序中进行的调试输出。

页面Trace的选项卡是可以定制和扩展的,默认的配置为:

'TRACE_PAGE_TABS'=>array(
    'base'=>'基本',
     'file'=>'文件',
     'think'=>'流程',
     'error'=>'错误',
     'sql'=>'SQL',
     'debug'=>'调试'
)

也就是我们看到的默认情况下显示的选项卡,如果你希望增加新的选项卡:用户,则可以修改配置如下:

'TRACE_PAGE_TABS'=>array(
     'base'=>'基本',
     'file'=>'文件',
     'think'=>'流程',
     'error'=>'错误',
     'sql'=>'SQL',
     'debug'=>'调试',
     'user'=>'用户'
)

也可以把某几个选项卡合并,例如:

'TRACE_PAGE_TABS'=>array(
     'base'=>'基本',
     'file'=>'文件',
     'think'=>'流程',
     'error|debug|sql'=>'调试',
     'user'=>'用户'
)

我们把刚才的用户信息调试输出到用户选项卡,trace方法的用法如下:

trace($user,'用户信息','user');

第三个参数表示选项卡的标识,和我们在TRACE_PAGE_TABS中配置的对应。 默认情况下,页面Trace窗口显示的信息是不会保存的,如果希望保存这些trace信息,我们可以配置PAGE_TRACE_SAVE参数

'PAGE_TRACE_SAVE'=>true

开启页面trace信息保存后,每次的页面Trace信息会以日志形式保存到项目的日志目录中,命名格式是: 当前日期_trace.log,例如:12-06-21_trace.log

如果不希望保存所有的选项卡的信息,可以设置需要保存的选项卡,例如:

'PAGE_TRACE_SAVE' => array('base','file','sql');

设置后只会保存base、file和sql三个选项卡的信息。

Trace方法

页面Trace只能用于有页面输出的情况,但是trace方法可以用在任何情况,而且trace方法可以用于AJAX等操作。

Trace方法的格式:

trace('变量','标签','级别','是否记录日志')

例如:

$info = '测试信息';
trace($info,'提示');

如果希望把变量调试输出到页面Trace的某个选项卡里面,可以使用:

trace($info,'提示','user');

表示输出到user选项卡,如果没有指定选项卡的话,默认会输出到debug选项卡。 trace方法也可以直接抛出异常,如果是输出到ERR选项卡,并且开启'TRACE_EXCEPTION'=>true的话,

trace($info,'错误','ERR');

会抛出异常。 有三种情况下,trace方法会记录日志:

  1. AJAX请求
  2. SHOW_PAGE_TRACE为false,也就是页面Trace关闭的情况下
  3. trace方法的第四个参数为true

在这种情况下,trace方法的第三个参数就表示记录的日志级别,通常包括:

'ERR'  // 一般错误: 一般性错误
'WARN'  // 警告性错误: 需要发出警告的错误
'NOTIC'  // 通知: 程序可以运行但是还不够完美的错误
'INFO'  // 信息: 程序输出信息
'DEBUG'  // 调试: 调试信息
'SQL'  // SQL:SQL语句

断点调试

凭借强大的页面Trace信息功能支持,ThinkPHP可以支持断点调试功能。 我们只需要在不同的位置对某个变量进行trace输出即可,例如:

 $blog = D("Blog");
 $vo = $blog->create();
 trace($vo,'create vo');
 $vo = $blog->find();
 trace($vo,'find vo');

变量调试

输出某个变量是开发过程中经常会用到的调试方法,除了使用php内置的var_dump和print_r之外,ThinkPHP框架内置了一个对浏览器友好的dump方法,用于输出变量的信息到浏览器查看。

用法:

dump($var, $echo=true, $label=null, $strict=true)

相关参数的使用如下:

参数 描述
var (必须) 要输出的变量,支持所有变量类型
echo (可选) 是否直接输出,默认为true,如果为false则返回但不输出
label (可选) 变量输出的label标识,默认为空
strict (可选) 输出变量类型,默认为true,如果为false则采用print_r输出

如果echo参数为false 则返回要输出的字符串

使用示例:

 $Blog = D("Blog");
 $blog = $Blog->find(3);
 dump($blog);

在浏览器输出的结果是:

array(12) {
   ["id"]            => string(1) "3"
   ["name"]          => string(0) ""
   ["user_id"]       => string(1) "0"
   ["cate_id"]       => string(1) "0"
   ["title"]         => string(4) "test"
   ["content"]       => string(4) "test"
   ["create_time"]   => string(1) "0"
   ["update_time"]   => string(1) "0"
   ["status"]        => string(1) "0"
   ["read_count"]    => string(1) "0"
   ["comment_count"] => string(1) "0"
   ["tags"]          => string(0) ""
}

性能测试

开发过程中,有些时候为了测试性能,经常需要调试某段代码的运行时间或者内存占用开销,系统提供了G方法可以很方便的获取某个区间的运行时间和内存占用情况。 例如:

G('begin');
// ...其他代码段
G('end');
// ...也许这里还有其他代码
// 进行统计区间
echo G('begin','end').'s';

G('begin','end') 表示统计begin位置到end位置的执行时间(单位是秒),begin必须是一个已经标记过的位置,如果这个时候end位置还没被标记过,则会自动把当前位置标记为end标签,输出的结果类似于:0.0056s

默认的统计精度是小数点后4位,如果觉得这个统计精度不够,还可以设置例如:

G('begin','end',6).'s';

可能的输出会变成:0.005587s

如果你的环境支持内存占用统计的话,还可以使用G方法进行区间内存开销统计(单位为kb),例如:

echo G('begin','end','m').'kb';

第三个参数使用m表示进行内存开销统计,输出的结果可能是:625kb

同样,如果end标签没有被标记的话,会自动把当前位置先标记位end标签。

如果环境不支持内存统计,则该参数无效,仍然会进行区间运行时间统计。

错误调试

如果需要我们可以使用E方法输出错误信息并中断执行,例如:

//输出错误信息,并中止执行
E($msg);

调试执行的SQL语句

在模型操作中 ,为了更好的查明错误,经常需要查看下最近使用的SQL语句,我们可以用getLastsql方法来输出上次执行的sql语句。例如:

$User = M("User"); // 实例化User对象
$User->find(1);
echo $User->getLastSql();
// 3.2版本中可以使用简化的方法
echo $User->_sql();

输出结果是 SELECT * FROM think_user WHERE id = 1

并且每个模型都使用独立的最后SQL记录,互不干扰,但是可以用空模型的getLastSql方法获取全局的最后SQL记录。

$User = M("User"); // 实例化User模型
$Info = M("Info"); // 实例化Info模型
$User->find(1);
$Info->find(2);
echo M()->getLastSql();
echo $User->getLastSql();
echo $Info->getLastSql();

输出结果是

SELECT * FROM think_info  WHERE id = 2
SELECT * FROM think_user  WHERE id = 1
SELECT * FROM think_info  WHERE id = 2

getLastSql方法只能获取最后执行的sql记录,如果需要了解更多的SQL日志,可以通过查看当前的页面Trace或者日志文件。

注意:Mongo数据库驱动由于接口的特殊性,不存在执行SQL的概念,因此SQL日志记录功能是额外封装实现的,所以出于性能考虑,只有在开启调试模式的时候才支持使用getLastSql方法获取最后执行的SQL记录。

调试数据库错误信息

在模型操作中,还可以获取数据库的错误信息,例如:

$User = M("User"); // 实例化User对象
$result = $User->find(1);
if(false === $result){
    echo $User->getDbError();
}

CURD操作如果返回值为false,表示数据库操作发生错误,这个时候就需要使用模型的getDbError方法来查看数据库返回的具体错误信息。

参考文档