#include #include #include "php.h" #include "zend_extensions.h" #define MINI_XDEBUG_CMD_END -1 #define MINI_XDEBUG_CMD_INVALID 0 #define MINI_XDEBUG_CMD_NEXT 1 static int g_is_first_statement; static int g_do_next_cmd; static int g_next_call_depth; static void mini_xdebug_activate(void) { CG(compiler_options) |= ZEND_COMPILE_EXTENDED_STMT; g_is_first_statement = 1; g_do_next_cmd = 0; g_next_call_depth = 0; } static int get_call_depth(void) { int depth = 0; zend_execute_data* ex = EG(current_execute_data); while (ex) { depth++; ex = ex->prev_execute_data; } return depth; } static void show_context(const char* filename, int lineno) { FILE* fp = fopen(filename, "r"); if (!fp) { return; } char buf[1024]; int n = 0; while (fgets(buf, sizeof(buf), fp)) { n++; if (lineno - 1 <= n && n <= lineno + 1) { fprintf(stdout, "%s %3d | %s", n == lineno ? ">" : " ", n, buf); } if (n > lineno + 1) { break; } } fclose(fp); } static void show_prompt(void) { fprintf(stdout, "(mini-xdebug) "); fflush(stdout); } static int query_command(char* buf) { if (!fgets(buf, sizeof(buf), stdin)) { return MINI_XDEBUG_CMD_END; } buf[strcspn(buf, "\n")] = '\0'; return (!strcmp(buf, "next") || !strcmp(buf, "n")) ? MINI_XDEBUG_CMD_NEXT : MINI_XDEBUG_CMD_INVALID; } static void mini_xdebug_statement_handler(zend_execute_data* frame) { if (!g_is_first_statement && !g_do_next_cmd) { return; } uint32_t lineno = EG(current_execute_data)->opline->lineno; const char* filename = ZSTR_VAL(frame->func->op_array.filename); int depth = get_call_depth(); if (g_is_first_statement) { g_is_first_statement = 0; } else if (g_do_next_cmd) { if (depth > g_next_call_depth) { return; } g_do_next_cmd = 0; } fprintf(stdout, "\n%s:%d\n", filename, lineno); show_context(filename, lineno); char command_buf[256]; for (;;) { show_prompt(); int cmd = query_command(command_buf); if (cmd == MINI_XDEBUG_CMD_NEXT) { g_do_next_cmd = 1; g_next_call_depth = depth; break; } else if (cmd == MINI_XDEBUG_CMD_END) { break; } else { fprintf(stdout, "Available commands: next (n)\n"); } } } #ifndef ZEND_EXT_API #define ZEND_EXT_API ZEND_DLEXPORT #endif ZEND_EXT_API zend_extension_version_info extension_version_info = { ZEND_EXTENSION_API_NO, (char*)ZEND_EXTENSION_BUILD_ID, }; ZEND_DLEXPORT zend_extension zend_extension_entry = { "mini-xdebug", /* name */ "0.1.0", /* version */ "nsfisis", /* author */ "", /* URL */ "", /* copyright */ NULL, /* startup */ NULL, /* shutdown */ mini_xdebug_activate, /* activate */ NULL, /* deactivate */ NULL, /* message_handler */ NULL, /* op_array_handler */ mini_xdebug_statement_handler, /* statement_handler */ NULL, /* fcall_begin_handler */ NULL, /* fcall_end_handler */ NULL, /* op_array_ctor */ NULL, /* op_array_dtor */ STANDARD_ZEND_EXTENSION_PROPERTIES, };