使用phpdbg调试laravel5.4

正如用gdb调试c/c++应用一样,我们可以用phpdbg来调试php应用。

自动生成初始化脚本

web应用通常运行在fpm-fcgi环境下,而phpdbg作为一个独立的sapi执行,不能直接获取到web应用独有的全局变量(比如$_COOKIE),此时需要fpm-fcgi在请求到达时将这些全局变量传递给phpdbg,这就涉及到进程间通信。官方给出两种方案:一种是手动写一个全局变量初始化脚步,通过.phpdbginit引入,这种方式每次请求都要去手动写一下初始化脚步,比较麻烦;另一种方式是,在phpdbg执行wait命令时,建立一个服务端,同时在fpm-fcgi环境下,通过一个请求初始化阶段的hook,将全局变量通过unix domain socket传递给phpdbg,目前这种方式不成熟,官方文档没有正式给出说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
static int phpdbg_is_auto_global(char *name, int len) {
int ret;
zend_string *str = zend_string_init(name, len, 0);
ret = zend_is_auto_global(str);
zend_string_free(str);
return ret;
}
static PHP_RINIT_FUNCTION(phpdbg_webhelper)
{
// ...
if (strcmp(sapi_module.name, "fpm-fcgi")) {
return SUCCESS;
}
zval globals;
zval *server, *script, *root;
smart_str buf = {0}, path = {0};
php_stream *stream;
size_t numbytes = 0;
array_init(&globals);
phpdbg_is_auto_global(ZEND_STRL("GLOBALS"));
phpdbg_is_auto_global(ZEND_STRL("_ENV"));
phpdbg_is_auto_global(ZEND_STRL("_SERVER"));
phpdbg_is_auto_global(ZEND_STRL("_REQUEST"));
zend_hash_copy(Z_ARRVAL(globals), &EG(symbol_table), NULL);
Z_ARRVAL(globals)->pDestructor = NULL;
zend_hash_str_del(Z_ARRVAL(globals), ZEND_STRL("GLOBALS"));
if ((server = zend_hash_str_find(Z_ARRVAL(globals), ZEND_STRL("_SERVER"))) && Z_TYPE_P(server) == IS_ARRAY) {
if ((script = zend_hash_str_find(Z_ARRVAL_P(server), ZEND_STRL("SCRIPT_FILENAME"))) && Z_TYPE_P(script) == IS_STRING
&& (root = zend_hash_str_find(Z_ARRVAL_P(server), ZEND_STRL("DOCUMENT_ROOT"))) && Z_TYPE_P(root) == IS_STRING) {
smart_str_appends(&path, Z_STRVAL_P(root));
smart_str_appends(&path, "/.phpdbginit");
smart_str_0(&path);
stream = php_stream_open_wrapper(ZSTR_VAL(path.s), "cb", 0 | REPORT_ERRORS, NULL);
php_stream_truncate_set_size(stream, 0);
php_stream_write_string(stream, "exec ");
php_stream_write_string(stream, Z_STRVAL_P(script));
php_stream_write_string(stream, "nn<:n$globals = ");
php_var_export_ex(&globals, 1, &buf);
smart_str_0(&buf);
numbytes = php_stream_write(stream, ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
php_stream_write_string(stream, "; foreach ($globals as $key => $value) { $$key = $value; }n:>n");
php_stream_close(stream);
smart_str_free(&path);
smart_str_free(&buf);
}
}
return SUCCESS;
} /* }}} */