跨平台PHP调试器设计及使用方法——探索和设计
在《跨平台PHP调试器设计及使用方法——立项》一文中,我确定了使用xdebug作为调试器插件部分的基础组件。xdebug提供了一个远程调试的功能(相关资料可以详见https://xdebug.org/docs/remote),我们这个项目便是基于这个功能实现的。(转载请指明出于breaksoftware的csdn博客)
远程调试是基于网络传输方式进行交互的一种调试方式,那么其必定有服务端和客户端两部分组成。这儿的服务端和客户端都是相对的,因为一个客户端可能在和服务器通信后就变成了一个服务端,而服务端则在一次通信后就变成了客户端。xdebug在这个模型中属于服务端,因为它是嵌入到PHP执行器内部,影响PHP执行流程的部分,这些核心功能肯定是作为服务端的一部分而存在。同时它也应该有接收和响应请求的功能。
xdebug提供了两种连接的方式,一种是固定地址单线连接。一种是未知地址多线连接。
我们先看下固定地址单线连接,它的执行流程如下图
- 嵌入在PHP执行程序中的Xdebug开启了一个80端口
- 控制调试过程的IDE发起一次HTTP的调试请求
- Xdebug根据配置项中的remote_host和remote_port字段(也就是IDE所在机器的IP和IDE开放的端口),向IDE发起连接请求
- IDE和Xdebug建立连接,相互通信
- Xdebug应答2过程中的HTTP请求
- 嵌入在PHP执行程序中的Xdebug开启一个80端口
- 控制调试过程的IDE发起一次HTTP的调试请求
- Xdebug的配置项中药配置remote_connect_back为1或者on,还要配置remote_port。Xdebug根据2中的请求解析出远端IDE的IP,然后通过该IP和remote_port发起一次连接请求
- IDE和Xdebug建立连接,相互通信
- Xdebug应答2过程中的HTTP请求
xdebug.remote_enable=On xdebug.remote_handler=dbgp xdebug.remote_host=localhost xdebug.remote_port=9000 |
- IDE获取Xdebug支持的一些属性(不同版本的Xdebug支持不同的功能,所以IDE要先探知它的支持什么不支持什么)
- IDE设置一些Xdebug属性、断点等信息
- Xdebug让代码运行起来,直到遇到断点或者运行结束
- 如果遇到断点,IDE可以向Xdebug询问一些变量值,堆栈信息,或者修改一些变量值等
IDE: feature_get supports_async DBG: yes IDE: stdin redirect DBG: ok IDE: stderr redirect DBG: ok IDE: run DBG: stdin data... DBG: stdin data... DBG: reached breakpoint, done running IDE: give me some variables DBG: ok, here they are IDE: evaluate this expression DBG: stderr data... DBG: ok, done IDE: run IDE: break DBG: ok, breaking DBG: at breakpoint, done running IDE: stop DBG: good bye |
第1部分是告诉IDE,调试用的IDE-KEY是什么,要监听哪个端口的。因为我是以netbeans的调试作为模板,所以我的IDE-KEY也是Netbeans和Xdebug交互的IDE-Key:netbeans-xdebug。当然这个值可以改成别的,但是要和xdebug的配置文件的idekey值一样
xdebug.idekey="netbeans-xdebug"然后我启动了监听本地9000端口。这个9000端口号也不是随便设置的,也要和Xdebug配置文件中的remote_port值一样
xdebug.remote_port=9000此时我们可以在网页中发起一次请求,用于触发php执行。这种触发行为分为两种,我会在之后做介绍。 网页此时一直处在等待状态,这表示Xdebug已经把PHP的执行过程给中断了。于是我们可以进行下步操作。 我们执行sessions指令,用于查看目前有哪些连接已经建立过了。如上图,pydbgp返回了连接信息。当然这个展现不是Xdebug的原始数据——原始数据是XML的。 知道连接号后,我们使用select指令进入特定的连接。之后使用status查看调试的状态。第一次status执行后,表示调试器处在开始状态,这种状态是一种中断状态,它还没进入PHP代码层。我们执行“步过”——step over操作一次,这个时候PHP执行便进入代码了。使用stack_get指令查看当前的调用堆栈信息,这些信息中包括了栈号、文件路径、函数名。如果我们不关心之后的执行,就直接调用run指令,让程序跑到底。这个时候调用status命令,可以看到调试器处在stopping的状态。此时再run一下,本次调试就彻底结束了。结束后,我们使用quit指令退出当前调试。如果还有别的调试请求过来,则可以再调用sessions查看连接号,重复上述的调试过程。上图中4是这个过程的一个体现。 如果不想进行调试了,则可以调用quit退出整个pydbgp。 pydbgp帮助我们连接Xdebug,转发命令,解析返回的XML结果。但是它只提供了标准输入方式的请求接口,我们没法像直接调用API一样调用这些接口。而且我也无意于将这套接口改成API的形式。于是我决定采用父子进程通信的方式,父进程是我们的业务逻辑,子进程是pydbgp,父子进程通过重定向输入输出来进行通信。这是我最初的想法,但是最后重定向的方案也被否掉了,因为python在不同平台上(windows和linux)对这种方式存在兼容问题。这也是迫使我在项目后期老老实实使用标准socket去通信,还好我之前代码耦合做的比较好,替代方案很快就用上了。这儿感慨下,一些事情想时总是美好的样子,而现实往往是曲折的样子。当然随着阅历的增多,想像也会越来越靠谱,这就是经验的作用。
Xdebug给我们提供了很多调试的基础功能。但是作为调试器,我们应该在这些基础之上开发出更多组合性的功能,这样可以帮助使用者更快的发现问题。所以我们还需要对这些功能进行一些高阶封装组合,这些内容我们会在之后介绍。还有就是有些功能可能不是需要调试器提供的,比如日志文件监控,所以这块也将是我们调试器的一些辅助功能。于是我们调试器的结构是这样的
声明:该文观点仅代表作者本人,入门客AI创业平台信息发布平台仅提供信息存储空间服务,如有疑问请联系rumenke@qq.com。