Including phpdbg.
--- /dev/null
+define ____phpdbg_globals
+ if basic_functions_module.zts
+ if !$tsrm_ls
+ set $tsrm_ls = ts_resource_ex(0, 0)
+ end
+ set $phpdbg = ((zend_phpdbg_globals*) (*((void ***) $tsrm_ls))[phpdbg_globals_id-1])
+ else
+ set $phpdbg = phpdbg_globals
+ end
+end
--- /dev/null
+.libs/
+./phpdbg
+*.lo
+*.o
+build
--- /dev/null
+##########################################################
+# .phpdbginit
+#
+# Lines starting with # are ignored
+# Code must start and end with <: and :> respectively
+##########################################################
+# Place initialization commands one per line
+##########################################################
+# exec sapi/phpdbg/test.php
+# set color prompt white-bold
+# set color notice green
+# set color error red
+
+##########################################################
+# Embedding code in .phpdbginit
+##########################################################
+<:
+/*
+* This embedded PHP is executed at init time
+*/
+
+/*
+* Functions defined and registered by init
+* will persist across cleans
+*/
+
+/*
+function my_debugging_function()
+{
+ var_dump(func_get_args());
+}
+*/
+
+/* phpdbg_break(PHPDBG_METHOD, "phpdbg::method"); */
+/* phpdbg_break(PHPDBG_FUNC, "my_global_function"); */
+/* phpdbg_break(PHPDBG_FILE, "/path/to/file.php:10"); */
+
+/*
+ If readline is loaded, you might want to setup completion:
+*/
+if (function_exists('readline_completion_function')) {
+ readline_completion_function(function(){
+ return array_merge(
+ get_defined_functions()['user'],
+ array_keys(get_defined_constants())
+ );
+ });
+}
+
+/*
+ Setting argv made trivial ...
+
+ argv 1 2 3 4
+ ^ set argv for next execution
+
+ argv
+ ^ unset argv for next execution
+
+*/
+function argv()
+{
+ $argv = func_get_args();
+
+ if (!$argv) {
+ $_SERVER['argv'] = array();
+ $_SERVER['argc'] = 0;
+ return;
+ }
+
+ $_SERVER['argv'] = array_merge
+ (
+ array("phpdbg"),
+ $argv
+ );
+ $_SERVER['argc'] = count($_SERVER['argv']);
+
+ return $_SERVER['argv'];
+}
+:>
+##########################################################
+# Now carry on initializing phpdbg ...
+##########################################################
+# R my_debugging_function
+# R argv
+
+##########################################################
+# PHP has many functions that might be useful
+# ... you choose ...
+##########################################################
+# R touch
+# R unlink
+# R scandir
+# R glob
+
+##########################################################
+# Remember: *you have access to the shell*
+##########################################################
+# The output of registered function calls is not,
+# by default, very pretty (unless you implement
+# and register a new implementation for phpdbg)
+# The output of shell commands will usually be more
+# readable on the console
+##########################################################
+# TLDR; if you have a good shell, use it ...
+##########################################################
--- /dev/null
+language: c
+
+script: ./travis/ci.sh
--- /dev/null
+ChangeLog for phpdbg
+====================
+
+Version 0.3.0 2013-00-00
+------------------------
+
+1. Added ability to disable an enable a single breakpoint
+2. Added ability to override SAPI name
+3. Added extended conditional breakpoint support "break at"
+4. Fix loading of zend extnsions with -z
+5. Fix crash when loading .phpdbginit with command line switch
+6. Fix crash on startup errors
+7. Added init.d for remote console (redhat)
+8. Added phpdbg_exec userland function
+9. Added testing facilities
+10. Added break on n-th opline support
+11. Improved trace output
+
+Version 0.2.0 2013-11-31
+------------------------
+
+1. Added "break delete <id>" command
+2. Added "break opcode <opcode>" command
+3. Added "set" command - control prompt and console colors
+4. .phpdbginit now searched in (additional) ini dirs
+5. Added source command - load additional .phpdbginit script during session
+6. Added remote console mode
+7. Added info memory command
+
+Version 0.1.0 2013-11-23
+------------------------
+
+1. New commands:
+ - until (continue until the current line is executed)
+ - frame (switch to a frame in the current stack for inspection)
+ - info (quick access to useful information on the console)
+ - finish (continue until the current function has returned)
+ - leave (continue until the current function is returning)
+ - shell (shell a command)
+ - register (register a function for use as a command)
+2. Added printers for class and method
+3. Make uniform commands and aliases where possible
+4. Include all alias information and sub-command information in help
+5. Added signal handling to break execution (ctrl-c)
+6. Fixed #13 (Output Buffering Control seems fail)
+7. Fixed #14 (Fixed typo in Makefile.frag)
+
+
+Version 0.0.1 2013-11-15
+------------------------
+
+1. Initial features
--- /dev/null
+phpdbg: $(BUILD_BINARY)
+
+phpdbg-shared: $(BUILD_SHARED)
+
+$(BUILD_SHARED): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
+ $(BUILD_PHPDBG_SHARED)
+
+$(BUILD_BINARY): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
+ $(BUILD_PHPDBG)
+
+install-phpdbg: $(BUILD_BINARY)
+ @echo "Installing phpdbg binary: $(INSTALL_ROOT)$(bindir)/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(bindir)
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/log
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/run
+ @$(INSTALL) -m 0755 $(BUILD_BINARY) $(INSTALL_ROOT)$(bindir)/$(program_prefix)phpdbg$(program_suffix)$(EXEEXT)
+
+clean-phpdbg:
+ @echo "Cleaning phpdbg object files ..."
+ find sapi/phpdbg/ -name *.lo -o -name *.o | xargs rm -f
+
+test-phpdbg:
+ @echo "Running phpdbg tests ..."
+ @$(top_builddir)/sapi/cli/php sapi/phpdbg/tests/run-tests.php --phpdbg sapi/phpdbg/phpdbg
+
+.PHONY: clean-phpdbg test-phpdbg
+
+
--- /dev/null
+The interactive PHP debugger
+============================
+
+Implemented as a SAPI module, phpdbg can excert complete control over the environment without impacting the functionality or performance of your code.
+
+phpdbg aims to be a lightweight, powerful, easy to use debugging platform for PHP 5.4+
+
+[](https://travis-ci.org/krakjoe/phpdbg)
+
+Features
+========
+
+ - Stepthrough Debugging
+ - Flexible Breakpoints (Class Method, Function, File:Line, Address, Opcode)
+ - Easy Access to PHP with built-in eval()
+ - Easy Access to Currently Executing Code
+ - Userland API
+ - SAPI Agnostic - Easily Integrated
+ - PHP Configuration File Support
+ - JIT Super Globals - Set Your Own!!
+ - Optional readline Support - Comfortable Terminal Operation
+ - Remote Debugging Support - Bundled Java GUI
+ - Easy Operation - See Help :)
+
+Planned
+=======
+
+ - Improve Everything :)
+
+Installation
+============
+
+To install **phpdbg**, you must compile the source against your PHP installation sources, and enable the SAPI with the configure command.
+
+```
+cd /usr/src/php-src/sapi
+git clone https://github.com/krakjoe/phpdbg
+cd ../
+./buildconf --force
+./configure --enable-phpdbg
+make -j8
+make install-phpdbg
+```
+
+Where the source directory has been used previously to build PHP, there exists a file named *config.nice* which can be used to invoke configure with the same
+parameters as were used by the last execution of *configure*.
+
+**Note:** PHP must be configured with the switch --with-readline for phpdbg to support history, autocompletion, tab-listing etc.
+
+Command Line Options
+====================
+
+The following switches are implemented (just like cli SAPI):
+
+ - -n ignore php ini
+ - -c search for php ini in path
+ - -z load zend extension
+ - -d define php ini entry
+
+The following switches change the default behaviour of phpdbg:
+
+ - -v disables quietness
+ - -s enabled stepping
+ - -e sets execution context
+ - -b boring - disables use of colour on the console
+ - -I ignore .phpdbginit (default init file)
+ - -i override .phpgdbinit location (implies -I)
+ - -O set oplog output file
+ - -q do not print banner on startup
+ - -r jump straight to run
+ - -E enable step through eval()
+ - -l listen ports for remote mode
+ - -a listen address for remote mode
+ - -S override SAPI name
+
+**Note:** Passing -rr will cause phpdbg to quit after execution, rather than returning to the console.
+
+Getting Started
+===============
+
+See the website for tutorials/documentation
+
+http://phpdbg.com
--- /dev/null
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_ENABLE(phpdbg, for phpdbg support,
+[ --enable-phpdbg Build phpdbg], yes, yes)
+
+PHP_ARG_ENABLE(phpdbg-debug, for phpdbg debug build,
+[ --enable-phpdbg-debug Build phpdbg in debug mode], no, no)
+
+if test "$PHP_PHPDBG" != "no"; then
+ AC_DEFINE(HAVE_PHPDBG, 1, [ ])
+
+ if test "$PHP_PHPDBG_DEBUG" != "no"; then
+ AC_DEFINE(PHPDBG_DEBUG, 1, [ ])
+ else
+ AC_DEFINE(PHPDBG_DEBUG, 0, [ ])
+ fi
+
+ PHP_PHPDBG_CFLAGS="-D_GNU_SOURCE"
+ PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_set.c phpdbg_frame.c"
+
+ PHP_SUBST(PHP_PHPDBG_CFLAGS)
+ PHP_SUBST(PHP_PHPDBG_FILES)
+
+ PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/phpdbg/Makefile.frag])
+ PHP_SELECT_SAPI(phpdbg, program, $PHP_PHPDBG_FILES, $PHP_PHPDBG_CFLAGS, [$(SAPI_PHPDBG_PATH)])
+
+ BUILD_BINARY="sapi/phpdbg/phpdbg"
+ BUILD_SHARED="sapi/phpdbg/libphpdbg.la"
+
+ BUILD_PHPDBG="\$(LIBTOOL) --mode=link \
+ \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \
+ \$(PHP_GLOBAL_OBJS) \
+ \$(PHP_BINARY_OBJS) \
+ \$(PHP_PHPDBG_OBJS) \
+ \$(EXTRA_LIBS) \
+ \$(PHPDBG_EXTRA_LIBS) \
+ \$(ZEND_EXTRA_LIBS) \
+ -o \$(BUILD_BINARY)"
+
+ BUILD_PHPDBG_SHARED="\$(LIBTOOL) --mode=link \
+ \$(CC) -shared -Wl,-soname,libphpdbg.so -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \
+ \$(PHP_GLOBAL_OBJS) \
+ \$(PHP_BINARY_OBJS) \
+ \$(PHP_PHPDBG_OBJS) \
+ \$(EXTRA_LIBS) \
+ \$(PHPDBG_EXTRA_LIBS) \
+ \$(ZEND_EXTRA_LIBS) \
+ \-DPHPDBG_SHARED \
+ -o \$(BUILD_SHARED)"
+
+ PHP_SUBST(BUILD_BINARY)
+ PHP_SUBST(BUILD_SHARED)
+ PHP_SUBST(BUILD_PHPDBG)
+ PHP_SUBST(BUILD_PHPDBG_SHARED)
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
--- /dev/null
+ARG_ENABLE('phpdbg', 'Build phpdbg', 'yes');
+ARG_ENABLE('phpdbgs', 'Build phpdbg shared', 'no');
+
+PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c';
+PHPDBG_DLL='php' + PHP_VERSION + 'phpdbg.dll';
+PHPDBG_EXE='phpdbg.exe';
+
+if (PHP_PHPDBG == "yes") {
+ /* build phpdbg binary */
+ SAPI('phpdbg', PHPDBG_SOURCES, PHPDBG_EXE);
+ ADD_FLAG("LIBS_PHPDBG", "ws2_32.lib user32.lib");
+}
+
+if (PHP_PHPDBGS == "yes") {
+ SAPI('phpdbgs', PHPDBG_SOURCES, PHPDBG_DLL, '/D PHP_PHPDBG_EXPORTS /I win32');
+ ADD_FLAG("LIBS_PHPDBGS", "ws2_32.lib user32.lib");
+}
+
+
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_SIGNALS
+# include <signal.h>
+#endif
+#include "phpdbg.h"
+#include "phpdbg_prompt.h"
+#include "phpdbg_bp.h"
+#include "phpdbg_break.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_set.h"
+
+/* {{{ remote console headers */
+#ifndef _WIN32
+# include <sys/socket.h>
+# include <sys/select.h>
+# include <sys/time.h>
+# include <sys/types.h>
+# include <unistd.h>
+# include <arpa/inet.h>
+#endif /* }}} */
+
+ZEND_DECLARE_MODULE_GLOBALS(phpdbg);
+
+static zend_bool phpdbg_booted = 0;
+
+#if PHP_VERSION_ID >= 50500
+void (*zend_execute_old)(zend_execute_data *execute_data TSRMLS_DC);
+#else
+void (*zend_execute_old)(zend_op_array *op_array TSRMLS_DC);
+#endif
+
+static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
+{
+ pg->prompt[0] = NULL;
+ pg->prompt[1] = NULL;
+
+ pg->colors[0] = NULL;
+ pg->colors[1] = NULL;
+ pg->colors[2] = NULL;
+
+ pg->exec = NULL;
+ pg->exec_len = 0;
+ pg->ops = NULL;
+ pg->vmret = 0;
+ pg->bp_count = 0;
+ pg->lcmd = NULL;
+ pg->flags = PHPDBG_DEFAULT_FLAGS;
+ pg->oplog = NULL;
+ pg->io[PHPDBG_STDIN] = NULL;
+ pg->io[PHPDBG_STDOUT] = NULL;
+ pg->io[PHPDBG_STDERR] = NULL;
+ memset(&pg->lparam, 0, sizeof(phpdbg_param_t));
+ pg->frame.num = 0;
+} /* }}} */
+
+static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
+{
+ ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL);
+#if PHP_VERSION_ID >= 50500
+ zend_execute_old = zend_execute_ex;
+ zend_execute_ex = phpdbg_execute_ex;
+#else
+ zend_execute_old = zend_execute;
+ zend_execute = phpdbg_execute_ex;
+#endif
+
+ REGISTER_STRINGL_CONSTANT("PHPDBG_VERSION", PHPDBG_VERSION, sizeof(PHPDBG_VERSION)-1, CONST_CS|CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("PHPDBG_FILE", FILE_PARAM, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_METHOD", METHOD_PARAM, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_LINENO", NUMERIC_PARAM, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_FUNC", STR_PARAM, CONST_CS|CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("PHPDBG_COLOR_PROMPT", PHPDBG_COLOR_PROMPT, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_COLOR_NOTICE", PHPDBG_COLOR_NOTICE, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_COLOR_ERROR", PHPDBG_COLOR_ERROR, CONST_CS|CONST_PERSISTENT);
+
+ return SUCCESS;
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_file(void *brake) /* {{{ */
+{
+ zend_hash_destroy((HashTable*)brake);
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_symbol(void *brake) /* {{{ */
+{
+ efree((char*)((phpdbg_breaksymbol_t*)brake)->symbol);
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_opcode(void *brake) /* {{{ */
+{
+ efree((char*)((phpdbg_breakop_t*)brake)->name);
+} /* }}} */
+
+
+static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */
+{
+ zend_hash_destroy((HashTable*)brake);
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */
+{
+ phpdbg_breakcond_t *brake = (phpdbg_breakcond_t*) data;
+
+ if (brake) {
+ if (brake->ops) {
+ TSRMLS_FETCH();
+
+ destroy_op_array(
+ brake->ops TSRMLS_CC);
+ efree(brake->ops);
+ }
+ efree((char*)brake->code);
+ }
+} /* }}} */
+
+static void php_phpdbg_destroy_registered(void *data) /* {{{ */
+{
+ TSRMLS_FETCH();
+
+ zend_function *function = (zend_function*) data;
+
+ destroy_zend_function(
+ function TSRMLS_CC);
+} /* }}} */
+
+static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
+{
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
+
+ zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
+ zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
+
+ return SUCCESS;
+} /* }}} */
+
+static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
+{
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
+ zend_hash_destroy(&PHPDBG_G(seek));
+ zend_hash_destroy(&PHPDBG_G(registered));
+
+ if (PHPDBG_G(exec)) {
+ efree(PHPDBG_G(exec));
+ PHPDBG_G(exec) = NULL;
+ }
+
+ if (PHPDBG_G(prompt)[0]) {
+ free(PHPDBG_G(prompt)[0]);
+ }
+ if (PHPDBG_G(prompt)[1]) {
+ free(PHPDBG_G(prompt)[1]);
+ }
+
+ PHPDBG_G(prompt)[0] = NULL;
+ PHPDBG_G(prompt)[1] = NULL;
+
+ if (PHPDBG_G(oplog)) {
+ fclose(
+ PHPDBG_G(oplog));
+ PHPDBG_G(oplog) = NULL;
+ }
+
+ if (PHPDBG_G(ops)) {
+ destroy_op_array(PHPDBG_G(ops) TSRMLS_CC);
+ efree(PHPDBG_G(ops));
+ PHPDBG_G(ops) = NULL;
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+/* {{{ proto mixed phpdbg_exec(string context)
+ Attempt to set the execution context for phpdbg
+ If the execution context was set previously it is returned
+ If the execution context was not set previously boolean true is returned
+ If the request to set the context fails, boolean false is returned, and an E_WARNING raised */
+static PHP_FUNCTION(phpdbg_exec)
+{
+ char *exec = NULL;
+ zend_ulong exec_len = 0L;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) {
+ return;
+ }
+
+ {
+ struct stat sb;
+ zend_bool result = 1;
+
+ if (VCWD_STAT(exec, &sb) != FAILURE) {
+ if (sb.st_mode & (S_IFREG|S_IFLNK)) {
+ if (PHPDBG_G(exec)) {
+ ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len), 1);
+ efree(PHPDBG_G(exec));
+ result = 0;
+ }
+
+ PHPDBG_G(exec) = estrndup(exec, exec_len);
+ PHPDBG_G(exec_len) = exec_len;
+
+ if (result)
+ ZVAL_BOOL(return_value, 1);
+ } else {
+ zend_error(
+ E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", exec);
+ ZVAL_BOOL(return_value, 0);
+ }
+ } else {
+ zend_error(
+ E_WARNING, "Failed to set execution context (%s) the file does not exist", exec);
+
+ ZVAL_BOOL(return_value, 0);
+ }
+ }
+} /* }}} */
+
+/* {{{ proto void phpdbg_break([integer type, string expression])
+ instructs phpdbg to insert a breakpoint at the next opcode */
+static PHP_FUNCTION(phpdbg_break)
+{
+ if (ZEND_NUM_ARGS() > 0) {
+ long type;
+ char *expr = NULL;
+ zend_uint expr_len = 0;
+ phpdbg_param_t param;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &type, &expr, &expr_len) == FAILURE) {
+ return;
+ }
+
+ phpdbg_parse_param(expr, expr_len, ¶m TSRMLS_CC);
+
+ switch (type) {
+ case METHOD_PARAM:
+ phpdbg_do_break_method(¶m, NULL TSRMLS_CC);
+ break;
+
+ case FILE_PARAM:
+ phpdbg_do_break_file(¶m, NULL TSRMLS_CC);
+ break;
+
+ case NUMERIC_PARAM:
+ phpdbg_do_break_lineno(¶m, NULL TSRMLS_CC);
+ break;
+
+ case STR_PARAM:
+ phpdbg_do_break_func(¶m, NULL TSRMLS_CC);
+ break;
+
+ default: zend_error(
+ E_WARNING, "unrecognized parameter type %ld", type);
+ }
+
+ phpdbg_clear_param(¶m TSRMLS_CC);
+
+ } else if (EG(current_execute_data) && EG(active_op_array)) {
+ zend_ulong opline_num = (EG(current_execute_data)->opline -
+ EG(active_op_array)->opcodes);
+
+ phpdbg_set_breakpoint_opline_ex(
+ &EG(active_op_array)->opcodes[opline_num+1] TSRMLS_CC);
+ }
+} /* }}} */
+
+/* {{{ proto void phpdbg_clear(void)
+ instructs phpdbg to clear breakpoints */
+static PHP_FUNCTION(phpdbg_clear)
+{
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+} /* }}} */
+
+/* {{{ proto void phpdbg_color(integer element, string color) */
+static PHP_FUNCTION(phpdbg_color)
+{
+ long element;
+ char *color;
+ zend_uint color_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) {
+ return;
+ }
+
+ switch (element) {
+ case PHPDBG_COLOR_NOTICE:
+ case PHPDBG_COLOR_ERROR:
+ case PHPDBG_COLOR_PROMPT:
+ phpdbg_set_color_ex(element, color, color_len TSRMLS_CC);
+ break;
+
+ default: zend_error(E_ERROR, "phpdbg detected an incorrect color constant");
+ }
+} /* }}} */
+
+/* {{{ proto void phpdbg_prompt(string prompt) */
+static PHP_FUNCTION(phpdbg_prompt)
+{
+ char *prompt;
+ zend_uint prompt_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) {
+ return;
+ }
+
+ phpdbg_set_prompt(prompt TSRMLS_CC);
+} /* }}} */
+
+ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_arginfo, 0, 0, 0)
+ ZEND_ARG_INFO(0, type)
+ ZEND_ARG_INFO(0, expression)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(phpdbg_color_arginfo, 0, 0, 0)
+ ZEND_ARG_INFO(0, element)
+ ZEND_ARG_INFO(0, color)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(phpdbg_prompt_arginfo, 0, 0, 0)
+ ZEND_ARG_INFO(0, string)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(phpdbg_exec_arginfo, 0, 0, 0)
+ ZEND_ARG_INFO(0, context)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(phpdbg_clear_arginfo, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+zend_function_entry phpdbg_user_functions[] = {
+ PHP_FE(phpdbg_clear, phpdbg_clear_arginfo)
+ PHP_FE(phpdbg_break, phpdbg_break_arginfo)
+ PHP_FE(phpdbg_exec, phpdbg_exec_arginfo)
+ PHP_FE(phpdbg_color, phpdbg_color_arginfo)
+ PHP_FE(phpdbg_prompt, phpdbg_prompt_arginfo)
+#ifdef PHP_FE_END
+ PHP_FE_END
+#else
+ {NULL,NULL,NULL}
+#endif
+};
+
+static zend_module_entry sapi_phpdbg_module_entry = {
+ STANDARD_MODULE_HEADER,
+ PHPDBG_NAME,
+ phpdbg_user_functions,
+ PHP_MINIT(phpdbg),
+ NULL,
+ PHP_RINIT(phpdbg),
+ PHP_RSHUTDOWN(phpdbg),
+ NULL,
+ PHPDBG_VERSION,
+ STANDARD_MODULE_PROPERTIES
+};
+
+static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */
+{
+ if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) {
+ return FAILURE;
+ }
+
+ phpdbg_booted=1;
+
+ return SUCCESS;
+} /* }}} */
+
+static char* php_sapi_phpdbg_read_cookies(TSRMLS_D) /* {{{ */
+{
+ return NULL;
+} /* }}} */
+
+static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s TSRMLS_DC) /* {{{ */
+{
+ return 0;
+}
+/* }}} */
+
+static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */
+{
+ /* We do nothing here, this function is needed to prevent that the fallback
+ * header handling is called. */
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+/* }}} */
+
+static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC) /* {{{ */
+{
+}
+/* }}} */
+
+static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */
+{
+ /*
+ * We must not request TSRM before being boot
+ */
+ if (phpdbg_booted) {
+ phpdbg_error("%s", message);
+ } else fprintf(stdout, "%s\n", message);
+}
+/* }}} */
+
+static int php_sapi_phpdbg_deactivate(TSRMLS_D) /* {{{ */
+{
+ fflush(stdout);
+ if(SG(request_info).argv0) {
+ free(SG(request_info).argv0);
+ SG(request_info).argv0 = NULL;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+static void php_sapi_phpdbg_register_vars(zval *track_vars_array TSRMLS_DC) /* {{{ */
+{
+ unsigned int len;
+ char *docroot = "";
+
+ /* In phpdbg mode, we consider the environment to be a part of the server variables
+ */
+ php_import_environment_variables(track_vars_array TSRMLS_CC);
+
+ if (PHPDBG_G(exec)) {
+ len = PHPDBG_G(exec_len);
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF",
+ &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
+ php_register_variable("PHP_SELF", PHPDBG_G(exec),
+ track_vars_array TSRMLS_CC);
+ }
+ if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME",
+ &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
+ php_register_variable("SCRIPT_NAME", PHPDBG_G(exec),
+ track_vars_array TSRMLS_CC);
+ }
+
+ if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME",
+ &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
+ php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec),
+ track_vars_array TSRMLS_CC);
+ }
+ if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED",
+ &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
+ php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec),
+ track_vars_array TSRMLS_CC);
+ }
+ }
+
+ /* any old docroot will doo */
+ len = 0U;
+ if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT",
+ &docroot, len, &len TSRMLS_CC)) {
+ php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array TSRMLS_CC);
+ }
+}
+/* }}} */
+
+static inline int php_sapi_phpdbg_ub_write(const char *message, unsigned int length TSRMLS_DC) /* {{{ */
+{
+ return phpdbg_write("%s", message);
+} /* }}} */
+
+#if PHP_VERSION_ID >= 50700
+static inline void php_sapi_phpdbg_flush(void *context TSRMLS_DC) /* {{{ */
+{
+#else
+static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */
+{
+ TSRMLS_FETCH();
+#endif
+
+ fflush(PHPDBG_G(io)[PHPDBG_STDOUT]);
+} /* }}} */
+
+/* {{{ sapi_module_struct phpdbg_sapi_module
+*/
+static sapi_module_struct phpdbg_sapi_module = {
+ "phpdbg", /* name */
+ "phpdbg", /* pretty name */
+
+ php_sapi_phpdbg_module_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ php_sapi_phpdbg_deactivate, /* deactivate */
+
+ php_sapi_phpdbg_ub_write, /* unbuffered write */
+ php_sapi_phpdbg_flush, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ php_sapi_phpdbg_header_handler, /* header handler */
+ php_sapi_phpdbg_send_headers, /* send headers handler */
+ php_sapi_phpdbg_send_header, /* send header handler */
+
+ NULL, /* read POST data */
+ php_sapi_phpdbg_read_cookies, /* read Cookies */
+
+ php_sapi_phpdbg_register_vars, /* register server variables */
+ php_sapi_phpdbg_log_message, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+/* }}} */
+
+const opt_struct OPTIONS[] = { /* {{{ */
+ {'c', 1, "ini path override"},
+ {'d', 1, "define ini entry on command line"},
+ {'n', 0, "no php.ini"},
+ {'z', 1, "load zend_extension"},
+ /* phpdbg options */
+ {'q', 0, "no banner"},
+ {'e', 1, "exec"},
+ {'v', 0, "disable quietness"},
+ {'s', 0, "enable stepping"},
+ {'b', 0, "boring colours"},
+ {'i', 1, "specify init"},
+ {'I', 0, "ignore init"},
+ {'O', 1, "opline log"},
+ {'r', 0, "run"},
+ {'E', 0, "step-through-eval"},
+ {'S', 1, "sapi-name"},
+#ifndef _WIN32
+ {'l', 1, "listen"},
+ {'a', 1, "address-or-any"},
+#endif
+ {'V', 0, "version"},
+ {'-', 0, NULL}
+}; /* }}} */
+
+const char phpdbg_ini_hardcoded[] =
+"html_errors=Off\n"
+"register_argc_argv=On\n"
+"implicit_flush=On\n"
+"display_errors=Off\n"
+"log_errors=On\n"
+"max_execution_time=0\n"
+"max_input_time=-1\n\0";
+
+/* overwriteable ini defaults must be set in phpdbg_ini_defaults() */
+#define INI_DEFAULT(name, value) \
+ Z_SET_REFCOUNT(tmp, 0); \
+ Z_UNSET_ISREF(tmp); \
+ ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \
+ zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL);
+
+void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */
+{
+ zval tmp;
+ INI_DEFAULT("report_zend_debug", "0");
+} /* }}} */
+
+static void phpdbg_welcome(zend_bool cleaning TSRMLS_DC) /* {{{ */
+{
+ /* print blurb */
+ if (!cleaning) {
+ phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s",
+ PHPDBG_VERSION);
+ phpdbg_writeln("To get help using phpdbg type \"help\" and press enter");
+ phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES);
+ } else {
+ phpdbg_notice("Clean Execution Environment");
+
+ phpdbg_writeln("Classes\t\t\t%d", zend_hash_num_elements(EG(class_table)));
+ phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(EG(function_table)));
+ phpdbg_writeln("Constants\t\t%d", zend_hash_num_elements(EG(zend_constants)));
+ phpdbg_writeln("Includes\t\t%d", zend_hash_num_elements(&EG(included_files)));
+ }
+} /* }}} */
+
+static inline void phpdbg_sigint_handler(int signo) /* {{{ */
+{
+ TSRMLS_FETCH();
+
+ if (EG(in_execution)) {
+ /* set signalled only when not interactive */
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
+ }
+ } else {
+ PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
+ zend_bailout();
+ }
+} /* }}} */
+
+#ifndef _WIN32
+int phpdbg_open_socket(const char *interface, short port) /* {{{ */
+{
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+
+ switch (fd) {
+ case -1:
+ return -1;
+
+ default: {
+ int reuse = 1;
+
+ switch (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse))) {
+ case -1:
+ close(fd);
+ return -2;
+
+ default: {
+ struct sockaddr_in address;
+
+ memset(&address, 0, sizeof(address));
+
+ address.sin_port = htons(port);
+ address.sin_family = AF_INET;
+
+ if ((*interface == '*')) {
+ address.sin_addr.s_addr = htonl(INADDR_ANY);
+ } else if (!inet_pton(AF_INET, interface, &address.sin_addr)) {
+ close(fd);
+ return -3;
+ }
+
+ switch (bind(fd, (struct sockaddr *)&address, sizeof(address))) {
+ case -1:
+ close(fd);
+ return -4;
+
+ default: {
+ listen(fd, 5);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return fd;
+} /* }}} */
+
+static inline void phpdbg_close_sockets(int (*socket)[2], FILE *streams[2]) /* {{{ */
+{
+ if ((*socket)[0] >= 0) {
+ shutdown(
+ (*socket)[0], SHUT_RDWR);
+ close((*socket)[0]);
+ }
+
+ if (streams[0]) {
+ fclose(streams[0]);
+ }
+
+ if ((*socket)[1] >= 0) {
+ shutdown(
+ (*socket)[1], SHUT_RDWR);
+ close((*socket)[1]);
+ }
+
+ if (streams[1]) {
+ fclose(streams[1]);
+ }
+} /* }}} */
+
+/* don't inline this, want to debug it easily, will inline when done */
+
+int phpdbg_open_sockets(char *address, int port[2], int (*listen)[2], int (*socket)[2], FILE* streams[2]) /* {{{ */
+{
+ if (((*listen)[0]) < 0 && ((*listen)[1]) < 0) {
+ ((*listen)[0]) = phpdbg_open_socket(address, (short)port[0]);
+ ((*listen)[1]) = phpdbg_open_socket(address, (short)port[1]);
+ }
+
+ streams[0] = NULL;
+ streams[1] = NULL;
+
+ if ((*listen)[0] < 0 || (*listen)[1] < 0) {
+ if ((*listen)[0] < 0) {
+ phpdbg_rlog(stderr,
+ "console failed to initialize (stdin) on %s:%d", address, port[0]);
+ }
+
+ if ((*listen)[1] < 0) {
+ phpdbg_rlog(stderr,
+ "console failed to initialize (stdout) on %s:%d", address, port[1]);
+ }
+
+ if ((*listen)[0] >= 0) {
+ close((*listen)[0]);
+ }
+
+ if ((*listen)[1] >= 0) {
+ close((*listen)[1]);
+ }
+
+ return FAILURE;
+ }
+
+ phpdbg_close_sockets(socket, streams);
+
+ phpdbg_rlog(stderr,
+ "accepting connections on %s:%d/%d", address, port[0], port[1]);
+ {
+ struct sockaddr_in address;
+ socklen_t size = sizeof(address);
+ char buffer[20] = {0};
+
+ {
+ memset(&address, 0, size);
+ (*socket)[0] = accept(
+ (*listen)[0], (struct sockaddr *) &address, &size);
+ inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer));
+
+ phpdbg_rlog(stderr, "connection (stdin) from %s", buffer);
+ }
+
+ {
+ memset(&address, 0, size);
+ (*socket)[1] = accept(
+ (*listen)[1], (struct sockaddr *) &address, &size);
+ inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer));
+
+ phpdbg_rlog(stderr, "connection (stdout) from %s", buffer);
+ }
+ }
+
+ dup2((*socket)[0], fileno(stdin));
+ dup2((*socket)[1], fileno(stdout));
+
+ setbuf(stdout, NULL);
+
+ streams[0] = fdopen((*socket)[0], "r");
+ streams[1] = fdopen((*socket)[1], "w");
+
+ return SUCCESS;
+} /* }}} */
+#endif
+
+int main(int argc, char **argv) /* {{{ */
+{
+ sapi_module_struct *phpdbg = &phpdbg_sapi_module;
+ char *sapi_name;
+ char *ini_entries;
+ int ini_entries_len;
+ char **zend_extensions = NULL;
+ zend_ulong zend_extensions_len = 0L;
+ zend_bool ini_ignore;
+ char *ini_override;
+ char *exec;
+ size_t exec_len;
+ char *init_file;
+ size_t init_file_len;
+ zend_bool init_file_default;
+ char *oplog_file;
+ size_t oplog_file_len;
+ zend_ulong flags;
+ char *php_optarg;
+ int php_optind, opt, show_banner = 1;
+ long cleaning = 0;
+ zend_bool remote = 0;
+ int run = 0;
+ int step = 0;
+ char *bp_tmp_file;
+#ifndef _WIN32
+ char *address;
+ int listen[2];
+ int server[2];
+ int socket[2];
+ FILE* streams[2] = {NULL, NULL};
+#endif
+
+#ifdef ZTS
+ void ***tsrm_ls;
+#endif
+
+#ifndef _WIN32
+ address = strdup("127.0.0.1");
+ socket[0] = -1;
+ socket[1] = -1;
+ listen[0] = -1;
+ listen[1] = -1;
+ server[0] = -1;
+ server[1] = -1;
+ streams[0] = NULL;
+ streams[1] = NULL;
+#endif
+
+#ifdef PHP_WIN32
+ _fmode = _O_BINARY; /* sets default for file streams to binary */
+ setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
+#endif
+
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+
+ tsrm_ls = ts_resource(0);
+#endif
+
+phpdbg_main:
+ if (!cleaning) {
+ bp_tmp_file = malloc(L_tmpnam);
+ tmpnam(bp_tmp_file);
+ if (bp_tmp_file == NULL) {
+ phpdbg_error("Unable to create temporary file");
+ }
+ }
+ ini_entries = NULL;
+ ini_entries_len = 0;
+ ini_ignore = 0;
+ ini_override = NULL;
+ zend_extensions = NULL;
+ zend_extensions_len = 0L;
+ exec = NULL;
+ exec_len = 0;
+ init_file = NULL;
+ init_file_len = 0;
+ init_file_default = 1;
+ oplog_file = NULL;
+ oplog_file_len = 0;
+ flags = PHPDBG_DEFAULT_FLAGS;
+ php_optarg = NULL;
+ php_optind = 1;
+ opt = 0;
+ run = 0;
+ step = 0;
+ sapi_name = NULL;
+
+
+ while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
+ switch (opt) {
+ case 'r':
+ run++;
+ break;
+ case 'n':
+ ini_ignore = 1;
+ break;
+ case 'c':
+ if (ini_override) {
+ free(ini_override);
+ }
+ ini_override = strdup(php_optarg);
+ break;
+ case 'd': {
+ int len = strlen(php_optarg);
+ char *val;
+
+ if ((val = strchr(php_optarg, '='))) {
+ val++;
+ if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
+ ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
+ memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
+ ini_entries_len += (val - php_optarg);
+ memcpy(ini_entries + ini_entries_len, "\"", 1);
+ ini_entries_len++;
+ memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg));
+ ini_entries_len += len - (val - php_optarg);
+ memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
+ ini_entries_len += sizeof("\n\0\"") - 2;
+ } else {
+ ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0"));
+ memcpy(ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
+ ini_entries_len += len + sizeof("\n\0") - 2;
+ }
+ } else {
+ ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
+ memcpy(ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
+ ini_entries_len += len + sizeof("=1\n\0") - 2;
+ }
+ } break;
+
+ case 'z':
+ zend_extensions_len++;
+ if (zend_extensions) {
+ zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len);
+ } else zend_extensions = malloc(sizeof(char*) * zend_extensions_len);
+ zend_extensions[zend_extensions_len-1] = strdup(php_optarg);
+ break;
+
+ /* begin phpdbg options */
+
+ case 'e': { /* set execution context */
+ exec_len = strlen(php_optarg);
+ if (exec_len) {
+ if (exec) {
+ free(exec);
+ }
+ exec = strdup(php_optarg);
+ }
+ } break;
+
+ case 'S': { /* set SAPI name */
+ if (sapi_name) {
+ free(sapi_name);
+ }
+ sapi_name = strdup(php_optarg);
+ } break;
+
+ case 'I': { /* ignore .phpdbginit */
+ init_file_default = 0;
+ } break;
+
+ case 'i': { /* set init file */
+ if (init_file) {
+ free(init_file);
+ }
+
+ init_file_len = strlen(php_optarg);
+ if (init_file_len) {
+ init_file = strdup(php_optarg);
+ }
+ } break;
+
+ case 'O': { /* set oplog output */
+ oplog_file_len = strlen(php_optarg);
+ if (oplog_file_len) {
+ oplog_file = strdup(php_optarg);
+ }
+ } break;
+
+ case 'v': /* set quietness off */
+ flags &= ~PHPDBG_IS_QUIET;
+ break;
+
+ case 's': /* set stepping on */
+ step = 1;
+ break;
+
+ case 'E': /* stepping through eval on */
+ flags |= PHPDBG_IS_STEPONEVAL;
+ break;
+
+ case 'b': /* set colours off */
+ flags &= ~PHPDBG_IS_COLOURED;
+ break;
+
+ case 'q': /* hide banner */
+ show_banner = 0;
+ break;
+
+#ifndef _WIN32
+ /* if you pass a listen port, we will accept input on listen port */
+ /* and write output to listen port * 2 */
+
+ case 'l': { /* set listen ports */
+ if (sscanf(php_optarg, "%d/%d", &listen[0], &listen[1]) != 2) {
+ if (sscanf(php_optarg, "%d", &listen[0]) != 1) {
+ /* default to hardcoded ports */
+ listen[0] = 4000;
+ listen[1] = 8000;
+ } else {
+ listen[1] = (listen[0] * 2);
+ }
+ }
+ } break;
+
+ case 'a': { /* set bind address */
+ free(address);
+ if (!php_optarg) {
+ address = strdup("*");
+ } else address = strdup(php_optarg);
+ } break;
+#endif
+
+ case 'V': {
+ sapi_startup(phpdbg);
+ phpdbg->startup(phpdbg);
+ printf(
+ "phpdbg %s (built: %s %s)\nCopyright (c) 2013 %s\nPHP %s, Copyright (c) 1997-2013 The PHP Group\n%s",
+ PHPDBG_VERSION,
+ __DATE__,
+ __TIME__,
+ PHPDBG_AUTHORS,
+ PHP_VERSION,
+ get_zend_version()
+ );
+ sapi_deactivate(TSRMLS_C);
+ sapi_shutdown();
+ return 0;
+ } break;
+ }
+ }
+
+#ifndef _WIN32
+ /* setup remote server if necessary */
+ if (!cleaning &&
+ (listen[0] > 0 && listen[1] > 0)) {
+ if (phpdbg_open_sockets(address, listen, &server, &socket, streams) == FAILURE) {
+ remote = 0;
+ exit(0);
+ }
+ /* set remote flag to stop service shutting down upon quit */
+ remote = 1;
+ }
+#endif
+
+ if (sapi_name) {
+ phpdbg->name = sapi_name;
+ }
+
+ phpdbg->ini_defaults = phpdbg_ini_defaults;
+ phpdbg->phpinfo_as_text = 1;
+ phpdbg->php_ini_ignore_cwd = 1;
+
+ sapi_startup(phpdbg);
+
+ phpdbg->executable_location = argv[0];
+ phpdbg->phpinfo_as_text = 1;
+ phpdbg->php_ini_ignore = ini_ignore;
+ phpdbg->php_ini_path_override = ini_override;
+
+ if (ini_entries) {
+ ini_entries = realloc(ini_entries, ini_entries_len + sizeof(phpdbg_ini_hardcoded));
+ memmove(ini_entries + sizeof(phpdbg_ini_hardcoded) - 2, ini_entries, ini_entries_len + 1);
+ memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded) - 2);
+ } else {
+ ini_entries = malloc(sizeof(phpdbg_ini_hardcoded));
+ memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded));
+ }
+ ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2;
+
+ if (zend_extensions_len) {
+ zend_ulong zend_extension = 0L;
+
+ while (zend_extension < zend_extensions_len) {
+ const char *ze = zend_extensions[zend_extension];
+ size_t ze_len = strlen(ze);
+
+ ini_entries = realloc(
+ ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n"))));
+ memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1));
+ ini_entries_len += (sizeof("zend_extension=")-1);
+ memcpy(&ini_entries[ini_entries_len], ze, ze_len);
+ ini_entries_len += ze_len;
+ memcpy(&ini_entries[ini_entries_len], "\n", (sizeof("\n") - 1));
+
+ free(zend_extensions[zend_extension]);
+ zend_extension++;
+ }
+
+ free(zend_extensions);
+ }
+
+ phpdbg->ini_entries = ini_entries;
+
+ if (phpdbg->startup(phpdbg) == SUCCESS) {
+
+ zend_activate(TSRMLS_C);
+
+ /* do not install sigint handlers for remote consoles */
+ /* sending SIGINT then provides a decent way of shutting down the server */
+#ifdef ZEND_SIGNALS
+# ifndef _WIN32
+ if (listen[0] < 0) {
+# endif
+ zend_try {
+ zend_signal_activate(TSRMLS_C);
+ zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC);
+ } zend_end_try();
+# ifndef _WIN32
+ }
+# endif
+#else
+# ifndef _WIN32
+ if (listen[0] < 0) {
+# endif
+ signal(SIGINT, phpdbg_sigint_handler);
+#ifndef _WIN32
+ }
+#endif
+#endif
+
+ PG(modules_activated) = 0;
+
+ /* set flags from command line */
+ PHPDBG_G(flags) = flags;
+
+#ifndef _WIN32
+ /* setup io here */
+ if (streams[0] && streams[1]) {
+ PHPDBG_G(flags) |= PHPDBG_IS_REMOTE;
+
+ signal(SIGPIPE, SIG_IGN);
+ }
+#endif
+
+ PHPDBG_G(io)[PHPDBG_STDIN] = stdin;
+ PHPDBG_G(io)[PHPDBG_STDOUT] = stdout;
+ PHPDBG_G(io)[PHPDBG_STDERR] = stderr;
+
+ if (exec) { /* set execution context */
+ PHPDBG_G(exec) = phpdbg_resolve_path(
+ exec TSRMLS_CC);
+ PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec));
+
+ free(exec);
+ }
+
+ if (oplog_file) { /* open oplog */
+ PHPDBG_G(oplog) = fopen(oplog_file, "w+");
+ if (!PHPDBG_G(oplog)) {
+ phpdbg_error(
+ "Failed to open oplog %s", oplog_file);
+ }
+ free(oplog_file);
+ }
+
+ /* set default colors */
+ phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold") TSRMLS_CC);
+ phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold") TSRMLS_CC);
+ phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green") TSRMLS_CC);
+
+ /* set default prompt */
+ phpdbg_set_prompt(PROMPT TSRMLS_CC);
+
+ zend_try {
+ zend_activate_modules(TSRMLS_C);
+ } zend_end_try();
+
+ if (show_banner) {
+ /* print blurb */
+ phpdbg_welcome((cleaning > 0) TSRMLS_CC);
+ }
+
+ zend_try {
+ /* activate globals, they can be overwritten */
+ zend_activate_auto_globals(TSRMLS_C);
+ } zend_end_try();
+
+ /* initialize from file */
+ zend_try {
+ PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
+ phpdbg_init(init_file, init_file_len, init_file_default TSRMLS_CC);
+ phpdbg_try_file_init(bp_tmp_file, strlen(bp_tmp_file), 0 TSRMLS_CC);
+ PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
+ } zend_catch {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
+ if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
+ goto phpdbg_out;
+ }
+ } zend_end_try();
+
+ /* step from here, not through init */
+ if (step) {
+ PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
+ }
+
+ if (run) {
+ /* no need to try{}, run does it ... */
+ PHPDBG_COMMAND_HANDLER(run)(NULL, NULL TSRMLS_CC);
+ if (run > 1) {
+ /* if -r is on the command line more than once just quit */
+ goto phpdbg_out;
+ }
+ }
+
+phpdbg_interact:
+ /* phpdbg main() */
+ do {
+ zend_try {
+ phpdbg_interactive(TSRMLS_C);
+ } zend_catch {
+ if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
+ FILE *bp_tmp_fp = fopen(bp_tmp_file, "w");
+ phpdbg_export_breakpoints(bp_tmp_fp TSRMLS_CC);
+ fclose(bp_tmp_fp);
+ cleaning = 1;
+ goto phpdbg_out;
+ } else {
+ cleaning = 0;
+ }
+#ifndef _WIN32
+ /* remote client disconnected */
+ if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
+
+ /* renegociate connections */
+ phpdbg_open_sockets(
+ address, listen, &server, &socket, streams);
+
+ /* set streams */
+ if (streams[0] && streams[1]) {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING;
+ }
+
+ /* this must be forced */
+ CG(unclean_shutdown) = 0;
+ }
+#endif
+ if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
+ goto phpdbg_out;
+ }
+ } zend_end_try();
+ } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
+
+ /* this must be forced */
+ CG(unclean_shutdown) = 0;
+
+phpdbg_out:
+#ifndef _WIN32
+ if (PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED) {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
+ goto phpdbg_interact;
+ }
+#endif
+
+#ifndef ZTS
+ /* force cleanup of auto and core globals */
+ zend_hash_clean(CG(auto_globals));
+ memset(
+ &core_globals, 0, sizeof(php_core_globals));
+#endif
+
+ if (ini_entries) {
+ free(ini_entries);
+ }
+
+ if (ini_override) {
+ free(ini_override);
+ }
+
+ if (PG(modules_activated)) {
+ zend_try {
+ zend_deactivate_modules(TSRMLS_C);
+ } zend_end_try();
+ }
+
+ zend_deactivate(TSRMLS_C);
+
+ zend_try {
+ zend_post_deactivate_modules(TSRMLS_C);
+ } zend_end_try();
+
+#ifdef ZEND_SIGNALS
+ zend_try {
+ zend_signal_deactivate(TSRMLS_C);
+ } zend_end_try();
+#endif
+
+ zend_try {
+ php_module_shutdown(TSRMLS_C);
+ } zend_end_try();
+
+ sapi_shutdown();
+ }
+
+ if (cleaning || remote) {
+ goto phpdbg_main;
+ }
+
+#ifdef ZTS
+ /* bugggy */
+ /* tsrm_shutdown(); */
+#endif
+
+#ifndef _WIN32
+ if (address) {
+ free(address);
+ }
+#endif
+
+ if (sapi_name) {
+ free(sapi_name);
+ }
+
+ free(bp_tmp_file);
+
+ return 0;
+} /* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_H
+#define PHPDBG_H
+
+#ifdef PHP_WIN32
+# define PHPDBG_API __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+# define PHPDBG_API __attribute__ ((visibility("default")))
+#else
+# define PHPDBG_API
+#endif
+
+#include "php.h"
+#include "php_globals.h"
+#include "php_variables.h"
+#include "php_getopt.h"
+#include "zend_builtin_functions.h"
+#include "zend_extensions.h"
+#include "zend_modules.h"
+#include "zend_globals.h"
+#include "zend_ini_scanner.h"
+#include "zend_stream.h"
+#include "SAPI.h"
+#include <fcntl.h>
+#include <sys/types.h>
+#if defined(_WIN32) && !defined(__MINGW32__)
+# include <windows.h>
+# include "config.w32.h"
+# undef strcasecmp
+# undef strncasecmp
+# define strcasecmp _stricmp
+# define strncasecmp _strnicmp
+#else
+# include "php_config.h"
+#endif
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+#include "php_main.h"
+
+#ifdef ZTS
+# include "TSRM.h"
+#endif
+
+#ifdef HAVE_LIBREADLINE
+# include <readline/readline.h>
+# include <readline/history.h>
+#endif
+
+#include "phpdbg_cmd.h"
+#include "phpdbg_utils.h"
+
+#ifdef ZTS
+# define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v)
+#else
+# define PHPDBG_G(v) (phpdbg_globals.v)
+#endif
+
+#define PHPDBG_NEXT 2
+#define PHPDBG_UNTIL 3
+#define PHPDBG_FINISH 4
+#define PHPDBG_LEAVE 5
+
+/*
+ BEGIN: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE
+*/
+
+/* {{{ tables */
+#define PHPDBG_BREAK_FILE 0
+#define PHPDBG_BREAK_SYM 1
+#define PHPDBG_BREAK_OPLINE 2
+#define PHPDBG_BREAK_METHOD 3
+#define PHPDBG_BREAK_COND 4
+#define PHPDBG_BREAK_OPCODE 5
+#define PHPDBG_BREAK_FUNCTION_OPLINE 6
+#define PHPDBG_BREAK_METHOD_OPLINE 7
+#define PHPDBG_BREAK_FILE_OPLINE 8
+#define PHPDBG_BREAK_MAP 9
+#define PHPDBG_BREAK_TABLES 10 /* }}} */
+
+/* {{{ flags */
+#define PHPDBG_HAS_FILE_BP (1<<1)
+#define PHPDBG_HAS_SYM_BP (1<<2)
+#define PHPDBG_HAS_OPLINE_BP (1<<3)
+#define PHPDBG_HAS_METHOD_BP (1<<4)
+#define PHPDBG_HAS_COND_BP (1<<5)
+#define PHPDBG_HAS_OPCODE_BP (1<<6)
+#define PHPDBG_HAS_FUNCTION_OPLINE_BP (1<<7)
+#define PHPDBG_HAS_METHOD_OPLINE_BP (1<<8)
+#define PHPDBG_HAS_FILE_OPLINE_BP (1<<9) /* }}} */
+
+/*
+ END: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE
+*/
+
+#define PHPDBG_IN_COND_BP (1<<10)
+#define PHPDBG_IN_EVAL (1<<11)
+
+#define PHPDBG_IS_STEPPING (1<<12)
+#define PHPDBG_IS_QUIET (1<<13)
+#define PHPDBG_IS_QUITTING (1<<14)
+#define PHPDBG_IS_COLOURED (1<<15)
+#define PHPDBG_IS_CLEANING (1<<16)
+
+#define PHPDBG_IN_UNTIL (1<<17)
+#define PHPDBG_IN_FINISH (1<<18)
+#define PHPDBG_IN_LEAVE (1<<19)
+
+#define PHPDBG_IS_REGISTERED (1<<20)
+#define PHPDBG_IS_STEPONEVAL (1<<21)
+#define PHPDBG_IS_INITIALIZING (1<<22)
+#define PHPDBG_IS_SIGNALED (1<<23)
+#define PHPDBG_IS_INTERACTIVE (1<<24)
+#define PHPDBG_IS_BP_ENABLED (1<<25)
+#define PHPDBG_IS_REMOTE (1<<26)
+#define PHPDBG_IS_DISCONNECTED (1<<27)
+
+#define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE)
+#define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP)
+#define PHPDBG_BP_MASK (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP|PHPDBG_HAS_COND_BP|PHPDBG_HAS_OPCODE_BP|PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP)
+
+#ifndef _WIN32
+# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED|PHPDBG_IS_BP_ENABLED)
+#else
+# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_BP_ENABLED)
+#endif /* }}} */
+
+/* {{{ strings */
+#define PHPDBG_NAME "phpdbg"
+#define PHPDBG_AUTHORS "Felipe Pena, Joe Watkins and Bob Weinand" /* Ordered by last name */
+#define PHPDBG_URL "http://phpdbg.com"
+#define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues"
+#define PHPDBG_VERSION "0.3.0"
+#define PHPDBG_INIT_FILENAME ".phpdbginit"
+/* }}} */
+
+/* {{{ output descriptors */
+#define PHPDBG_STDIN 0
+#define PHPDBG_STDOUT 1
+#define PHPDBG_STDERR 2
+#define PHPDBG_IO_FDS 3 /* }}} */
+
+/* {{{ structs */
+ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
+ HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */
+ HashTable registered; /* registered */
+ HashTable seek; /* seek oplines */
+ phpdbg_frame_t frame; /* frame */
+
+ char *exec; /* file to execute */
+ size_t exec_len; /* size of exec */
+ zend_op_array *ops; /* op_array */
+ zval *retval; /* return value */
+ int bp_count; /* breakpoint count */
+ int vmret; /* return from last opcode handler execution */
+
+ FILE *oplog; /* opline log */
+ FILE *io[PHPDBG_IO_FDS]; /* io */
+
+ char *prompt[2]; /* prompt */
+ const phpdbg_color_t *colors[PHPDBG_COLORS]; /* colors */
+
+ phpdbg_command_t *lcmd; /* last command */
+ phpdbg_param_t lparam; /* last param */
+
+ zend_ulong flags; /* phpdbg flags */
+ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */
+
+#endif /* PHPDBG_H */
--- /dev/null
+################################################################
+# File: /etc/init.d/phpdbg #
+# Author: krakjoe #
+# Purpose: Daemonize phpdbg automatically on boot #
+# chkconfig: 2345 07 09 #
+# description: Starts, stops and restarts phpdbg daemon #
+################################################################
+LOCKFILE=/var/lock/subsys/phpdbg
+PIDFILE=/var/run/phpdbg.pid
+STDIN=4000
+STDOUT=8000
+################################################################
+# Either set path to phpdbg here or rely on phpdbg in ENV/PATH #
+################################################################
+if [ "x${PHPDBG}" == "x" ]; then
+ PHPDBG=$(which phpdbg 2>/dev/null)
+fi
+################################################################
+# Options to pass to phpdbg upon boot #
+################################################################
+OPTIONS=
+LOGFILE=/var/log/phpdbg.log
+################################################################
+# STOP EDITING STOP EDITING STOP EDITING STOP EDITING #
+################################################################
+. /etc/rc.d/init.d/functions
+RETVAL=1
+################################################################
+insanity()
+{
+ if [ "x${PHPDBG}" == "x" ]; then
+ PHPDBG=$(which phpdbg 2>>/dev/null)
+ if [ $? != 0 ]; then
+ echo -n $"Fatal: cannot find phpdbg ${PHPDBG}"
+ echo_failure
+ echo
+ return 1
+ fi
+ else
+ if [ ! -x ${PHPDBG} ]; then
+ echo -n $"Fatal: cannot execute phpdbg ${PHPDBG}"
+ echo_failure
+ echo
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+start()
+{
+ insanity
+
+ if [ $? -eq 1 ]; then
+ return $RETVAL
+ fi
+
+ echo -n $"Starting: phpdbg ${OPTIONS} on ${STDIN}/${STDOUT} "
+ nohup ${PHPDBG} -l${STDIN}/${STDOUT} ${OPTIONS} 2>>${LOGFILE} 1>/dev/null </dev/null &
+ PID=$!
+ RETVAL=$?
+ if [ $RETVAL -eq 0 ]; then
+ echo $PID > $PIDFILE
+ echo_success
+ else
+ echo_failure
+ fi
+ echo
+ [ $RETVAL = 0 ] && touch ${LOCKFILE}
+ return $RETVAL
+}
+
+stop()
+{
+ insanity
+
+ if [ $? -eq 1 ]; then
+ return $RETVAL
+ fi
+
+ if [ -f ${LOCKFILE} ] && [ -f ${PIDFILE} ]
+ then
+ echo -n $"Stopping: phpdbg ${OPTIONS} on ${STDIN}/${STDOUT} "
+ kill -s TERM $(cat $PIDFILE)
+ RETVAL=$?
+ if [ $RETVAL -eq 0 ]; then
+ echo_success
+ else
+ echo_failure
+ fi
+ echo
+ [ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE}
+ else
+ echo -n $"Error: phpdbg not running"
+ echo_failure
+ echo
+ [ $RETVAL = 1 ]
+ fi
+ return $RETVAL
+}
+##################################################################
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status $PHPDBG
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ echo "usage: $0 start|stop|restart|status"
+ ;;
+esac
+###################################################################
+exit $RETVAL
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "zend_hash.h"
+#include "phpdbg.h"
+#include "phpdbg_bp.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_opcode.h"
+#include "zend_globals.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+/* {{{ private api functions */
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_file(zend_op_array* TSRMLS_DC);
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_symbol(zend_function* TSRMLS_DC);
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_method(zend_op_array* TSRMLS_DC);
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t TSRMLS_DC);
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opcode(zend_uchar TSRMLS_DC);
+static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execute_data *execute_data TSRMLS_DC); /* }}} */
+
+/*
+* Note:
+* A break point must always set the correct id and type
+* A set breakpoint function must always map new points
+*/
+static inline void _phpdbg_break_mapping(int id, HashTable *table TSRMLS_DC)
+{
+ zend_hash_index_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_MAP], (id), (void**) &table, sizeof(void*), NULL);
+}
+
+#define PHPDBG_BREAK_MAPPING(id, table) _phpdbg_break_mapping(id, table TSRMLS_CC)
+#define PHPDBG_BREAK_UNMAPPING(id) \
+ zend_hash_index_del(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], (id))
+
+#define PHPDBG_BREAK_INIT(b, t) do {\
+ b.id = PHPDBG_G(bp_count)++; \
+ b.type = t; \
+ b.disabled = 0;\
+ b.hits = 0; \
+} while(0)
+
+static void phpdbg_file_breaks_dtor(void *data) /* {{{ */
+{
+ phpdbg_breakfile_t *bp = (phpdbg_breakfile_t*) data;
+
+ efree((char*)bp->filename);
+} /* }}} */
+
+static void phpdbg_class_breaks_dtor(void *data) /* {{{ */
+{
+ phpdbg_breakmethod_t *bp = (phpdbg_breakmethod_t*) data;
+
+ efree((char*)bp->class_name);
+ efree((char*)bp->func_name);
+} /* }}} */
+
+static void phpdbg_opline_class_breaks_dtor(void *data) /* {{{ */
+{
+ zend_hash_destroy((HashTable *)data);
+} /* }}} */
+
+static void phpdbg_opline_breaks_dtor(void *data) /* {{{ */
+{
+ phpdbg_breakopline_t *bp = (phpdbg_breakopline_t *) data;
+
+ if (bp->class_name) {
+ efree((char*)bp->class_name);
+ }
+ if (bp->func_name) {
+ efree((char*)bp->func_name);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_reset_breakpoints(TSRMLS_D) /* {{{ */
+{
+ if (zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP])) {
+ HashPosition position[2];
+ HashTable **table = NULL;
+
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], (void**)&table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], &position[0])) {
+ phpdbg_breakbase_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex((*table), &position[1]);
+ zend_hash_get_current_data_ex((*table), (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex((*table), &position[1])) {
+ brake->hits = 0;
+ }
+ }
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
+{
+ HashPosition position[2];
+ HashTable **table = NULL;
+ zend_ulong id = 0L;
+
+ if (zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP])) {
+ phpdbg_notice(
+ "Exporting %d breakpoints",
+ zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]));
+ /* this only looks like magic, it isn't */
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], (void**)&table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], &position[0])) {
+ phpdbg_breakbase_t *brake;
+
+ zend_hash_get_current_key_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], NULL, NULL, &id, 0, &position[0]);
+
+ for (zend_hash_internal_pointer_reset_ex((*table), &position[1]);
+ zend_hash_get_current_data_ex((*table), (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex((*table), &position[1])) {
+ if (brake->id == id) {
+ switch (brake->type) {
+ case PHPDBG_BREAK_FILE: {
+ fprintf(handle,
+ "break file %s:%lu\n",
+ ((phpdbg_breakfile_t*)brake)->filename,
+ ((phpdbg_breakfile_t*)brake)->line);
+ } break;
+
+ case PHPDBG_BREAK_SYM: {
+ fprintf(handle,
+ "break func %s\n",
+ ((phpdbg_breaksymbol_t*)brake)->symbol);
+ } break;
+
+ case PHPDBG_BREAK_METHOD: {
+ fprintf(handle,
+ "break method %s::%s\n",
+ ((phpdbg_breakmethod_t*)brake)->class_name,
+ ((phpdbg_breakmethod_t*)brake)->func_name);
+ } break;
+
+ case PHPDBG_BREAK_METHOD_OPLINE: {
+ fprintf(handle,
+ "break address %s::%s#%ld\n",
+ ((phpdbg_breakopline_t*)brake)->class_name,
+ ((phpdbg_breakopline_t*)brake)->func_name,
+ ((phpdbg_breakopline_t*)brake)->opline_num);
+ } break;
+
+ case PHPDBG_BREAK_FUNCTION_OPLINE: {
+ fprintf(handle,
+ "break address %s#%ld\n",
+ ((phpdbg_breakopline_t*)brake)->func_name,
+ ((phpdbg_breakopline_t*)brake)->opline_num);
+ } break;
+
+ case PHPDBG_BREAK_FILE_OPLINE: {
+ fprintf(handle,
+ "break address %s:%ld\n",
+ ((phpdbg_breakopline_t*)brake)->class_name,
+ ((phpdbg_breakopline_t*)brake)->opline_num);
+ } break;
+
+ case PHPDBG_BREAK_OPCODE: {
+ fprintf(handle,
+ "break op %s\n",
+ ((phpdbg_breakop_t*)brake)->name);
+ } break;
+
+ case PHPDBG_BREAK_COND: {
+ phpdbg_breakcond_t *conditional = (phpdbg_breakcond_t*) brake;
+
+ if (conditional->paramed) {
+ switch (conditional->param.type) {
+ case STR_PARAM:
+ fprintf(handle,
+ "break at %s if %s\n", conditional->param.str, conditional->code);
+ break;
+
+ case METHOD_PARAM:
+ fprintf(handle,
+ "break at %s::%s if %s\n",
+ conditional->param.method.class, conditional->param.method.name,
+ conditional->code);
+ break;
+
+ case FILE_PARAM:
+ fprintf(handle,
+ "break at %s:%lu if %s\n",
+ conditional->param.file.name, conditional->param.file.line,
+ conditional->code);
+ break;
+
+ default: { /* do nothing */ } break;
+ }
+ } else {
+ fprintf(
+ handle, "break on %s\n", conditional->code);
+ }
+ } break;
+ }
+ }
+ }
+ }
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_file(const char *path, long line_num TSRMLS_DC) /* {{{ */
+{
+ struct stat sb;
+
+ if (VCWD_STAT(path, &sb) != FAILURE) {
+ if (sb.st_mode & (S_IFREG|S_IFLNK)) {
+ HashTable *broken;
+ phpdbg_breakfile_t new_break;
+ size_t path_len = strlen(path);
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE],
+ path, path_len, (void**)&broken) == FAILURE) {
+ HashTable breaks;
+
+ zend_hash_init(&breaks, 8, NULL, phpdbg_file_breaks_dtor, 0);
+
+ zend_hash_update(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE],
+ path, path_len, &breaks, sizeof(HashTable),
+ (void**)&broken);
+ }
+
+ if (!zend_hash_index_exists(broken, line_num)) {
+ PHPDBG_G(flags) |= PHPDBG_HAS_FILE_BP;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_FILE);
+ new_break.filename = estrndup(path, path_len);
+ new_break.line = line_num;
+
+ zend_hash_index_update(
+ broken, line_num, (void**)&new_break, sizeof(phpdbg_breakfile_t), NULL);
+
+ phpdbg_notice("Breakpoint #%d added at %s:%ld",
+ new_break.id, new_break.filename, new_break.line);
+
+ PHPDBG_BREAK_MAPPING(new_break.id, broken);
+ } else {
+ phpdbg_error("Breakpoint at %s:%ld exists", path, line_num);
+ }
+
+ } else {
+ phpdbg_error("Cannot set breakpoint in %s, it is not a regular file", path);
+ }
+ } else {
+ phpdbg_error("Cannot stat %s, it does not exist", path);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_symbol(const char *name, size_t name_len TSRMLS_DC) /* {{{ */
+{
+ if (!zend_hash_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], name, name_len)) {
+ phpdbg_breaksymbol_t new_break;
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_SYM_BP;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_SYM);
+ new_break.symbol = estrndup(name, name_len);
+
+ zend_hash_update(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], new_break.symbol,
+ name_len, &new_break, sizeof(phpdbg_breaksymbol_t), NULL);
+
+ phpdbg_notice("Breakpoint #%d added at %s",
+ new_break.id, new_break.symbol);
+
+ PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
+ } else {
+ phpdbg_notice("Breakpoint exists at %s", name);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_method(const char *class_name, const char *func_name TSRMLS_DC) /* {{{ */
+{
+ HashTable class_breaks, *class_table;
+ size_t class_len = strlen(class_name);
+ size_t func_len = strlen(func_name);
+ char *lcname = zend_str_tolower_dup(func_name, func_len);
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], class_name,
+ class_len, (void**)&class_table) != SUCCESS) {
+ zend_hash_init(&class_breaks, 8, NULL, phpdbg_class_breaks_dtor, 0);
+ zend_hash_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_METHOD],
+ class_name, class_len,
+ (void**)&class_breaks, sizeof(HashTable), (void**)&class_table);
+ }
+
+ if (!zend_hash_exists(class_table, lcname, func_len)) {
+ phpdbg_breakmethod_t new_break;
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_METHOD_BP;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_METHOD);
+ new_break.class_name = estrndup(class_name, class_len);
+ new_break.class_len = class_len;
+ new_break.func_name = estrndup(func_name, func_len);
+ new_break.func_len = func_len;
+
+ zend_hash_update(class_table, lcname, func_len,
+ &new_break, sizeof(phpdbg_breakmethod_t), NULL);
+
+ phpdbg_notice("Breakpoint #%d added at %s::%s",
+ new_break.id, class_name, func_name);
+
+ PHPDBG_BREAK_MAPPING(new_break.id, class_table);
+ } else {
+ phpdbg_notice("Breakpoint exists at %s::%s", class_name, func_name);
+ }
+
+ efree(lcname);
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ */
+{
+ if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline)) {
+ phpdbg_breakline_t new_break;
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_OPLINE);
+ new_break.name = NULL;
+ new_break.opline = opline;
+ new_break.base = NULL;
+
+ zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline,
+ &new_break, sizeof(phpdbg_breakline_t), NULL);
+
+ phpdbg_notice("Breakpoint #%d added at %#lx",
+ new_break.id, new_break.opline);
+ PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ } else {
+ phpdbg_notice("Breakpoint exists at %#lx", opline);
+ }
+} /* }}} */
+
+PHPDBG_API int phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakline_t opline_break;
+ if (op_array->last < brake->opline_num) {
+ if (brake->class_name == NULL) {
+ phpdbg_error("There are only %d oplines in function %s (breaking at opline %ld impossible)", op_array->last, brake->func_name, brake->opline_num);
+ } else if (brake->func_name == NULL) {
+ phpdbg_error("There are only %d oplines in file %s (breaking at opline %ld impossible)", op_array->last, brake->class_name, brake->opline_num);
+ } else {
+ phpdbg_error("There are only %d oplines in method %s::%s (breaking at opline %ld impossible)", op_array->last, brake->class_name, brake->func_name, brake->opline_num);
+ }
+
+ return FAILURE;
+ }
+
+ opline_break.disabled = 0;
+ opline_break.hits = 0;
+ opline_break.id = brake->id;
+ opline_break.opline = brake->opline = (zend_ulong)(op_array->opcodes + brake->opline_num);
+ opline_break.name = NULL;
+ opline_break.base = brake;
+ if (op_array->scope) {
+ opline_break.type = PHPDBG_BREAK_METHOD_OPLINE;
+ } else if (op_array->function_name) {
+ opline_break.type = PHPDBG_BREAK_FUNCTION_OPLINE;
+ } else {
+ opline_break.type = PHPDBG_BREAK_FILE_OPLINE;
+ }
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP;
+
+ zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline_break.opline, &opline_break, sizeof(phpdbg_breakline_t), NULL);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_API void phpdbg_resolve_op_array_breaks(zend_op_array *op_array TSRMLS_DC) /* {{{ */
+{
+ HashTable *func_table = &PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE];
+ HashTable *oplines_table;
+ HashPosition position;
+ phpdbg_breakopline_t *brake;
+
+ if (op_array->scope != NULL &&
+ zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], op_array->scope->name, op_array->scope->name_length, (void **)&func_table) == FAILURE) {
+ return;
+ }
+
+ if (op_array->function_name == NULL) {
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], op_array->filename, strlen(op_array->filename), (void **)&oplines_table) == FAILURE) {
+ return;
+ }
+ } else if (zend_hash_find(func_table, op_array->function_name?op_array->function_name:"", op_array->function_name?strlen(op_array->function_name):0, (void **)&oplines_table) == FAILURE) {
+ return;
+ }
+
+ for (zend_hash_internal_pointer_reset_ex(oplines_table, &position);
+ zend_hash_get_current_data_ex(oplines_table, (void**) &brake, &position) == SUCCESS;
+ zend_hash_move_forward_ex(oplines_table, &position)) {
+ if (phpdbg_resolve_op_array_break(brake, op_array TSRMLS_CC) == SUCCESS) {
+ phpdbg_breakline_t *opline_break;
+
+ zend_hash_internal_pointer_end(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ zend_hash_get_current_data(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], (void **)&opline_break);
+
+ phpdbg_notice("Breakpoint #%d resolved at %s%s%s#%ld (opline %#lx)",
+ brake->id,
+ brake->class_name?brake->class_name:"",
+ brake->class_name&&brake->func_name?"::":"",
+ brake->func_name?brake->func_name:"",
+ brake->opline_num,
+ brake->opline);
+ }
+ }
+} /* }}} */
+
+PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break TSRMLS_DC) /* {{{ */
+{
+ HashTable *func_table = EG(function_table);
+ zend_function *func;
+
+ if (new_break->func_name == NULL) {
+ if (EG(current_execute_data) == NULL) {
+ if (PHPDBG_G(ops) != NULL && !memcmp(PHPDBG_G(ops)->filename, new_break->class_name, new_break->class_len)) {
+ if (phpdbg_resolve_op_array_break(new_break, PHPDBG_G(ops) TSRMLS_CC) == SUCCESS) {
+ return SUCCESS;
+ } else {
+ return 2;
+ }
+ }
+ return FAILURE;
+ } else {
+ zend_execute_data *execute_data = EG(current_execute_data);
+ do {
+ if (execute_data->op_array->function_name == NULL && execute_data->op_array->scope == NULL && !memcmp(execute_data->op_array->filename, new_break->class_name, new_break->class_len)) {
+ if (phpdbg_resolve_op_array_break(new_break, execute_data->op_array TSRMLS_CC) == SUCCESS) {
+ return SUCCESS;
+ } else {
+ return 2;
+ }
+ }
+ } while ((execute_data = execute_data->prev_execute_data) != NULL);
+ return FAILURE;
+ }
+ }
+
+ if (new_break->class_name != NULL) {
+ zend_class_entry **ce;
+ if (zend_hash_find(EG(class_table), zend_str_tolower_dup(new_break->class_name, new_break->class_len), new_break->class_len + 1, (void **)&ce) == FAILURE) {
+ return FAILURE;
+ }
+ func_table = &(*ce)->function_table;
+ }
+
+ if (zend_hash_find(func_table, zend_str_tolower_dup(new_break->func_name, new_break->func_len), new_break->func_len + 1, (void **)&func) == FAILURE) {
+ if (new_break->class_name != NULL && new_break->func_name != NULL) {
+ phpdbg_error("Method %s doesn't exist in class %s", new_break->func_name, new_break->class_name);
+ return 2;
+ }
+ return FAILURE;
+ }
+
+ if (func->type != ZEND_USER_FUNCTION) {
+ if (new_break->class_name == NULL) {
+ phpdbg_error("%s is not an user defined function, no oplines exist", new_break->func_name);
+ } else {
+ phpdbg_error("%s::%s is not an user defined method, no oplines exist", new_break->class_name, new_break->func_name);
+ }
+ return 2;
+ }
+
+ if (phpdbg_resolve_op_array_break(new_break, &func->op_array TSRMLS_CC) == FAILURE) {
+ return 2;
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const char *method, zend_ulong opline TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakopline_t new_break;
+ HashTable class_breaks, *class_table;
+ HashTable method_breaks, *method_table;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_METHOD_OPLINE);
+ new_break.func_len = strlen(method);
+ new_break.func_name = estrndup(method, new_break.func_len);
+ new_break.class_len = strlen(class);
+ new_break.class_name = estrndup(class, new_break.class_len);
+ new_break.opline_num = opline;
+ new_break.opline = 0;
+
+ switch (phpdbg_resolve_opline_break(&new_break TSRMLS_CC)) {
+ case FAILURE:
+ phpdbg_notice("Pending breakpoint #%d at %s::%s#%ld", new_break.id, new_break.class_name, new_break.func_name, opline);
+ break;
+
+ case SUCCESS:
+ phpdbg_notice("Breakpoint #%d added at %s::%s#%ld", new_break.id, new_break.class_name, new_break.func_name, opline);
+ break;
+
+ case 2:
+ return;
+ }
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], new_break.class_name, new_break.class_len, (void **)&class_table) == FAILURE) {
+ zend_hash_init(&class_breaks, 8, NULL, phpdbg_opline_class_breaks_dtor, 0);
+ zend_hash_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE],
+ new_break.class_name,
+ new_break.class_len,
+ (void **)&class_breaks, sizeof(HashTable), (void **)&class_table);
+ }
+
+ if (zend_hash_find(class_table, new_break.func_name, new_break.func_len, (void **)&method_table) == FAILURE) {
+ zend_hash_init(&method_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0);
+ zend_hash_update(
+ class_table,
+ new_break.func_name,
+ new_break.func_len,
+ (void **)&method_breaks, sizeof(HashTable), (void **)&method_table);
+ }
+
+ if (zend_hash_index_exists(method_table, opline)) {
+ phpdbg_notice("Breakpoint already exists for %s::%s#%ld", new_break.class_name, new_break.func_name, opline);
+ efree((char*)new_break.func_name);
+ efree((char*)new_break.class_name);
+ PHPDBG_G(bp_count)--;
+ return;
+ }
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_METHOD_OPLINE_BP;
+
+ PHPDBG_BREAK_MAPPING(new_break.id, method_table);
+
+ zend_hash_index_update(method_table, opline, &new_break, sizeof(phpdbg_breakopline_t), NULL);
+}
+
+PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, zend_ulong opline TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakopline_t new_break;
+ HashTable func_breaks, *func_table;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_FUNCTION_OPLINE);
+ new_break.func_len = strlen(function);
+ new_break.func_name = estrndup(function, new_break.func_len);
+ new_break.class_len = 0;
+ new_break.class_name = NULL;
+ new_break.opline_num = opline;
+ new_break.opline = 0;
+
+ switch (phpdbg_resolve_opline_break(&new_break TSRMLS_CC)) {
+ case FAILURE:
+ phpdbg_notice("Pending breakpoint #%d at %s#%ld", new_break.id, new_break.func_name, opline);
+ break;
+
+ case SUCCESS:
+ phpdbg_notice("Breakpoint #%d added at %s#%ld", new_break.id, new_break.func_name, opline);
+ break;
+
+ case 2:
+ return;
+ }
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], new_break.func_name, new_break.func_len, (void **)&func_table) == FAILURE) {
+ zend_hash_init(&func_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0);
+ zend_hash_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE],
+ new_break.func_name,
+ new_break.func_len,
+ (void **)&func_breaks, sizeof(HashTable), (void **)&func_table);
+ }
+
+ if (zend_hash_index_exists(func_table, opline)) {
+ phpdbg_notice("Breakpoint already exists for %s#%ld", new_break.func_name, opline);
+ efree((char*)new_break.func_name);
+ PHPDBG_G(bp_count)--;
+ return;
+ }
+
+ PHPDBG_BREAK_MAPPING(new_break.id, func_table);
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_FUNCTION_OPLINE_BP;
+
+ zend_hash_index_update(func_table, opline, &new_break, sizeof(phpdbg_breakopline_t), NULL);
+}
+
+PHPDBG_API void phpdbg_set_breakpoint_file_opline(const char *file, zend_ulong opline TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakopline_t new_break;
+ HashTable file_breaks, *file_table;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_FILE_OPLINE);
+ new_break.func_len = 0;
+ new_break.func_name = NULL;
+ new_break.class_len = strlen(file);
+ new_break.class_name = estrndup(file, new_break.class_len);
+ new_break.opline_num = opline;
+ new_break.opline = 0;
+
+ switch (phpdbg_resolve_opline_break(&new_break TSRMLS_CC)) {
+ case FAILURE:
+ phpdbg_notice("Pending breakpoint #%d at %s:%ld", new_break.id, new_break.class_name, opline);
+ break;
+
+ case SUCCESS:
+ phpdbg_notice("Breakpoint #%d added at %s:%ld", new_break.id, new_break.class_name, opline);
+ break;
+
+ case 2:
+ return;
+ }
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], new_break.class_name, new_break.class_len, (void **)&file_table) == FAILURE) {
+ zend_hash_init(&file_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0);
+ zend_hash_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE],
+ new_break.class_name,
+ new_break.class_len,
+ (void **)&file_breaks, sizeof(HashTable), (void **)&file_table);
+ }
+
+ if (zend_hash_index_exists(file_table, opline)) {
+ phpdbg_notice("Breakpoint already exists for %s:%ld", new_break.class_name, opline);
+ efree((char*)new_break.class_name);
+ PHPDBG_G(bp_count)--;
+ return;
+ }
+
+ PHPDBG_BREAK_MAPPING(new_break.id, file_table);
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_FILE_OPLINE_BP;
+
+ zend_hash_index_update(file_table, opline, &new_break, sizeof(phpdbg_breakopline_t), NULL);
+}
+
+PHPDBG_API void phpdbg_set_breakpoint_opcode(const char *name, size_t name_len TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakop_t new_break;
+ zend_ulong hash = zend_hash_func(name, name_len);
+
+ if (zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], hash)) {
+ phpdbg_notice(
+ "Breakpoint exists for %s", name);
+ return;
+ }
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_OPCODE);
+ new_break.hash = hash;
+ new_break.name = estrndup(name, name_len);
+
+ zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], hash,
+ &new_break, sizeof(phpdbg_breakop_t), NULL);
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_OPCODE_BP;
+
+ phpdbg_notice("Breakpoint #%d added at %s", new_break.id, name);
+ PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t opline TSRMLS_DC) /* {{{ */
+{
+ if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], (zend_ulong) opline)) {
+ phpdbg_breakline_t new_break;
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_OPLINE);
+ new_break.opline = (zend_ulong) opline;
+
+ zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE],
+ (zend_ulong) opline, &new_break, sizeof(phpdbg_breakline_t), NULL);
+
+ phpdbg_notice("Breakpoint #%d added at %#lx",
+ new_break.id, new_break.opline);
+ PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ }
+} /* }}} */
+
+static inline void phpdbg_create_conditional_break(phpdbg_breakcond_t *brake, const phpdbg_param_t *param, const char *expr, size_t expr_len, zend_ulong hash TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakcond_t new_break;
+ zend_uint cops = CG(compiler_options);
+ zval pv;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_COND);
+ new_break.hash = hash;
+
+ if (param) {
+ new_break.paramed = 1;
+ phpdbg_copy_param(
+ param, &new_break.param TSRMLS_CC);
+ } else {
+ new_break.paramed = 0;
+ }
+
+ cops = CG(compiler_options);
+
+ CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL;
+
+ new_break.code = estrndup(expr, expr_len);
+ new_break.code_len = expr_len;
+
+ Z_STRLEN(pv) = expr_len + sizeof("return ;") - 1;
+ Z_STRVAL(pv) = emalloc(Z_STRLEN(pv) + 1);
+ memcpy(Z_STRVAL(pv), "return ", sizeof("return ") - 1);
+ memcpy(Z_STRVAL(pv) + sizeof("return ") - 1, expr, expr_len);
+ Z_STRVAL(pv)[Z_STRLEN(pv) - 1] = ';';
+ Z_STRVAL(pv)[Z_STRLEN(pv)] = '\0';
+ Z_TYPE(pv) = IS_STRING;
+
+ new_break.ops = zend_compile_string(
+ &pv, "Conditional Breakpoint Code" TSRMLS_CC);
+
+ zval_dtor(&pv);
+
+ if (new_break.ops) {
+ zend_hash_index_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash, &new_break,
+ sizeof(phpdbg_breakcond_t), (void**)&brake);
+
+ phpdbg_notice("Conditional breakpoint #%d added %s/%p",
+ brake->id, brake->code, brake->ops);
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_COND_BP;
+ PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+ } else {
+ phpdbg_error(
+ "Failed to compile code for expression %s", expr);
+ efree((char*)new_break.code);
+ PHPDBG_G(bp_count)--;
+ }
+ CG(compiler_options) = cops;
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_expression(const char *expr, size_t expr_len TSRMLS_DC) /* {{{ */
+{
+ zend_ulong expr_hash = zend_inline_hash_func(expr, expr_len);
+ phpdbg_breakcond_t new_break;
+
+ if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], expr_hash)) {
+ phpdbg_create_conditional_break(
+ &new_break, NULL, expr, expr_len, expr_hash TSRMLS_CC);
+ } else {
+ phpdbg_notice("Conditional break %s exists", expr);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+{
+ if (input->argc > 3 && phpdbg_argv_is(2, "if")) {
+ phpdbg_breakcond_t new_break;
+ phpdbg_param_t new_param;
+
+ zend_ulong expr_hash = 0L;
+ size_t expr_len;
+ const char *join = strstr(input->string, "if");
+ const char *expr = (join) + sizeof("if");
+
+ expr_len = strlen(expr);
+ expr = phpdbg_trim(expr, expr_len, &expr_len);
+ expr_hash = zend_inline_hash_func(expr, expr_len);
+
+ {
+ /* get a clean parameter from input string */
+ size_t sparam_len = 0L;
+ char *sparam = input->string;
+
+ sparam[
+ strstr(input->string, " ") - input->string] = 0;
+ sparam_len = strlen(sparam);
+
+ switch (phpdbg_parse_param(sparam, sparam_len, &new_param TSRMLS_CC)) {
+ case EMPTY_PARAM:
+ case NUMERIC_PARAM:
+ phpdbg_clear_param(
+ &new_param TSRMLS_CC);
+ goto usage;
+
+ default: { /* do nothing */ } break;
+ }
+
+ expr_hash += phpdbg_hash_param(&new_param TSRMLS_CC);
+ }
+
+ if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], expr_hash)) {
+ phpdbg_create_conditional_break(
+ &new_break, &new_param, expr, expr_len, expr_hash TSRMLS_CC);
+ } else {
+ phpdbg_notice(
+ "Conditional break %s exists at the specified location", expr);
+ }
+
+ phpdbg_clear_param(&new_param TSRMLS_CC);
+ } else {
+usage:
+ phpdbg_error("usage: break at <func|method|file:line|address> if <expression>");
+ }
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_file(zend_op_array *op_array TSRMLS_DC) /* {{{ */
+{
+ HashTable *breaks;
+ phpdbg_breakbase_t *brake;
+ size_t name_len = strlen(op_array->filename);
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], op_array->filename,
+ name_len, (void**)&breaks) == FAILURE) {
+ return NULL;
+ }
+
+ if (zend_hash_index_find(breaks, (*EG(opline_ptr))->lineno, (void**)&brake) == SUCCESS) {
+ return brake;
+ }
+
+ return NULL;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_symbol(zend_function *fbc TSRMLS_DC) /* {{{ */
+{
+ const char *fname;
+ zend_op_array *ops;
+ phpdbg_breakbase_t *brake;
+
+ if (fbc->type != ZEND_USER_FUNCTION) {
+ return NULL;
+ }
+
+ ops = (zend_op_array*)fbc;
+
+ if (ops->scope) {
+ /* find method breaks here */
+ return phpdbg_find_breakpoint_method(ops TSRMLS_CC);
+ }
+
+ fname = ops->function_name;
+
+ if (!fname) {
+ fname = "main";
+ }
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], fname, strlen(fname), (void**)&brake) == SUCCESS) {
+ return brake;
+ }
+
+ return NULL;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_method(zend_op_array *ops TSRMLS_DC) /* {{{ */
+{
+ HashTable *class_table;
+ phpdbg_breakbase_t *brake;
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], ops->scope->name,
+ ops->scope->name_length, (void**)&class_table) == SUCCESS) {
+ char *lcname = zend_str_tolower_dup(ops->function_name, strlen(ops->function_name));
+ size_t lcname_len = strlen(lcname);
+
+ if (zend_hash_find(
+ class_table,
+ lcname,
+ lcname_len, (void**)&brake) == SUCCESS) {
+ efree(lcname);
+ return brake;
+ }
+
+ efree(lcname);
+ }
+
+ return NULL;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t opline TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakline_t *brake;
+
+ if (zend_hash_index_find(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE],
+ (zend_ulong) opline, (void**)&brake) == SUCCESS) {
+ return (brake->base?(phpdbg_breakbase_t *)brake->base:(phpdbg_breakbase_t *)brake);
+ }
+
+ return NULL;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opcode(zend_uchar opcode TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakbase_t *brake;
+ const char *opname = phpdbg_decode_opcode(opcode);
+
+ if (memcmp(opname, PHPDBG_STRL("UNKNOWN")) == 0) {
+ return NULL;
+ }
+
+ if (zend_hash_index_find(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE],
+ zend_hash_func(opname, strlen(opname)), (void**)&brake) == SUCCESS) {
+ return brake;
+ }
+ return NULL;
+} /* }}} */
+
+static inline zend_bool phpdbg_find_breakpoint_param(phpdbg_param_t *param, zend_execute_data *execute_data TSRMLS_DC) /* {{{ */
+{
+ zend_function *function = (zend_function*) execute_data->function_state.function;
+
+ switch (param->type) {
+ case NUMERIC_FUNCTION_PARAM:
+ case STR_PARAM: {
+ /* function breakpoint */
+
+ if (function->type != ZEND_USER_FUNCTION) {
+ return 0;
+ }
+
+ {
+ const char *str = NULL;
+ size_t len = 0L;
+ zend_op_array *ops = (zend_op_array*)function;
+ str = ops->function_name ? ops->function_name : "main";
+ len = strlen(str);
+
+ if (len == param->len && memcmp(param->str, str, len) == SUCCESS) {
+ return param->type == STR_PARAM || execute_data->opline - ops->opcodes == param->num;
+ }
+ }
+ } break;
+
+ case FILE_PARAM: {
+ if (param->file.line == zend_get_executed_lineno(TSRMLS_C)) {
+ const char *str = zend_get_executed_filename(TSRMLS_C);
+ size_t lengths[2] = {strlen(param->file.name), strlen(str)};
+
+ if (lengths[0] == lengths[1]) {
+ return (memcmp(
+ param->file.name, str, lengths[0]) == SUCCESS);
+ }
+ }
+ } break;
+
+ case NUMERIC_METHOD_PARAM:
+ case METHOD_PARAM: {
+ if (function->type != ZEND_USER_FUNCTION) {
+ return 0;
+ }
+
+ {
+ zend_op_array *ops = (zend_op_array*) function;
+
+ if (ops->scope) {
+ size_t lengths[2] = {strlen(param->method.class), ops->scope->name_length};
+ if (lengths[0] == lengths[1] && memcmp(param->method.class, ops->scope->name, lengths[0]) == SUCCESS) {
+ lengths[0] = strlen(param->method.name);
+ lengths[1] = strlen(ops->function_name);
+
+ if (lengths[0] == lengths[1] && memcmp(param->method.name, ops->function_name, lengths[0]) == SUCCESS) {
+ return param->type == METHOD_PARAM || (execute_data->opline - ops->opcodes) == param->num;
+ }
+ }
+ }
+ }
+ } break;
+
+ case ADDR_PARAM: {
+ return ((zend_ulong)(phpdbg_opline_ptr_t)execute_data->opline == param->addr);
+ } break;
+
+ default: {
+ /* do nothing */
+ } break;
+ }
+ return 0;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execute_data *execute_data TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakcond_t *bp;
+ HashPosition position;
+ int breakpoint = FAILURE;
+
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], (void*)&bp, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) {
+ zval *retval = NULL;
+ int orig_interactive = CG(interactive);
+ zval **orig_retval = EG(return_value_ptr_ptr);
+ zend_op_array *orig_ops = EG(active_op_array);
+ zend_op **orig_opline = EG(opline_ptr);
+
+ if (((phpdbg_breakbase_t*)bp)->disabled) {
+ continue;
+ }
+
+ if (bp->paramed) {
+ if (!phpdbg_find_breakpoint_param(&bp->param, execute_data TSRMLS_CC)) {
+ continue;
+ }
+ }
+
+ ALLOC_INIT_ZVAL(retval);
+
+ EG(return_value_ptr_ptr) = &retval;
+ EG(active_op_array) = bp->ops;
+ EG(no_extensions) = 1;
+
+ if (!EG(active_symbol_table)) {
+ zend_rebuild_symbol_table(TSRMLS_C);
+ }
+
+ CG(interactive) = 0;
+
+ zend_try {
+ PHPDBG_G(flags) |= PHPDBG_IN_COND_BP;
+ zend_execute(EG(active_op_array) TSRMLS_CC);
+#if PHP_VERSION_ID >= 50700
+ if (zend_is_true(retval TSRMLS_CC)) {
+#else
+ if (zend_is_true(retval)) {
+#endif
+ breakpoint = SUCCESS;
+ }
+ } zend_catch {
+ CG(interactive) = orig_interactive;
+
+ EG(no_extensions)=1;
+ EG(return_value_ptr_ptr) = orig_retval;
+ EG(active_op_array) = orig_ops;
+ EG(opline_ptr) = orig_opline;
+ PHPDBG_G(flags) &= ~PHPDBG_IN_COND_BP;
+ } zend_end_try();
+
+ CG(interactive) = orig_interactive;
+
+ EG(no_extensions)=1;
+ EG(return_value_ptr_ptr) = orig_retval;
+ EG(active_op_array) = orig_ops;
+ EG(opline_ptr) = orig_opline;
+ PHPDBG_G(flags) &= ~PHPDBG_IN_COND_BP;
+
+ if (breakpoint == SUCCESS) {
+ break;
+ }
+ }
+
+ return (breakpoint == SUCCESS) ? ((phpdbg_breakbase_t*)bp) : NULL;
+} /* }}} */
+
+PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakpoint(zend_execute_data* execute_data TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakbase_t *base = NULL;
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED)) {
+ return NULL;
+ }
+
+ /* conditions cannot be executed by eval()'d code */
+ if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL) &&
+ (PHPDBG_G(flags) & PHPDBG_HAS_COND_BP) &&
+ (base = phpdbg_find_conditional_breakpoint(execute_data TSRMLS_CC))) {
+ goto result;
+ }
+
+ if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_BP) &&
+ (base = phpdbg_find_breakpoint_file(execute_data->op_array TSRMLS_CC))) {
+ goto result;
+ }
+
+ if (PHPDBG_G(flags) & (PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_SYM_BP)) {
+ /* check we are at the beginning of the stack */
+ if (execute_data->opline == EG(active_op_array)->opcodes) {
+ if ((base = phpdbg_find_breakpoint_symbol(
+ execute_data->function_state.function TSRMLS_CC))) {
+ goto result;
+ }
+ }
+ }
+
+ if ((PHPDBG_G(flags) & PHPDBG_HAS_OPLINE_BP) &&
+ (base = phpdbg_find_breakpoint_opline(execute_data->opline TSRMLS_CC))) {
+ goto result;
+ }
+
+ if ((PHPDBG_G(flags) & PHPDBG_HAS_OPCODE_BP) &&
+ (base = phpdbg_find_breakpoint_opcode(execute_data->opline->opcode TSRMLS_CC))) {
+ goto result;
+ }
+
+ return NULL;
+
+result:
+ /* we return nothing for disable breakpoints */
+ if (base->disabled) {
+ return NULL;
+ }
+
+ return base;
+} /* }}} */
+
+PHPDBG_API void phpdbg_delete_breakpoint(zend_ulong num TSRMLS_DC) /* {{{ */
+{
+ HashTable **table;
+ HashPosition position;
+ phpdbg_breakbase_t *brake;
+
+ if ((brake = phpdbg_find_breakbase_ex(num, &table, &position TSRMLS_CC))) {
+ char *key;
+ zend_uint klen;
+ zend_ulong idx;
+ int type = brake->type;
+ char *name = NULL;
+ size_t name_len = 0L;
+
+ switch (type) {
+ case PHPDBG_BREAK_FILE:
+ case PHPDBG_BREAK_METHOD:
+ if (zend_hash_num_elements((*table)) == 1) {
+ name = estrdup(brake->name);
+ name_len = strlen(name);
+ if (zend_hash_num_elements(&PHPDBG_G(bp)[type]) == 1) {
+ PHPDBG_G(flags) &= ~(1<<(brake->type+1));
+ }
+ }
+ break;
+
+ default: {
+ if (zend_hash_num_elements((*table)) == 1) {
+ PHPDBG_G(flags) &= ~(1<<(brake->type+1));
+ }
+ }
+ }
+
+ switch (type) {
+ case PHPDBG_BREAK_FILE_OPLINE:
+ case PHPDBG_BREAK_FUNCTION_OPLINE:
+ case PHPDBG_BREAK_METHOD_OPLINE:
+ if (zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]) == 1) {
+ PHPDBG_G(flags) &= PHPDBG_HAS_OPLINE_BP;
+ }
+ zend_hash_index_del(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], ((phpdbg_breakopline_t*)brake)->opline);
+ }
+
+ switch (zend_hash_get_current_key_ex(
+ (*table), &key, &klen, &idx, 0, &position)) {
+
+ case HASH_KEY_IS_STRING:
+ zend_hash_del((*table), key, klen);
+ break;
+
+ default:
+ zend_hash_index_del((*table), idx);
+ }
+
+ switch (type) {
+ case PHPDBG_BREAK_FILE:
+ case PHPDBG_BREAK_METHOD:
+ if (name) {
+ zend_hash_del(&PHPDBG_G(bp)[type], name, name_len);
+ efree(name);
+ }
+ break;
+ }
+
+ phpdbg_notice("Deleted breakpoint #%ld", num);
+ PHPDBG_BREAK_UNMAPPING(num);
+ } else {
+ phpdbg_error("Failed to find breakpoint #%ld", num);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_clear_breakpoints(TSRMLS_D) /* {{{ */
+{
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
+
+ PHPDBG_G(flags) &= ~PHPDBG_BP_MASK;
+
+ PHPDBG_G(bp_count) = 0;
+} /* }}} */
+
+PHPDBG_API void phpdbg_hit_breakpoint(phpdbg_breakbase_t *brake, zend_bool output TSRMLS_DC) /* {{{ */
+{
+ brake->hits++;
+
+ if (output) {
+ phpdbg_print_breakpoint(brake TSRMLS_CC);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_print_breakpoint(phpdbg_breakbase_t *brake TSRMLS_DC) /* {{{ */
+{
+ if (!brake)
+ goto unknown;
+
+ switch (brake->type) {
+ case PHPDBG_BREAK_FILE: {
+ phpdbg_notice("Breakpoint #%d at %s:%ld, hits: %lu",
+ ((phpdbg_breakfile_t*)brake)->id,
+ ((phpdbg_breakfile_t*)brake)->filename,
+ ((phpdbg_breakfile_t*)brake)->line,
+ ((phpdbg_breakfile_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_SYM: {
+ phpdbg_notice("Breakpoint #%d in %s() at %s:%u, hits: %lu",
+ ((phpdbg_breaksymbol_t*)brake)->id,
+ ((phpdbg_breaksymbol_t*)brake)->symbol,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakfile_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_OPLINE: {
+ phpdbg_notice("Breakpoint #%d in %#lx at %s:%u, hits: %lu",
+ ((phpdbg_breakline_t*)brake)->id,
+ ((phpdbg_breakline_t*)brake)->opline,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakline_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_METHOD_OPLINE: {
+ phpdbg_notice("Breakpoint #%d in %s::%s()#%lu at %s:%u, hits: %lu",
+ ((phpdbg_breakopline_t*)brake)->id,
+ ((phpdbg_breakopline_t*)brake)->class_name,
+ ((phpdbg_breakopline_t*)brake)->func_name,
+ ((phpdbg_breakopline_t*)brake)->opline_num,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakopline_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_FUNCTION_OPLINE: {
+ phpdbg_notice("Breakpoint #%d in %s()#%lu at %s:%u, hits: %lu",
+ ((phpdbg_breakopline_t*)brake)->id,
+ ((phpdbg_breakopline_t*)brake)->func_name,
+ ((phpdbg_breakopline_t*)brake)->opline_num,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakopline_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_FILE_OPLINE: {
+ phpdbg_notice("Breakpoint #%d in %s:%lu at %s:%u, hits: %lu",
+ ((phpdbg_breakopline_t*)brake)->id,
+ ((phpdbg_breakopline_t*)brake)->class_name,
+ ((phpdbg_breakopline_t*)brake)->opline_num,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakopline_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_OPCODE: {
+ phpdbg_notice("Breakpoint #%d in %s at %s:%u, hits: %lu",
+ ((phpdbg_breakop_t*)brake)->id,
+ ((phpdbg_breakop_t*)brake)->name,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakop_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_METHOD: {
+ phpdbg_notice("Breakpoint #%d in %s::%s() at %s:%u, hits: %lu",
+ ((phpdbg_breakmethod_t*)brake)->id,
+ ((phpdbg_breakmethod_t*)brake)->class_name,
+ ((phpdbg_breakmethod_t*)brake)->func_name,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakmethod_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_COND: {
+ if (((phpdbg_breakcond_t*)brake)->paramed) {
+ char *param;
+ phpdbg_notice("Conditional breakpoint #%d: at %s if %s %s:%u, hits: %lu",
+ ((phpdbg_breakcond_t*)brake)->id,
+ phpdbg_param_tostring(&((phpdbg_breakcond_t*)brake)->param, ¶m TSRMLS_CC),
+ ((phpdbg_breakcond_t*)brake)->code,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakcond_t*)brake)->hits);
+ if (param)
+ free(param);
+ } else {
+ phpdbg_notice("Conditional breakpoint #%d: on %s == true %s:%u, hits: %lu",
+ ((phpdbg_breakcond_t*)brake)->id,
+ ((phpdbg_breakcond_t*)brake)->code,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakcond_t*)brake)->hits);
+ }
+
+ } break;
+
+ default: {
+unknown:
+ phpdbg_notice("Unknown breakpoint at %s:%u",
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C));
+ }
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_enable_breakpoint(zend_ulong id TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakbase_t *brake = phpdbg_find_breakbase(id TSRMLS_CC);
+
+ if (brake) {
+ brake->disabled = 0;
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_disable_breakpoint(zend_ulong id TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakbase_t *brake = phpdbg_find_breakbase(id TSRMLS_CC);
+
+ if (brake) {
+ brake->disabled = 1;
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_enable_breakpoints(TSRMLS_D) /* {{{ */
+{
+ PHPDBG_G(flags) |= PHPDBG_IS_BP_ENABLED;
+} /* }}} */
+
+PHPDBG_API void phpdbg_disable_breakpoints(TSRMLS_D) { /* {{{ */
+ PHPDBG_G(flags) &= ~PHPDBG_IS_BP_ENABLED;
+} /* }}} */
+
+PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase(zend_ulong id TSRMLS_DC) /* {{{ */
+{
+ HashTable **table;
+ HashPosition position;
+
+ return phpdbg_find_breakbase_ex(id, &table, &position TSRMLS_CC);
+} /* }}} */
+
+PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase_ex(zend_ulong id, HashTable ***table, HashPosition *position TSRMLS_DC) /* {{{ */
+{
+ if (zend_hash_index_find(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], id, (void**)table) == SUCCESS) {
+ phpdbg_breakbase_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex((**table), position);
+ zend_hash_get_current_data_ex((**table), (void**)&brake, position) == SUCCESS;
+ zend_hash_move_forward_ex((**table), position)) {
+
+ if (brake->id == id) {
+ return brake;
+ }
+ }
+ }
+ return NULL;
+} /* }}} */
+
+PHPDBG_API void phpdbg_print_breakpoints(zend_ulong type TSRMLS_DC) /* {{{ */
+{
+ switch (type) {
+ case PHPDBG_BREAK_SYM: if ((PHPDBG_G(flags) & PHPDBG_HAS_SYM_BP)) {
+ HashPosition position;
+ phpdbg_breaksymbol_t *brake;
+
+ phpdbg_writeln(SEPARATE);
+ phpdbg_writeln("Function Breakpoints:");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], &position);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], (void**) &brake, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], &position)) {
+ phpdbg_writeln("#%d\t\t%s%s",
+ brake->id, brake->symbol,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ } break;
+
+ case PHPDBG_BREAK_METHOD: if ((PHPDBG_G(flags) & PHPDBG_HAS_METHOD_BP)) {
+ HashPosition position[2];
+ HashTable *class_table;
+ char *class_name = NULL;
+ zend_uint class_len = 0;
+ zend_ulong class_idx = 0L;
+
+ phpdbg_writeln(SEPARATE);
+ phpdbg_writeln("Method Breakpoints:");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], (void**) &class_table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], &position[0])) {
+
+ if (zend_hash_get_current_key_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD],
+ &class_name, &class_len, &class_idx, 0, &position[0]) == HASH_KEY_IS_STRING) {
+ phpdbg_breakmethod_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex(class_table, &position[1]);
+ zend_hash_get_current_data_ex(class_table, (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex(class_table, &position[1])) {
+ phpdbg_writeln("#%d\t\t%s::%s%s",
+ brake->id, brake->class_name, brake->func_name,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+
+ }
+ } break;
+
+ case PHPDBG_BREAK_FILE: if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_BP)) {
+ HashPosition position[2];
+ HashTable *points;
+
+ phpdbg_writeln(SEPARATE);
+ phpdbg_writeln("File Breakpoints:");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], (void**) &points, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], &position[0])) {
+ phpdbg_breakfile_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex(points, &position[1]);
+ zend_hash_get_current_data_ex(points, (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex(points, &position[1])) {
+ phpdbg_writeln("#%d\t\t%s:%lu%s",
+ brake->id, brake->filename, brake->line,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+
+ } break;
+
+ case PHPDBG_BREAK_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_OPLINE_BP)) {
+ HashPosition position;
+ phpdbg_breakline_t *brake;
+
+ phpdbg_writeln(SEPARATE);
+ phpdbg_writeln("Opline Breakpoints:");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], &position);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], (void**) &brake, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], &position)) {
+ switch (brake->type) {
+ case PHPDBG_BREAK_METHOD_OPLINE:
+ case PHPDBG_BREAK_FUNCTION_OPLINE:
+ case PHPDBG_BREAK_FILE_OPLINE:
+ phpdbg_writeln("#%d\t\t%#lx\t\t(%s breakpoint)%s", brake->id, brake->opline,
+ brake->type == PHPDBG_BREAK_METHOD_OPLINE?"method":
+ brake->type == PHPDBG_BREAK_FUNCTION_OPLINE?"function":
+ brake->type == PHPDBG_BREAK_FILE_OPLINE?"file":
+ "--- error ---",
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ default:
+ phpdbg_writeln("#%d\t\t%#lx", brake->id, brake->opline);
+ break;
+ }
+ }
+ } break;
+
+ case PHPDBG_BREAK_METHOD_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_METHOD_OPLINE_BP)) {
+ HashPosition position[3];
+ HashTable *class_table, *method_table;
+ char *class_name = NULL, *method_name = NULL;
+ zend_uint class_len = 0, method_len = 0;
+ zend_ulong class_idx = 0L, method_idx = 0L;
+
+ phpdbg_writeln(SEPARATE);
+ phpdbg_writeln("Method opline Breakpoints:");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], (void**) &class_table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], &position[0])) {
+
+ if (zend_hash_get_current_key_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE],
+ &class_name, &class_len, &class_idx, 0, &position[0]) == HASH_KEY_IS_STRING) {
+
+ for (zend_hash_internal_pointer_reset_ex(class_table, &position[1]);
+ zend_hash_get_current_data_ex(class_table, (void**) &method_table, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex(class_table, &position[1])) {
+
+ if (zend_hash_get_current_key_ex(class_table,
+ &method_name, &method_len, &method_idx, 0, &position[0]) == HASH_KEY_IS_STRING) {
+
+ phpdbg_breakopline_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex(method_table, &position[2]);
+ zend_hash_get_current_data_ex(method_table, (void**)&brake, &position[2]) == SUCCESS;
+ zend_hash_move_forward_ex(method_table, &position[2])) {
+ phpdbg_writeln("#%d\t\t%s::%s opline %ld%s",
+ brake->id, brake->class_name, brake->func_name, brake->opline_num,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+ }
+ }
+
+ }
+ } break;
+
+ case PHPDBG_BREAK_FUNCTION_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_FUNCTION_OPLINE_BP)) {
+ HashPosition position[2];
+ HashTable *function_table;
+ char *function_name = NULL;
+ zend_uint function_len = 0;
+ zend_ulong function_idx = 0L;
+
+ phpdbg_writeln(SEPARATE);
+ phpdbg_writeln("Function opline Breakpoints:");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], (void**) &function_table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], &position[0])) {
+
+ if (zend_hash_get_current_key_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE],
+ &function_name, &function_len, &function_idx, 0, &position[0]) == HASH_KEY_IS_STRING) {
+
+ phpdbg_breakopline_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex(function_table, &position[1]);
+ zend_hash_get_current_data_ex(function_table, (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex(function_table, &position[1])) {
+ phpdbg_writeln("#%d\t\t%s opline %ld%s",
+ brake->id, brake->func_name, brake->opline_num,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+
+ }
+ } break;
+
+ case PHPDBG_BREAK_FILE_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_OPLINE_BP)) {
+ HashPosition position[2];
+ HashTable *file_table;
+ char *file_name = NULL;
+ zend_uint file_len = 0;
+ zend_ulong file_idx = 0L;
+
+ phpdbg_writeln(SEPARATE);
+ phpdbg_writeln("File opline Breakpoints:");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], (void**) &file_table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], &position[0])) {
+
+ if (zend_hash_get_current_key_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE],
+ &file_name, &file_len, &file_idx, 0, &position[0]) == HASH_KEY_IS_STRING) {
+
+ phpdbg_breakopline_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex(file_table, &position[1]);
+ zend_hash_get_current_data_ex(file_table, (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex(file_table, &position[1])) {
+ phpdbg_writeln("#%d\t\t%s opline %ld%s",
+ brake->id, brake->class_name, brake->opline_num,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+ }
+ } break;
+
+ case PHPDBG_BREAK_COND: if ((PHPDBG_G(flags) & PHPDBG_HAS_COND_BP)) {
+ HashPosition position;
+ phpdbg_breakcond_t *brake;
+
+ phpdbg_writeln(SEPARATE);
+ phpdbg_writeln("Conditional Breakpoints:");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], (void**) &brake, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) {
+ if (brake->paramed) {
+ switch (brake->param.type) {
+ case STR_PARAM:
+ phpdbg_writeln("#%d\t\tat %s if %s%s",
+ brake->id,
+ brake->param.str,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ phpdbg_writeln("#%d\t\tat %s#%ld if %s%s",
+ brake->id,
+ brake->param.str,
+ brake->param.num,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ case METHOD_PARAM:
+ phpdbg_writeln("#%d\t\tat %s::%s if %s%s",
+ brake->id,
+ brake->param.method.class,
+ brake->param.method.name,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ phpdbg_writeln("#%d\t\tat %s::%s#%ld if %s%s",
+ brake->id,
+ brake->param.method.class,
+ brake->param.method.name,
+ brake->param.num,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ case FILE_PARAM:
+ phpdbg_writeln("#%d\t\tat %s:%lu if %s%s",
+ brake->id,
+ brake->param.file.name,
+ brake->param.file.line,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ case ADDR_PARAM:
+ phpdbg_writeln("#%d\t\tat #%lx if %s%s",
+ brake->id,
+ brake->param.addr,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ default:
+ phpdbg_error("Invalid parameter type for conditional breakpoint");
+ return;
+ }
+ } else {
+ phpdbg_writeln("#%d\t\tif %s%s",
+ brake->id, brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+ } break;
+
+ case PHPDBG_BREAK_OPCODE: if (PHPDBG_G(flags) & PHPDBG_HAS_OPCODE_BP) {
+ HashPosition position;
+ phpdbg_breakop_t *brake;
+
+ phpdbg_writeln(SEPARATE);
+ phpdbg_writeln("Opcode Breakpoints:");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], &position);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], (void**) &brake, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], &position)) {
+ phpdbg_writeln("#%d\t\t%s%s",
+ brake->id, brake->name,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ } break;
+ }
+} /* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_BP_H
+#define PHPDBG_BP_H
+
+/* {{{ */
+typedef struct _zend_op *phpdbg_opline_ptr_t; /* }}} */
+
+/* {{{ breakpoint base structure */
+#define phpdbg_breakbase(name) \
+ int id; \
+ zend_uchar type; \
+ zend_ulong hits; \
+ zend_bool disabled; \
+ const char *name /* }}} */
+
+/* {{{ breakpoint base */
+typedef struct _phpdbg_breakbase_t {
+ phpdbg_breakbase(name);
+} phpdbg_breakbase_t; /* }}} */
+
+/**
+ * Breakpoint file-based representation
+ */
+typedef struct _phpdbg_breakfile_t {
+ phpdbg_breakbase(filename);
+ long line;
+} phpdbg_breakfile_t;
+
+/**
+ * Breakpoint symbol-based representation
+ */
+typedef struct _phpdbg_breaksymbol_t {
+ phpdbg_breakbase(symbol);
+} phpdbg_breaksymbol_t;
+
+/**
+ * Breakpoint method based representation
+ */
+typedef struct _phpdbg_breakmethod_t {
+ phpdbg_breakbase(class_name);
+ size_t class_len;
+ const char *func_name;
+ size_t func_len;
+} phpdbg_breakmethod_t;
+
+/**
+ * Breakpoint opline num based representation
+ */
+typedef struct _phpdbg_breakopline_t {
+ phpdbg_breakbase(func_name);
+ size_t func_len;
+ const char *class_name;
+ size_t class_len;
+ zend_ulong opline_num;
+ zend_ulong opline;
+} phpdbg_breakopline_t;
+
+/**
+ * Breakpoint opline based representation
+ */
+typedef struct _phpdbg_breakline_t {
+ phpdbg_breakbase(name);
+ zend_ulong opline;
+ phpdbg_breakopline_t *base;
+} phpdbg_breakline_t;
+
+/**
+ * Breakpoint opcode based representation
+ */
+typedef struct _phpdbg_breakop_t {
+ phpdbg_breakbase(name);
+ zend_ulong hash;
+} phpdbg_breakop_t;
+
+/**
+ * Breakpoint condition based representation
+ */
+typedef struct _phpdbg_breakcond_t {
+ phpdbg_breakbase(code);
+ size_t code_len;
+ zend_bool paramed;
+ phpdbg_param_t param;
+ zend_ulong hash;
+ zend_op_array *ops;
+} phpdbg_breakcond_t;
+
+/* {{{ Opline breaks API */
+PHPDBG_API void phpdbg_resolve_op_array_breaks(zend_op_array *op_array TSRMLS_DC);
+PHPDBG_API int phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array TSRMLS_DC);
+PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break TSRMLS_DC); /* }}} */
+
+/* {{{ Breakpoint Creation API */
+PHPDBG_API void phpdbg_set_breakpoint_file(const char* filename, long lineno TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_symbol(const char* func_name, size_t func_name_len TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_method(const char* class_name, const char* func_name TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_opcode(const char* opname, size_t opname_len TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t opline TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const char *method, zend_ulong opline TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, zend_ulong opline TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_file_opline(const char *file, zend_ulong opline TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_expression(const char* expression, size_t expression_len TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC); /* }}} */
+
+/* {{{ Breakpoint Detection API */
+PHPDBG_API phpdbg_breakbase_t* phpdbg_find_breakpoint(zend_execute_data* TSRMLS_DC); /* }}} */
+
+/* {{{ Misc Breakpoint API */
+PHPDBG_API void phpdbg_hit_breakpoint(phpdbg_breakbase_t* brake, zend_bool output TSRMLS_DC);
+PHPDBG_API void phpdbg_print_breakpoints(zend_ulong type TSRMLS_DC);
+PHPDBG_API void phpdbg_print_breakpoint(phpdbg_breakbase_t* brake TSRMLS_DC);
+PHPDBG_API void phpdbg_reset_breakpoints(TSRMLS_D);
+PHPDBG_API void phpdbg_clear_breakpoints(TSRMLS_D);
+PHPDBG_API void phpdbg_delete_breakpoint(zend_ulong num TSRMLS_DC);
+PHPDBG_API void phpdbg_enable_breakpoints(TSRMLS_D);
+PHPDBG_API void phpdbg_enable_breakpoint(zend_ulong id TSRMLS_DC);
+PHPDBG_API void phpdbg_disable_breakpoint(zend_ulong id TSRMLS_DC);
+PHPDBG_API void phpdbg_disable_breakpoints(TSRMLS_D); /* }}} */
+
+/* {{{ Breakbase API */
+PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase(zend_ulong id TSRMLS_DC);
+PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase_ex(zend_ulong id, HashTable ***table, HashPosition *position TSRMLS_DC); /* }}} */
+
+/* {{{ Breakpoint Exportation API */
+PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC); /* }}} */
+
+#endif /* PHPDBG_BP_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_print.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_opcode.h"
+#include "phpdbg_break.h"
+#include "phpdbg_bp.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+PHPDBG_BREAK(file) /* {{{ */
+{
+ switch (param->type) {
+ case FILE_PARAM:
+ phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_BREAK(method) /* {{{ */
+{
+ switch (param->type) {
+ case METHOD_PARAM:
+ phpdbg_set_breakpoint_method(param->method.class, param->method.name TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_BREAK(address) /* {{{ */
+{
+ switch (param->type) {
+ case ADDR_PARAM:
+ phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC);
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC);
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC);
+ break;
+
+ case FILE_PARAM:
+ phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_BREAK(on) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM:
+ phpdbg_set_breakpoint_expression(param->str, param->len TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_BREAK(at) /* {{{ */
+{
+ phpdbg_set_breakpoint_at(param, input TSRMLS_CC);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_BREAK(lineno) /* {{{ */
+{
+ switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (PHPDBG_G(exec)) {
+ phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC);
+ } else {
+ phpdbg_error("Execution context not set!");
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_BREAK(func) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM:
+ phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_BREAK(op) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM:
+ phpdbg_set_breakpoint_opcode(param->str, param->len TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_BREAK(del) /* {{{ */
+{
+ switch (param->type) {
+ case NUMERIC_PARAM: {
+ phpdbg_delete_breakpoint(param->num TSRMLS_CC);
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_BREAK_H
+#define PHPDBG_BREAK_H
+
+#include "TSRM.h"
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_BREAK(name) PHPDBG_COMMAND(break_##name)
+
+/**
+ * Printer Forward Declarations
+ */
+PHPDBG_BREAK(file);
+PHPDBG_BREAK(func);
+PHPDBG_BREAK(method);
+PHPDBG_BREAK(address);
+PHPDBG_BREAK(at);
+PHPDBG_BREAK(op);
+PHPDBG_BREAK(on);
+PHPDBG_BREAK(lineno);
+PHPDBG_BREAK(del);
+
+/**
+ * Commands
+ */
+static const phpdbg_command_t phpdbg_break_commands[] = {
+ PHPDBG_COMMAND_D_EX(file, "specify breakpoint by file:line", 'F', break_file, NULL, 1),
+ PHPDBG_COMMAND_D_EX(func, "specify breakpoint by global function name", 'f', break_func, NULL, 1),
+ PHPDBG_COMMAND_D_EX(method, "specify breakpoint by class::method", 'm', break_method, NULL, 1),
+ PHPDBG_COMMAND_D_EX(address, "specify breakpoint by address", 'a', break_address, NULL, 1),
+ PHPDBG_COMMAND_D_EX(op, "specify breakpoint by opcode", 'O', break_op, NULL, 1),
+ PHPDBG_COMMAND_D_EX(on, "specify breakpoint by condition", 'o', break_on, NULL, 1),
+ PHPDBG_COMMAND_D_EX(at, "specify breakpoint by location and condition", 'A', break_at, NULL, 1),
+ PHPDBG_COMMAND_D_EX(lineno, "specify breakpoint by line of currently executing file", 'l', break_lineno, NULL, 1),
+ PHPDBG_COMMAND_D_EX(del, "delete breakpoint by identifier number", 'd', break_del, NULL, 1),
+ PHPDBG_END_COMMAND
+};
+
+#endif /* PHPDBG_BREAK_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_set.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
+{
+ switch (param->type) {
+ case EMPTY_PARAM:
+ return "empty";
+ case ADDR_PARAM:
+ return "address";
+ case NUMERIC_PARAM:
+ return "numeric";
+ case METHOD_PARAM:
+ return "method";
+ case NUMERIC_FUNCTION_PARAM:
+ return "function opline";
+ case NUMERIC_METHOD_PARAM:
+ return "method opline";
+ case FILE_PARAM:
+ return "file or file opline";
+ case STR_PARAM:
+ return "string";
+ default: /* this is bad */
+ return "unknown";
+ }
+}
+
+PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char *str, size_t len, phpdbg_param_t *param TSRMLS_DC) /* {{{ */
+{
+ char *class_name, *func_name;
+
+ if (len == 0) {
+ param->type = EMPTY_PARAM;
+ goto parsed;
+ }
+
+ if (phpdbg_is_addr(str)) {
+ param->addr = strtoul(str, 0, 16);
+ param->type = ADDR_PARAM;
+ goto parsed;
+
+ } else if (phpdbg_is_numeric(str)) {
+ param->num = strtol(str, NULL, 0);
+ param->type = NUMERIC_PARAM;
+ goto parsed;
+
+ } else if (phpdbg_is_class_method(str, len+1, &class_name, &func_name)) {
+ param->method.class = class_name;
+ param->method.name = func_name;
+ param->type = METHOD_PARAM;
+ goto parsed;
+ } else {
+ char *line_pos = strrchr(str, ':');
+
+ if (line_pos && phpdbg_is_numeric(line_pos+1)) {
+ if (strchr(str, ':') == line_pos) {
+ char path[MAXPATHLEN];
+
+ memcpy(path, str, line_pos - str);
+ path[line_pos - str] = 0;
+ *line_pos = 0;
+ param->file.name = phpdbg_resolve_path(path TSRMLS_CC);
+ param->file.line = strtol(line_pos+1, NULL, 0);
+ param->type = FILE_PARAM;
+
+ goto parsed;
+ }
+ }
+
+ line_pos = strrchr(str, '#');
+
+ if (line_pos && phpdbg_is_numeric(line_pos+1)) {
+ if (strchr(str, '#') == line_pos) {
+ param->num = strtol(line_pos + 1, NULL, 0);
+
+ if (phpdbg_is_class_method(str, line_pos - str, &class_name, &func_name)) {
+ param->method.class = class_name;
+ param->method.name = func_name;
+ param->type = NUMERIC_METHOD_PARAM;
+ } else {
+ param->len = line_pos - str;
+ param->str = estrndup(str, param->len);
+ param->type = NUMERIC_FUNCTION_PARAM;
+ }
+
+ goto parsed;
+ }
+ }
+ }
+
+ param->str = estrndup(str, len);
+ param->len = len;
+ param->type = STR_PARAM;
+
+parsed:
+ phpdbg_debug("phpdbg_parse_param(\"%s\", %lu): %s",
+ str, len, phpdbg_get_param_type(param TSRMLS_CC));
+ return param->type;
+} /* }}} */
+
+PHPDBG_API void phpdbg_clear_param(phpdbg_param_t *param TSRMLS_DC) /* {{{ */
+{
+ if (param) {
+ switch (param->type) {
+ case FILE_PARAM:
+ efree(param->file.name);
+ break;
+ case METHOD_PARAM:
+ efree(param->method.class);
+ efree(param->method.name);
+ break;
+ case STR_PARAM:
+ efree(param->str);
+ break;
+ default:
+ break;
+ }
+ }
+
+} /* }}} */
+
+PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM:
+ asprintf(pointer,
+ "%s", param->str);
+ break;
+
+ case ADDR_PARAM:
+ asprintf(pointer,
+ "%#lx", param->addr);
+ break;
+
+ case NUMERIC_PARAM:
+ asprintf(pointer,
+ "%li",
+ param->num);
+ break;
+
+ case METHOD_PARAM:
+ asprintf(pointer,
+ "%s::%s",
+ param->method.class,
+ param->method.name);
+ break;
+
+ case FILE_PARAM:
+ if (param->num) {
+ asprintf(pointer,
+ "%s:%lu#%lu",
+ param->file.name,
+ param->file.line,
+ param->num);
+ } else {
+ asprintf(pointer,
+ "%s:%lu",
+ param->file.name,
+ param->file.line);
+ }
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ asprintf(pointer,
+ "%s#%lu", param->str, param->num);
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ asprintf(pointer,
+ "%s::%s#%lu",
+ param->method.class,
+ param->method.name,
+ param->num);
+ break;
+
+ default:
+ asprintf(pointer,
+ "%s", "unknown");
+ }
+
+ return *pointer;
+} /* }}} */
+
+PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest TSRMLS_DC) /* {{{ */
+{
+ switch ((dest->type = src->type)) {
+ case STR_PARAM:
+ dest->str = estrndup(src->str, src->len);
+ dest->len = src->len;
+ break;
+
+ case ADDR_PARAM:
+ dest->addr = src->addr;
+ break;
+
+ case NUMERIC_PARAM:
+ dest->num = src->num;
+ break;
+
+ case METHOD_PARAM:
+ dest->method.class = estrdup(src->method.class);
+ dest->method.name = estrdup(src->method.name);
+ break;
+
+ case FILE_PARAM:
+ dest->file.name = estrdup(src->file.name);
+ dest->file.line = src->file.line;
+ if (src->num)
+ dest->num = src->num;
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ dest->str = estrndup(src->str, src->len);
+ dest->num = src->num;
+ dest->len = src->len;
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ dest->method.class = estrdup(src->method.class);
+ dest->method.name = estrdup(src->method.name);
+ dest->num = src->num;
+ break;
+
+ case EMPTY_PARAM: { /* do nothing */ } break;
+ }
+} /* }}} */
+
+PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
+{
+ zend_ulong hash = param->type;
+
+ switch (param->type) {
+ case STR_PARAM:
+ hash += zend_inline_hash_func(param->str, param->len);
+ break;
+
+ case METHOD_PARAM:
+ hash += zend_inline_hash_func(param->method.class, strlen(param->method.class));
+ hash += zend_inline_hash_func(param->method.name, strlen(param->method.name));
+ break;
+
+ case FILE_PARAM:
+ hash += zend_inline_hash_func(param->file.name, strlen(param->file.name));
+ hash += param->file.line;
+ if (param->num)
+ hash += param->num;
+ break;
+
+ case ADDR_PARAM:
+ hash += param->addr;
+ break;
+
+ case NUMERIC_PARAM:
+ hash += param->num;
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ hash += zend_inline_hash_func(param->str, param->len);
+ hash += param->num;
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ hash += zend_inline_hash_func(param->method.class, strlen(param->method.class));
+ hash += zend_inline_hash_func(param->method.name, strlen(param->method.name));
+ if (param->num)
+ hash+= param->num;
+ break;
+
+ case EMPTY_PARAM: { /* do nothing */ } break;
+ }
+
+ return hash;
+} /* }}} */
+
+PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_param_t *r TSRMLS_DC) /* {{{ */
+{
+ if (l && r) {
+ if (l->type == r->type) {
+ switch (l->type) {
+
+ case NUMERIC_FUNCTION_PARAM:
+ if (l->num != r->num) {
+ break;
+ }
+ /* break intentionally omitted */
+
+ case STR_PARAM:
+ return (l->len == r->len) &&
+ (memcmp(l->str, r->str, l->len) == SUCCESS);
+
+ case NUMERIC_PARAM:
+ return (l->num == r->num);
+
+ case ADDR_PARAM:
+ return (l->addr == r->addr);
+
+ case FILE_PARAM: {
+ if (l->file.line == r->file.line) {
+ size_t lengths[2] = {
+ strlen(l->file.name), strlen(r->file.name)};
+
+ if (lengths[0] == lengths[1]) {
+ if ((!l->num && !r->num) || (l->num == r->num)) {
+ return (memcmp(
+ l->file.name, r->file.name, lengths[0]) == SUCCESS);
+ }
+ }
+ }
+ } break;
+
+ case NUMERIC_METHOD_PARAM:
+ if (l->num != r->num) {
+ break;
+ }
+ /* break intentionally omitted */
+
+ case METHOD_PARAM: {
+ size_t lengths[2] = {
+ strlen(l->method.class), strlen(r->method.class)};
+ if (lengths[0] == lengths[1]) {
+ if (memcmp(l->method.class, r->method.class, lengths[0]) == SUCCESS) {
+ lengths[0] = strlen(l->method.name);
+ lengths[1] = strlen(r->method.name);
+
+ if (lengths[0] == lengths[1]) {
+ return (memcmp(
+ l->method.name, r->method.name, lengths[0]) == SUCCESS);
+ }
+ }
+ }
+ } break;
+
+ case EMPTY_PARAM:
+ return 1;
+ }
+ }
+ }
+ return 0;
+} /* }}} */
+
+PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */
+{
+ char *p;
+ char b[PHPDBG_MAX_CMD];
+ int l=0;
+ enum states {
+ IN_BETWEEN,
+ IN_WORD,
+ IN_STRING
+ } state = IN_BETWEEN;
+ phpdbg_input_t **argv = NULL;
+
+ argv = (phpdbg_input_t**) emalloc(sizeof(phpdbg_input_t*));
+ (*argc) = 0;
+
+#define RESET_STATE() do { \
+ phpdbg_input_t *arg = emalloc(sizeof(phpdbg_input_t)); \
+ if (arg) { \
+ b[l]=0; \
+ arg->length = l; \
+ arg->string = estrndup(b, arg->length); \
+ arg->argv = NULL; \
+ arg->argc = 0; \
+ argv = (phpdbg_input_t**) erealloc(argv, sizeof(phpdbg_input_t*) * ((*argc)+1)); \
+ argv[(*argc)++] = arg; \
+ l = 0; \
+ } \
+ state = IN_BETWEEN; \
+} while (0)
+
+ for (p = buffer; *p != '\0'; p++) {
+ int c = (unsigned char) *p;
+ switch (state) {
+ case IN_BETWEEN:
+ if (isspace(c)) {
+ continue;
+ }
+ if (c == '"') {
+ state = IN_STRING;
+ continue;
+ }
+ state = IN_WORD;
+ b[l++]=c;
+ continue;
+
+ case IN_STRING:
+ if (c == '"') {
+ if (buffer[(p - buffer)-1] == '\\') {
+ b[l-1]=c;
+ continue;
+ }
+ RESET_STATE();
+ } else {
+ b[l++]=c;
+ }
+ continue;
+
+ case IN_WORD:
+ if (isspace(c)) {
+ RESET_STATE();
+ } else {
+ b[l++]=c;
+ }
+ continue;
+ }
+ }
+
+ switch (state) {
+ case IN_WORD: {
+ RESET_STATE();
+ } break;
+
+ case IN_STRING:
+ phpdbg_error(
+ "Malformed command line (unclosed quote) @ %ld: %s!",
+ (p - buffer)-1, &buffer[(p - buffer)-1]);
+ break;
+
+ case IN_BETWEEN:
+ break;
+ }
+
+ if ((*argc) == 0) {
+ /* not needed */
+ efree(argv);
+
+ /* to be sure */
+ return NULL;
+ }
+
+ return argv;
+} /* }}} */
+
+PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
+{
+ phpdbg_input_t *buffer = NULL;
+ char *cmd = NULL;
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) &&
+ (buffered == NULL)) {
+ fflush(PHPDBG_G(io)[PHPDBG_STDOUT]);
+ }
+
+ if (buffered == NULL) {
+#ifndef HAVE_LIBREADLINE
+ char buf[PHPDBG_MAX_CMD];
+ if ((!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && !phpdbg_write(phpdbg_get_prompt(TSRMLS_C))) ||
+ !fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) {
+ /* the user has gone away */
+ phpdbg_error("Failed to read console!");
+ PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED);
+ zend_bailout();
+ return NULL;
+ }
+
+ cmd = buf;
+#else
+ if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
+ char buf[PHPDBG_MAX_CMD];
+ if (fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) {
+ cmd = buf;
+ } else cmd = NULL;
+ } else cmd = readline(phpdbg_get_prompt(TSRMLS_C));
+
+ if (!cmd) {
+ /* the user has gone away */
+ phpdbg_error("Failed to read console!");
+ PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED);
+ zend_bailout();
+ return NULL;
+ }
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
+ add_history(cmd);
+ }
+#endif
+ } else cmd = buffered;
+
+ /* allocate and sanitize buffer */
+ buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t));
+ if (!buffer) {
+ return NULL;
+ }
+
+ buffer->string = phpdbg_trim(cmd, strlen(cmd), &buffer->length);
+
+ /* store constant pointer to start of buffer */
+ buffer->start = (char* const*) buffer->string;
+
+ buffer->argv = phpdbg_read_argv(
+ buffer->string, &buffer->argc TSRMLS_CC);
+
+#ifdef PHPDBG_DEBUG
+ if (buffer->argc) {
+ int arg = 0;
+
+ while (arg < buffer->argc) {
+ phpdbg_debug(
+ "argv %d=%s", arg, buffer->argv[arg]->string);
+ arg++;
+ }
+ }
+#endif
+
+#ifdef HAVE_LIBREADLINE
+ if (!buffered && cmd &&
+ !(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
+ free(cmd);
+ }
+#endif
+
+ return buffer;
+ }
+
+ return NULL;
+} /* }}} */
+
+PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC) /* {{{ */
+{
+ if (argv) {
+ if (argc) {
+ int arg;
+ for (arg=0; arg<argc; arg++) {
+ phpdbg_destroy_input(
+ &argv[arg] TSRMLS_CC);
+ }
+ }
+ efree(argv);
+ }
+
+} /* }}} */
+
+PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */
+{
+ if (*input) {
+ if ((*input)->string) {
+ efree((*input)->string);
+ }
+
+ phpdbg_destroy_argv(
+ (*input)->argv, (*input)->argc TSRMLS_CC);
+
+ efree(*input);
+ }
+} /* }}} */
+
+PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+{
+ int rc = FAILURE;
+
+ if (input->argc > 0) {
+ while (command && command->name && command->handler) {
+ if (((command->name_len == input->argv[0]->length) &&
+ (memcmp(command->name, input->argv[0]->string, command->name_len) == SUCCESS)) ||
+ (command->alias &&
+ (input->argv[0]->length == 1) &&
+ (command->alias == *input->argv[0]->string))) {
+
+ phpdbg_param_t param;
+
+ param.type = EMPTY_PARAM;
+
+ if (input->argc > 1) {
+ if (command->subs) {
+ phpdbg_input_t sub = *input;
+
+ sub.string += input->argv[0]->length;
+ sub.length -= input->argv[0]->length;
+
+ sub.string = phpdbg_trim(
+ sub.string, sub.length, &sub.length);
+
+ sub.argc--;
+ sub.argv++;
+
+ phpdbg_debug(
+ "trying sub commands in \"%s\" for \"%s\" with %d arguments",
+ command->name, sub.argv[0]->string, sub.argc-1);
+
+ if (phpdbg_do_cmd(command->subs, &sub TSRMLS_CC) == SUCCESS) {
+ efree(sub.string);
+ return SUCCESS;
+ }
+
+ efree(sub.string);
+ }
+
+ /* no sub command found */
+ {
+ char *store = input->string;
+
+ input->string += input->argv[0]->length;
+ input->length -= input->argv[0]->length;
+
+ input->string = phpdbg_trim(
+ input->string, input->length, &input->length);
+
+ efree(store);
+ }
+
+ /* pass parameter on */
+ phpdbg_parse_param(
+ input->string,
+ input->length,
+ ¶m TSRMLS_CC);
+ }
+
+ phpdbg_debug(
+ "found command %s for %s with %d arguments",
+ command->name, input->argv[0]->string, input->argc-1);
+ {
+ int arg;
+ for (arg=1; arg<input->argc; arg++) {
+ phpdbg_debug(
+ "\t#%d: [%s=%zu]",
+ arg,
+ input->argv[arg]->string,
+ input->argv[arg]->length);
+ }
+ }
+
+ rc = command->handler(¶m, input TSRMLS_CC);
+
+ /* only set last command when it is worth it! */
+ if ((rc != FAILURE) &&
+ !(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) {
+ PHPDBG_G(lcmd) = (phpdbg_command_t*) command;
+ phpdbg_clear_param(
+ &PHPDBG_G(lparam) TSRMLS_CC);
+ PHPDBG_G(lparam) = param;
+ }
+ break;
+ }
+ command++;
+ }
+ } else {
+ /* this should NEVER happen */
+ phpdbg_error(
+ "No function executed!!");
+ }
+
+ return rc;
+} /* }}} */
+
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_CMD_H
+#define PHPDBG_CMD_H
+
+#include "TSRM.h"
+
+typedef struct _phpdbg_command_t phpdbg_command_t;
+
+/* {{{ Command and Parameter */
+enum {
+ NO_ARG = 0,
+ REQUIRED_ARG,
+ OPTIONAL_ARG
+};
+
+typedef enum {
+ EMPTY_PARAM = 0,
+ ADDR_PARAM,
+ FILE_PARAM,
+ METHOD_PARAM,
+ STR_PARAM,
+ NUMERIC_PARAM,
+ NUMERIC_FUNCTION_PARAM,
+ NUMERIC_METHOD_PARAM
+} phpdbg_param_type;
+
+typedef struct _phpdbg_input_t phpdbg_input_t;
+
+struct _phpdbg_input_t {
+ char * const *start;
+ char *string;
+ size_t length;
+ phpdbg_input_t **argv;
+ int argc;
+};
+
+typedef struct _phpdbg_param {
+ phpdbg_param_type type;
+ long num;
+ zend_ulong addr;
+ struct {
+ char *name;
+ long line;
+ } file;
+ struct {
+ char *class;
+ char *name;
+ } method;
+ char *str;
+ size_t len;
+} phpdbg_param_t;
+
+typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t*, const phpdbg_input_t* TSRMLS_DC);
+
+struct _phpdbg_command_t {
+ const char *name; /* Command name */
+ size_t name_len; /* Command name length */
+ const char *tip; /* Menu tip */
+ size_t tip_len; /* Menu tip length */
+ char alias; /* Alias */
+ phpdbg_command_handler_t handler; /* Command handler */
+ const phpdbg_command_t *subs; /* Sub Commands */
+ char arg_type; /* Accept args? */
+};
+/* }}} */
+
+/* {{{ misc */
+#define PHPDBG_STRL(s) s, sizeof(s)-1
+#define PHPDBG_MAX_CMD 500
+#define PHPDBG_FRAME(v) (PHPDBG_G(frame).v)
+#define PHPDBG_EX(v) (EG(current_execute_data)->v)
+
+typedef struct {
+ int num;
+ zend_execute_data *execute_data;
+} phpdbg_frame_t;
+/* }}} */
+
+
+
+/*
+* Workflow:
+* 1) read input
+* input takes the line from console, creates argc/argv
+* 2) parse parameters into suitable types based on arg_type
+* takes input from 1) and arg_type and creates parameters
+* 3) do command
+* executes commands
+* 4) destroy parameters
+* cleans up what was allocated by creation of parameters
+* 5) destroy input
+* cleans up what was allocated by creation of input
+*/
+
+/*
+* Input Management
+*/
+PHPDBG_API phpdbg_input_t* phpdbg_read_input(char *buffered TSRMLS_DC);
+PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t** TSRMLS_DC);
+
+/*
+* Argument Management
+*/
+PHPDBG_API phpdbg_input_t** phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC);
+PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC);
+#define phpdbg_argv_is(n, s) \
+ (memcmp(input->argv[n]->string, s, input->argv[n]->length) == SUCCESS)
+
+/*
+* Parameter Management
+*/
+PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char*, size_t, phpdbg_param_t* TSRMLS_DC);
+PHPDBG_API void phpdbg_clear_param(phpdbg_param_t* TSRMLS_DC);
+PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t*, phpdbg_param_t* TSRMLS_DC);
+PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *, const phpdbg_param_t * TSRMLS_DC);
+PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t * TSRMLS_DC);
+PHPDBG_API const char* phpdbg_get_param_type(const phpdbg_param_t* TSRMLS_DC);
+PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC);
+
+/*
+* Command Executor
+*/
+PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t*, phpdbg_input_t* TSRMLS_DC);
+
+/**
+ * Command Declarators
+ */
+#define PHPDBG_COMMAND_HANDLER(name) phpdbg_do_##name
+
+#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, has_args) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, has_args}
+
+#define PHPDBG_COMMAND_D(name, tip, alias, children, has_args) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, has_args}
+
+#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC)
+
+#define PHPDBG_COMMAND_ARGS param, input TSRMLS_CC
+
+#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0'}
+
+/*
+* Default Switch Case
+*/
+#define phpdbg_default_switch_case() \
+ default: \
+ phpdbg_error("Unsupported parameter type (%s) for command", phpdbg_get_param_type(param TSRMLS_CC)); \
+ break
+
+#endif /* PHPDBG_CMD_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "phpdbg.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_frame.h"
+#include "phpdbg_list.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+void phpdbg_restore_frame(TSRMLS_D) /* {{{ */
+{
+ if (PHPDBG_FRAME(num) == 0) {
+ return;
+ }
+
+ PHPDBG_FRAME(num) = 0;
+
+ /* move things back */
+ EG(current_execute_data) = PHPDBG_FRAME(execute_data);
+
+ EG(opline_ptr) = &PHPDBG_EX(opline);
+ EG(active_op_array) = PHPDBG_EX(op_array);
+ EG(return_value_ptr_ptr) = PHPDBG_EX(original_return_value);
+ EG(active_symbol_table) = PHPDBG_EX(symbol_table);
+ EG(This) = PHPDBG_EX(current_this);
+ EG(scope) = PHPDBG_EX(current_scope);
+ EG(called_scope) = PHPDBG_EX(current_called_scope);
+} /* }}} */
+
+void phpdbg_switch_frame(int frame TSRMLS_DC) /* {{{ */
+{
+ zend_execute_data *execute_data = PHPDBG_FRAME(num)?PHPDBG_FRAME(execute_data):EG(current_execute_data);
+ int i = 0;
+
+ if (PHPDBG_FRAME(num) == frame) {
+ phpdbg_notice("Already in frame #%d", frame);
+ return;
+ }
+
+ while (execute_data) {
+ if (i++ == frame) {
+ break;
+ }
+
+ do {
+ execute_data = execute_data->prev_execute_data;
+ } while (execute_data && execute_data->opline == NULL);
+ }
+
+ if (execute_data == NULL) {
+ phpdbg_error("No frame #%d", frame);
+ return;
+ }
+
+ phpdbg_restore_frame(TSRMLS_C);
+
+ if (frame > 0) {
+ PHPDBG_FRAME(num) = frame;
+
+ /* backup things and jump back */
+ PHPDBG_FRAME(execute_data) = EG(current_execute_data);
+ EG(current_execute_data) = execute_data;
+
+ EG(opline_ptr) = &PHPDBG_EX(opline);
+ EG(active_op_array) = PHPDBG_EX(op_array);
+ PHPDBG_FRAME(execute_data)->original_return_value = EG(return_value_ptr_ptr);
+ EG(return_value_ptr_ptr) = PHPDBG_EX(original_return_value);
+ EG(active_symbol_table) = PHPDBG_EX(symbol_table);
+ EG(This) = PHPDBG_EX(current_this);
+ EG(scope) = PHPDBG_EX(current_scope);
+ EG(called_scope) = PHPDBG_EX(current_called_scope);
+ }
+
+ phpdbg_notice("Switched to frame #%d", frame);
+ phpdbg_list_file(
+ zend_get_executed_filename(TSRMLS_C),
+ 3,
+ zend_get_executed_lineno(TSRMLS_C)-1,
+ zend_get_executed_lineno(TSRMLS_C)
+ TSRMLS_CC
+ );
+} /* }}} */
+
+static void phpdbg_dump_prototype(zval **tmp TSRMLS_DC) /* {{{ */
+{
+ zval **funcname, **class, **type, **args, **argstmp;
+ char is_class;
+
+ zend_hash_find(Z_ARRVAL_PP(tmp), "function", sizeof("function"),
+ (void **)&funcname);
+
+ if ((is_class = zend_hash_find(Z_ARRVAL_PP(tmp),
+ "object", sizeof("object"), (void **)&class)) == FAILURE) {
+ is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "class", sizeof("class"),
+ (void **)&class);
+ } else {
+ zend_get_object_classname(*class, (const char **)&Z_STRVAL_PP(class),
+ (zend_uint *)&Z_STRLEN_PP(class) TSRMLS_CC);
+ }
+
+ if (is_class == SUCCESS) {
+ zend_hash_find(Z_ARRVAL_PP(tmp), "type", sizeof("type"), (void **)&type);
+ }
+
+ phpdbg_write("%s%s%s(",
+ is_class == FAILURE?"":Z_STRVAL_PP(class),
+ is_class == FAILURE?"":Z_STRVAL_PP(type),
+ Z_STRVAL_PP(funcname)
+ );
+
+ if (zend_hash_find(Z_ARRVAL_PP(tmp), "args", sizeof("args"),
+ (void **)&args) == SUCCESS) {
+ HashPosition iterator;
+ const zend_function *func = phpdbg_get_function(
+ Z_STRVAL_PP(funcname), is_class == FAILURE ? NULL : Z_STRVAL_PP(class) TSRMLS_CC);
+ const zend_arg_info *arginfo = func ? func->common.arg_info : NULL;
+ int j = 0, m = func ? func->common.num_args : 0;
+ zend_bool is_variadic = 0;
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(args), &iterator);
+ while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(args),
+ (void **) &argstmp, &iterator) == SUCCESS) {
+ if (j) {
+ phpdbg_write(", ");
+ }
+ if (m && j < m) {
+#if PHP_VERSION_ID >= 50600
+ is_variadic = arginfo[j].is_variadic;
+#endif
+ phpdbg_write("%s=%s",
+ arginfo[j].name, is_variadic ? "[": "");
+ }
+ ++j;
+
+ zend_print_flat_zval_r(*argstmp TSRMLS_CC);
+ zend_hash_move_forward_ex(Z_ARRVAL_PP(args), &iterator);
+ }
+ if (is_variadic) {
+ phpdbg_write("]");
+ }
+ }
+ phpdbg_write(")");
+}
+
+void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
+{
+ zval zbacktrace;
+ zval **tmp;
+ zval **file, **line;
+ HashPosition position;
+ int i = 1, limit = num;
+ int user_defined;
+
+ if (limit < 0) {
+ phpdbg_error("Invalid backtrace size %d", limit);
+ }
+
+ zend_fetch_debug_backtrace(
+ &zbacktrace, 0, 0, limit TSRMLS_CC);
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL(zbacktrace), &position);
+ zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void**)&tmp, &position);
+ while (1) {
+ user_defined = zend_hash_find(Z_ARRVAL_PP(tmp), "file", sizeof("file"), (void **)&file);
+ zend_hash_find(Z_ARRVAL_PP(tmp), "line", sizeof("line"), (void **)&line);
+ zend_hash_move_forward_ex(Z_ARRVAL(zbacktrace), &position);
+
+ if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace),
+ (void**)&tmp, &position) == FAILURE) {
+ phpdbg_write("frame #0: {main} at %s:%ld", Z_STRVAL_PP(file), Z_LVAL_PP(line));
+ break;
+ }
+
+ if (user_defined == SUCCESS) {
+ phpdbg_write("frame #%d: ", i++);
+ phpdbg_dump_prototype(tmp TSRMLS_CC);
+ phpdbg_writeln(" at %s:%ld", Z_STRVAL_PP(file), Z_LVAL_PP(line));
+ } else {
+ phpdbg_write(" => ");
+ phpdbg_dump_prototype(tmp TSRMLS_CC);
+ phpdbg_writeln(" (internal function)");
+ }
+ }
+
+ phpdbg_writeln(EMPTY);
+ zval_dtor(&zbacktrace);
+} /* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_FRAME_H
+#define PHPDBG_FRAME_H
+
+#include "TSRM.h"
+
+void phpdbg_restore_frame(TSRMLS_D);
+void phpdbg_switch_frame(int TSRMLS_DC);
+void phpdbg_dump_backtrace(size_t TSRMLS_DC);
+
+#endif /* PHPDBG_FRAME_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_help.h"
+#include "phpdbg_print.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_break.h"
+#include "phpdbg_list.h"
+#include "phpdbg_info.h"
+#include "phpdbg_set.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+PHPDBG_HELP(exec) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("\tWill attempt execution, if compilation has not yet taken place, it occurs now");
+ phpdbg_writeln("The execution context must be set before execution can take place");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(step) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("You can enable and disable stepping at any phpdbg prompt during execution");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%sstepping 1", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%ss 1", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill enable stepping");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("While stepping is enabled you are presented with a prompt after the execution of each opcode");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(next) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_write("Step back into the vm and execute the next opcode");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%snext", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sn", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: is only useful while executing");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(until) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Step back into the vm, skipping breakpoints until the next source line");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%suntil", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%su", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: is only useful while executing");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(finish) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Step back into the vm, skipping breakpoints until past the end of the current stack");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%sfinish", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sF", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: this allows all breakpoints that would otherwise break execution in the current scope to be skipped");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(leave) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Step back into the vm, skipping breakpoints until the current stack is returning");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%sleave", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sL", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill cause a break when instructed to leave the current context");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: this allows inspection of the return value before it is returned");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(compile) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Pre-compilation of the execution context provides the opportunity to inspect opcodes before execution");
+ phpdbg_writeln("The execution context must be set for compilation to succeed");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%scompile", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sc", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill compile the current execution context, populating class/function/constant/etc tables");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: It is a good idea to clean the environment between each compilation");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(print) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("By default, print will show information about the current execution context");
+ phpdbg_writeln("Other printing commands give access to instruction information");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%sprint class \\my\\class", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sp c \\my\\class", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the instructions for the methods in \\my\\class");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sprint method \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sp m \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the instructions for \\my\\class::method");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sprint func .getSomething", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sp f .getSomething", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the instructions for ::getSomething in the active scope");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sprint func my_function", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sp f my_function", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the instructions for the global function my_function");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sprint opline", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sp o", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the instruction for the current opline");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sprint exec", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sp e", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the instructions for the execution context");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sprint stack", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sp s", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the instructions for the current stack");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Specific printers loaded are show below:");
+ phpdbg_notice("Commands");
+ {
+ const phpdbg_command_t *print_command = phpdbg_print_commands;
+
+ phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
+ while (print_command && print_command->name) {
+ if (print_command->alias) {
+ phpdbg_writeln("\t[%c]\t%s\t\t%s", print_command->alias, print_command->name, print_command->tip);
+ } else {
+ phpdbg_writeln("\t[ ]\t%s\t\t%s", print_command->name, print_command->tip);
+ }
+ ++print_command;
+ }
+ }
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(run) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Execute the current context inside the phpdbg vm");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%srun", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sr", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill cause execution of the context, if it is set.");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: The execution context must be set, but not necessarily compiled before execution occurs");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(eval) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Access to eval() allows you to change the environment during execution, careful though!!");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%seval $variable", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sE $variable", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print_r($variable) on the console, if it is defined");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%seval $variable = \"Hello phpdbg :)\"", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sE $variable = \"Hello phpdbg :)\"", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill set $variable in the current scope");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: eval() will always show the result; do not prefix the code with \"return\"");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(break) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Setting a breakpoint stops execution at a specific stage");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%sbreak [file] test.php:1", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb [F] test.php:1", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break execution on line 1 of test.php");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak [func] my_function", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb [f] my_function", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break execution on entry to my_function");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak [method] \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb [m] \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break execution on entry to \\my\\class::method");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak [address] 0x7ff68f570e08", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb [a] 0x7ff68f570e08", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break at the opline with the address provided");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak [address] my_function#1", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb [a] my_function#1", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break at the opline number 1 of the function my_function");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak [address] \\my\\class::method#2", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb [a] \\my\\class::method#2", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break at the opline number 2 of the method \\my\\class::method");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak address test.php:3", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb a test.php:3", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break at the opline number 3 of test.php");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak [lineno] 200", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb [l] 200", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break at line 200 of the currently executing file");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak on ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb on ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break when the condition evaluates to true");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak at phpdbg::isGreat if ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break at every opcode in phpdbg::isGreat when the condition evaluates to true");
+ phpdbg_writeln("\t%sbreak at test.php:20 if ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break at every opcode on line 20 of test.php when the condition evaluates to true");
+ phpdbg_write("\t");
+ phpdbg_notice("The location can be anything accepted by file, func, method, or address break commands");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak op ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb O ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill break on every occurence of the opcode provided");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%sbreak del 1", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sb d 1", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill remove the breakpoint with the given identifier");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: An address is only valid for the current compilation");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("The parameters enclosed by [] are usually optional, but help avoid ambigious commands");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Specific breakers loaded are show below:");
+ phpdbg_notice("Commands");
+ {
+ const phpdbg_command_t *break_command = phpdbg_break_commands;
+
+ phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
+ while (break_command && break_command->name) {
+ if (break_command->alias) {
+ phpdbg_writeln("\t[%c]\t%s\t\t%s", break_command->alias, break_command->name, break_command->tip);
+ } else {
+ phpdbg_writeln("\t[ ]\t%s\t\t%s", break_command->name, break_command->tip);
+ }
+ ++break_command;
+ }
+ }
+ phpdbg_writeln("Note: Conditional breaks are costly, use them sparingly!");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(clean) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("While debugging you may experience errors because of attempts to redeclare classes, constants or functions");
+ phpdbg_writeln("Cleaning the environment cleans these tables, so that files can be recompiled without exiting phpdbg");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(clear) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Clearing breakpoints means you can once again run code without interruption");
+ phpdbg_writeln("Note: all breakpoints are lost; be sure debugging is complete before clearing");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(info) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("info commands provide quick access to various types of information about the PHP environment");
+ phpdbg_writeln("Specific info commands are show below:");
+ phpdbg_notice("Commands");
+ {
+ const phpdbg_command_t *info_command = phpdbg_info_commands;
+
+ phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
+ while (info_command && info_command->name) {
+ if (info_command->alias) {
+ phpdbg_writeln("\t[%c]\t%s\t\t%s", info_command->alias, info_command->name, info_command->tip);
+ } else {
+ phpdbg_writeln("\t[ ]\t%s\t\t%s", info_command->name, info_command->tip);
+ }
+ ++info_command;
+ }
+ }
+
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(quiet) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Setting quietness on will stop the OPLINE output during execution");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%squiet 1", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sQ 1", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill silence OPLINE output, while");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%squiet 0", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sQ 0", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill enable OPLINE output again");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: Quietness is disabled automatically while stepping");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(back) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("The backtrace is built with the default debug backtrace functionality");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%sback 5", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%st 5", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill limit the number of frames to 5, the default is no limit");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: it is not necessary for an exception to be thrown to show a backtrace");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(frame) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("When viewing a backtrace, it is sometimes useful to jump to a frame in that trace");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%sframe 2", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sf 2", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill go to frame 2, temporarily affecting scope and allowing access to the variables in that frame");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: the current frame is restored when execution continues");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(list) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("The list command displays source code for the given argument");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%slist [lines] 2", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sl [l] 2", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print next 2 lines from the current file");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%slist [func] my_function", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sl [f] my_function", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the source of the global function \"my_function\"");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%slist [func] .mine", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sl [f] .mine", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the source of the method \"mine\" from the active scope");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%slist [method] my::method", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sl [m] my::method", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the source of \"my::method\"");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%slist c myClass", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sl c myClass", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill print the source of \"myClass\"");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: before listing functions you must have a populated function table, try compile!!");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("The parameters enclosed by [] are usually optional, but help avoid ambigious commands");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Specific listers loaded are show below:");
+ phpdbg_notice("Commands");
+ {
+ const phpdbg_command_t *list_command = phpdbg_list_commands;
+
+ phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
+ while (list_command && list_command->name) {
+ if (list_command->alias) {
+ phpdbg_writeln("\t[%c]\t%s\t\t%s", list_command->alias, list_command->name, list_command->tip);
+ } else {
+ phpdbg_writeln("\t[ ]\t%s\t\t%s", list_command->name, list_command->tip);
+ }
+ ++list_command;
+ }
+ }
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(oplog) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Even when quietness is enabled you may wish to save opline logs to a file");
+ phpdbg_writeln("Setting a new oplog closes the previously open log");
+ phpdbg_writeln("The log includes a high resolution timestamp on each entry");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%soplog /path/to/my.oplog", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sO /path/to/my.oplog", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill open the file /path/to/my.oplog for writing, creating it if it does not exist");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("\t%soplog 0", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sO 0", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill close the currently open log file, disabling oplog");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: upon failure to open a new oplog, the last oplog is held open");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(set) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Configure how phpdbg looks and behaves with the set command");
+ phpdbg_writeln("Specific set commands are show below:");
+ phpdbg_notice("Commands");
+ {
+ const phpdbg_command_t *set_command = phpdbg_set_commands;
+
+ phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
+ while (set_command && set_command->name) {
+ if (set_command->alias) {
+ phpdbg_writeln("\t[%c]\t%s\t\t%s", set_command->alias, set_command->name, set_command->tip);
+ } else {
+ phpdbg_writeln("\t[ ]\t%s\t\t%s", set_command->name, set_command->tip);
+ }
+ ++set_command;
+ }
+ }
+#ifndef _WIN32
+ phpdbg_notice("Colors");
+ {
+ const phpdbg_color_t *color = phpdbg_get_colors(TSRMLS_C);
+
+ if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
+ phpdbg_writeln("\t%-20s\t\tExample", "Name");
+ } else {
+ phpdbg_writeln("\tName");
+ }
+
+ while (color && color->name) {
+ if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
+ phpdbg_writeln(
+ "\t%-20s\t\t\033[%smphpdbg rocks :)\033[0m", color->name, color->code);
+ } else {
+ phpdbg_writeln("\t%s", color->name);
+ }
+ ++color;
+ }
+ }
+ phpdbg_writeln("The <element> for set color can be \"prompt\", \"notice\", or \"error\"");
+#endif
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(register) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Register any global function for use as a command in phpdbg console");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%sregister scandir", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%sR scandir", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill register the scandir function for use in phpdbg");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: arguments passed as strings, return (if present) print_r'd on console");
+ if (zend_hash_num_elements(&PHPDBG_G(registered))) {
+ HashPosition position;
+ char *name = NULL;
+ zend_uint name_len = 0;
+
+ phpdbg_notice("Registered Functions (%d)", zend_hash_num_elements(&PHPDBG_G(registered)));
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(registered), &position);
+ zend_hash_get_current_key_ex(&PHPDBG_G(registered), &name, &name_len, NULL, 1, &position) == HASH_KEY_IS_STRING;
+ zend_hash_move_forward_ex(&PHPDBG_G(registered), &position)) {
+ phpdbg_writeln("|-------> %s", name);
+ efree(name);
+ }
+ }
+
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(source) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Sourcing a phpdbginit during your debugging session might save some time");
+ phpdbg_writeln("The source command can also be used to export breakpoints to a phpdbginit file");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%ssource /my/init", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%s. /my/init", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill execute the phpdbginit file at /my/init");
+ phpdbg_writeln("\t%ssource export /my/init", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%s. export /my/init", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill export breakpoints to /my/init in phpdbginit file format");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(shell) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Direct access to shell commands saves having to switch windows/consoles");
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Examples");
+ phpdbg_writeln("\t%sshell ls /usr/src/php-src", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\t%s- ls /usr/src/php-src", phpdbg_get_prompt(TSRMLS_C));
+ phpdbg_writeln("\tWill execute ls /usr/src/php-src, displaying the output in the console");
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln("Note: read only commands please!");
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_HELP(options) /* {{{ */
+{
+ phpdbg_help_header();
+ phpdbg_writeln("Below are the command line options supported by phpdbg");
+ phpdbg_notice("Command Line Options and Flags");
+ phpdbg_writeln(" -c\t-c/my/php.ini\t\tSet php.ini file to load");
+ phpdbg_writeln(" -d\t-dmemory_limit=4G\tSet a php.ini directive");
+ phpdbg_writeln(" -n\tN/A\t\t\tDisable default php.ini");
+ phpdbg_writeln(" -q\tN/A\t\t\tSupress welcome banner");
+ phpdbg_writeln(" -e\t-emytest.php\t\tSet execution context");
+ phpdbg_writeln(" -v\tN/A\t\t\tEnable oplog output");
+ phpdbg_writeln(" -s\tN/A\t\t\tEnable stepping");
+ phpdbg_writeln(" -b\tN/A\t\t\tDisable colour");
+ phpdbg_writeln(" -i\t-imy.init\t\tSet .phpdbginit file");
+ phpdbg_writeln(" -I\tN/A\t\t\tIgnore default .phpdbginit");
+ phpdbg_writeln(" -O\t-Omy.oplog\t\tSets oplog output file");
+ phpdbg_writeln(" -r\tN/A\t\t\tRun execution context");
+ phpdbg_writeln(" -E\tN/A\t\t\tEnable step through eval, careful!");
+ phpdbg_writeln(" -S\t-Scli\t\t\tOverride SAPI name, careful!");
+#ifndef _WIN32
+ phpdbg_writeln(" -l\t-l4000\t\t\tSetup remote console ports");
+ phpdbg_writeln(" -a\t-a192.168.0.3\t\tSetup remote console bind address");
+#endif
+ phpdbg_writeln(" -V\tN/A\t\t\tVersion number");
+ phpdbg_notice("Passing -rr will quit automatically after execution");
+#ifndef _WIN32
+ phpdbg_writeln("Remote Console Mode");
+ phpdbg_notice("For security, phpdbg will bind only to the loopback interface by default");
+ phpdbg_writeln("-a without an argument implies all; phpdbg will bind to all available interfaces.");
+ phpdbg_writeln("specify both stdin and stdout with -lstdin/stdout; by default stdout is stdin * 2.");
+ phpdbg_notice("Steps should be taken to secure this service if bound to a public interface/port");
+#endif
+ phpdbg_help_footer();
+ return SUCCESS;
+} /* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_HELP_H
+#define PHPDBG_HELP_H
+
+#include "TSRM.h"
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_HELP(name) PHPDBG_COMMAND(help_##name)
+
+/**
+ * Helper Forward Declarations
+ */
+PHPDBG_HELP(exec);
+PHPDBG_HELP(compile);
+PHPDBG_HELP(step);
+PHPDBG_HELP(next);
+PHPDBG_HELP(run);
+PHPDBG_HELP(eval);
+PHPDBG_HELP(until);
+PHPDBG_HELP(finish);
+PHPDBG_HELP(leave);
+PHPDBG_HELP(print);
+PHPDBG_HELP(break);
+PHPDBG_HELP(clean);
+PHPDBG_HELP(clear);
+PHPDBG_HELP(info);
+PHPDBG_HELP(back);
+PHPDBG_HELP(frame);
+PHPDBG_HELP(quiet);
+PHPDBG_HELP(list);
+PHPDBG_HELP(set);
+PHPDBG_HELP(register);
+PHPDBG_HELP(options);
+PHPDBG_HELP(source);
+PHPDBG_HELP(shell);
+
+/**
+ * Commands
+ */
+static const phpdbg_command_t phpdbg_help_commands[] = {
+ PHPDBG_COMMAND_D_EX(exec, "the execution context should be a valid path", 'e', help_exec, NULL, 0),
+ PHPDBG_COMMAND_D_EX(compile, "allow inspection of code before execution", 'c', help_compile, NULL, 0),
+ PHPDBG_COMMAND_D_EX(step, "step through execution to break at every opcode", 's', help_step, NULL, 0),
+ PHPDBG_COMMAND_D_EX(next, "continue executing while stepping or after breaking", 'n', help_next, NULL, 0),
+ PHPDBG_COMMAND_D_EX(run, "execute inside the phpdbg vm", 'r', help_run, NULL, 0),
+ PHPDBG_COMMAND_D_EX(eval, "access to eval() allows affecting the environment", 'E', help_eval, NULL, 0),
+ PHPDBG_COMMAND_D_EX(until, "continue until the current line is executed", 'u', help_until, NULL, 0),
+ PHPDBG_COMMAND_D_EX(finish, "continue until the current function has returned", 'F', help_finish, NULL, 0),
+ PHPDBG_COMMAND_D_EX(leave, "continue until the current function is returning", 'L', help_leave, NULL, 0),
+ PHPDBG_COMMAND_D_EX(print, "print context information or instructions", 'p', help_print, NULL, 0),
+ PHPDBG_COMMAND_D_EX(break, "breakpoints allow execution interruption", 'b', help_break, NULL, 0),
+ PHPDBG_COMMAND_D_EX(clean, "resetting the environment is useful while debugging", 'X', help_clean, NULL, 0),
+ PHPDBG_COMMAND_D_EX(clear, "reset breakpoints to execute without interruption", 'c', help_clear, NULL, 0),
+ PHPDBG_COMMAND_D_EX(info, "quick access to useful information on the console", 'i', help_info, NULL, 0),
+ PHPDBG_COMMAND_D_EX(back, "show debug backtrace information during execution", 't', help_back, NULL, 0),
+ PHPDBG_COMMAND_D_EX(frame, "switch to a frame in the current stack for inspection", 'f', help_frame, NULL, 0),
+ PHPDBG_COMMAND_D_EX(quiet, "be quiet during execution", 'Q', help_quiet, NULL, 0),
+ PHPDBG_COMMAND_D_EX(list, "list code gives you quick access to code", 'l', help_list, NULL, 0),
+ PHPDBG_COMMAND_D_EX(set, "configure how phpdbg looks and behaves", 'S', help_set, NULL, 0),
+ PHPDBG_COMMAND_D_EX(register, "register a function for use as a command", 'R', help_register,NULL, 0),
+ PHPDBG_COMMAND_D_EX(options, "show information about command line options", 'o', help_options, NULL, 0),
+ PHPDBG_COMMAND_D_EX(source, "load a phpdbginit file at the console", '.', help_source, NULL, 0),
+ PHPDBG_COMMAND_D_EX(shell, "execute system commands with direct shell access", '-', help_shell, NULL, 0),
+ PHPDBG_END_COMMAND
+};
+
+#define phpdbg_help_header() \
+ phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
+#define phpdbg_help_footer() \
+ phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES);
+
+#endif /* PHPDBG_HELP_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "phpdbg.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_info.h"
+#include "phpdbg_bp.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+PHPDBG_INFO(break) /* {{{ */
+{
+ phpdbg_print_breakpoints(PHPDBG_BREAK_FILE TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_SYM TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_OPLINE TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_FILE_OPLINE TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_FUNCTION_OPLINE TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD_OPLINE TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_COND TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_OPCODE TSRMLS_CC);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(files) /* {{{ */
+{
+ HashPosition pos;
+ char *fname;
+
+ phpdbg_notice("Included files: %d",
+ zend_hash_num_elements(&EG(included_files)));
+
+ zend_hash_internal_pointer_reset_ex(&EG(included_files), &pos);
+ while (zend_hash_get_current_key_ex(&EG(included_files), &fname,
+ NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) {
+ phpdbg_writeln("File: %s", fname);
+ zend_hash_move_forward_ex(&EG(included_files), &pos);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(error) /* {{{ */
+{
+ if (PG(last_error_message)) {
+ phpdbg_writeln("Last error: %s at %s line %d",
+ PG(last_error_message), PG(last_error_file), PG(last_error_lineno));
+ } else {
+ phpdbg_notice("No error found!");
+ }
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(vars) /* {{{ */
+{
+ HashTable vars;
+ HashPosition pos;
+ char *var;
+ zval **data;
+
+ if (!EG(active_op_array)) {
+ phpdbg_error("No active op array!");
+ return SUCCESS;
+ }
+
+ if (!EG(active_symbol_table)) {
+ zend_rebuild_symbol_table(TSRMLS_C);
+
+ if (!EG(active_symbol_table)) {
+ phpdbg_error("No active symbol table!");
+ return SUCCESS;
+ }
+ }
+
+ zend_hash_init(&vars, 8, NULL, NULL, 0);
+
+ zend_hash_internal_pointer_reset_ex(EG(active_symbol_table), &pos);
+ while (zend_hash_get_current_key_ex(EG(active_symbol_table), &var,
+ NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) {
+ zend_hash_get_current_data_ex(EG(active_symbol_table), (void **)&data, &pos);
+ if (*var != '_') {
+ zend_hash_update(
+ &vars, var, strlen(var)+1, (void**)data, sizeof(zval*), NULL);
+ }
+ zend_hash_move_forward_ex(EG(active_symbol_table), &pos);
+ }
+
+ {
+ zend_op_array *ops = EG(active_op_array);
+
+ if (ops->function_name) {
+ if (ops->scope) {
+ phpdbg_notice(
+ "Variables in %s::%s() (%d)", ops->scope->name, ops->function_name, zend_hash_num_elements(&vars));
+ } else {
+ phpdbg_notice(
+ "Variables in %s() (%d)", ops->function_name, zend_hash_num_elements(&vars));
+ }
+ } else {
+ if (ops->filename) {
+ phpdbg_notice(
+ "Variables in %s (%d)", ops->filename, zend_hash_num_elements(&vars));
+ } else {
+ phpdbg_notice(
+ "Variables @ %p (%d)", ops, zend_hash_num_elements(&vars));
+ }
+ }
+ }
+
+ if (zend_hash_num_elements(&vars)) {
+ phpdbg_writeln("Address\t\tRefs\tType\t\tVariable");
+ for (zend_hash_internal_pointer_reset_ex(&vars, &pos);
+ zend_hash_get_current_data_ex(&vars, (void**) &data, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(&vars, &pos)) {
+ char *var;
+
+ zend_hash_get_current_key_ex(&vars, &var, NULL, NULL, 0, &pos);
+
+ if (*data) {
+ phpdbg_write(
+ "%p\t%d\t",
+ *data,
+ Z_REFCOUNT_PP(data));
+
+ switch (Z_TYPE_PP(data)) {
+ case IS_STRING: phpdbg_write("(string)\t"); break;
+ case IS_LONG: phpdbg_write("(integer)\t"); break;
+ case IS_DOUBLE: phpdbg_write("(float)\t"); break;
+ case IS_RESOURCE: phpdbg_write("(resource)\t"); break;
+ case IS_ARRAY: phpdbg_write("(array)\t"); break;
+ case IS_OBJECT: phpdbg_write("(object)\t"); break;
+ case IS_NULL: phpdbg_write("(null)\t"); break;
+ }
+
+ if (Z_TYPE_PP(data) == IS_RESOURCE) {
+ int type;
+
+ phpdbg_writeln(
+ "%s$%s", Z_ISREF_PP(data) ? "&": "", var);
+ if (zend_list_find(Z_RESVAL_PP(data), &type)) {
+ phpdbg_write(
+ "|-------(typeof)------> (%s)",
+ zend_rsrc_list_get_rsrc_type(type TSRMLS_CC));
+ } else {
+ phpdbg_write(
+ "|-------(typeof)------> (unknown)");
+ }
+ phpdbg_writeln(EMPTY);
+ } else if (Z_TYPE_PP(data) == IS_OBJECT) {
+ phpdbg_writeln(
+ "%s$%s", Z_ISREF_PP(data) ? "&": "", var);
+ phpdbg_write(
+ "|-----(instanceof)----> (%s)", Z_OBJCE_PP(data)->name);
+ phpdbg_writeln(EMPTY);
+ } else {
+ phpdbg_write(
+ "%s$%s", Z_ISREF_PP(data) ? "&": "", var);
+ }
+ } else {
+ phpdbg_write(
+ "n/a\tn/a\tn/a\t$%s", var);
+ }
+ phpdbg_writeln(EMPTY);
+ }
+ }
+
+ zend_hash_destroy(&vars);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(literal) /* {{{ */
+{
+ if ((EG(in_execution) && EG(active_op_array)) || PHPDBG_G(ops)) {
+ zend_op_array *ops = EG(active_op_array) ? EG(active_op_array) : PHPDBG_G(ops);
+ int literal = 0, count = ops->last_literal-1;
+
+ if (ops->function_name) {
+ if (ops->scope) {
+ phpdbg_notice(
+ "Literal Constants in %s::%s() (%d)", ops->scope->name, ops->function_name, count);
+ } else {
+ phpdbg_notice(
+ "Literal Constants in %s() (%d)", ops->function_name, count);
+ }
+ } else {
+ if (ops->filename) {
+ phpdbg_notice(
+ "Literal Constants in %s (%d)", ops->filename, count);
+ } else {
+ phpdbg_notice(
+ "Literal Constants @ %p (%d)", ops, count);
+ }
+ }
+
+ while (literal < ops->last_literal) {
+ if (Z_TYPE(ops->literals[literal].constant) != IS_NULL) {
+ phpdbg_write("|-------- C%u -------> [", literal);
+ zend_print_zval(
+ &ops->literals[literal].constant, 0);
+ phpdbg_write("]");
+ phpdbg_writeln(EMPTY);
+ }
+ literal++;
+ }
+ } else {
+ phpdbg_error("Not executing!");
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(memory) /* {{{ */
+{
+ if (is_zend_mm(TSRMLS_C)) {
+ phpdbg_notice("Memory Manager Information");
+ phpdbg_notice("Current");
+ phpdbg_writeln("|-------> Used:\t%.3f kB",
+ (float) (zend_memory_usage(0 TSRMLS_CC)/1024));
+ phpdbg_writeln("|-------> Real:\t%.3f kB",
+ (float) (zend_memory_usage(1 TSRMLS_CC)/1024));
+ phpdbg_notice("Peak");
+ phpdbg_writeln("|-------> Used:\t%.3f kB",
+ (float) (zend_memory_peak_usage(0 TSRMLS_CC)/1024));
+ phpdbg_writeln("|-------> Real:\t%.3f kB",
+ (float) (zend_memory_peak_usage(1 TSRMLS_CC)/1024));
+ } else {
+ phpdbg_error("Memory Manager Disabled!");
+ }
+ return SUCCESS;
+} /* }}} */
+
+static inline void phpdbg_print_class_name(zend_class_entry **ce TSRMLS_DC) /* {{{ */
+{
+ phpdbg_write(
+ "%s %s %s (%d)",
+ ((*ce)->type == ZEND_USER_CLASS) ?
+ "User" : "Internal",
+ ((*ce)->ce_flags & ZEND_ACC_INTERFACE) ?
+ "Interface" :
+ ((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ?
+ "Abstract Class" :
+ "Class",
+ (*ce)->name, zend_hash_num_elements(&(*ce)->function_table));
+} /* }}} */
+
+PHPDBG_INFO(classes) /* {{{ */
+{
+ HashPosition position;
+ zend_class_entry **ce;
+ HashTable classes;
+
+ zend_hash_init(&classes, 8, NULL, NULL, 0);
+
+ for (zend_hash_internal_pointer_reset_ex(EG(class_table), &position);
+ zend_hash_get_current_data_ex(EG(class_table), (void**)&ce, &position) == SUCCESS;
+ zend_hash_move_forward_ex(EG(class_table), &position)) {
+
+ if ((*ce)->type == ZEND_USER_CLASS) {
+ zend_hash_next_index_insert(
+ &classes, ce, sizeof(ce), NULL);
+ }
+ }
+
+ phpdbg_notice("User Classes (%d)",
+ zend_hash_num_elements(&classes));
+
+ for (zend_hash_internal_pointer_reset_ex(&classes, &position);
+ zend_hash_get_current_data_ex(&classes, (void**)&ce, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&classes, &position)) {
+
+ phpdbg_print_class_name(ce TSRMLS_CC);
+ phpdbg_writeln(EMPTY);
+
+ if ((*ce)->parent) {
+ zend_class_entry *pce = (*ce)->parent;
+ do {
+ phpdbg_write("|-------- ");
+ phpdbg_print_class_name(&pce TSRMLS_CC);
+ phpdbg_writeln(EMPTY);
+ } while ((pce = pce->parent));
+ }
+
+ if ((*ce)->info.user.filename) {
+ phpdbg_writeln(
+ "|---- in %s on line %u",
+ (*ce)->info.user.filename,
+ (*ce)->info.user.line_start);
+ } else {
+ phpdbg_writeln("|---- no source code");
+ }
+ phpdbg_writeln(EMPTY);
+ }
+
+ zend_hash_destroy(&classes);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(funcs) /* {{{ */
+{
+ HashPosition position;
+ zend_function *zf, **pzf;
+ HashTable functions;
+
+ zend_hash_init(&functions, 8, NULL, NULL, 0);
+
+ for (zend_hash_internal_pointer_reset_ex(EG(function_table), &position);
+ zend_hash_get_current_data_ex(EG(function_table), (void**)&zf, &position) == SUCCESS;
+ zend_hash_move_forward_ex(EG(function_table), &position)) {
+
+ if (zf->type == ZEND_USER_FUNCTION) {
+ zend_hash_next_index_insert(
+ &functions, (void**) &zf, sizeof(zend_function), NULL);
+ }
+ }
+
+ phpdbg_notice("User Functions (%d)",
+ zend_hash_num_elements(&functions));
+
+ for (zend_hash_internal_pointer_reset_ex(&functions, &position);
+ zend_hash_get_current_data_ex(&functions, (void**)&pzf, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&functions, &position)) {
+ zend_op_array *op_array = &((*pzf)->op_array);
+
+ phpdbg_writeln(
+ "|-------- %s in %s on line %d",
+ op_array->function_name ? op_array->function_name : "{main}",
+ op_array->filename ? op_array->filename : "(no source code)",
+ op_array->line_start);
+ }
+
+ zend_hash_destroy(&functions);
+
+ return SUCCESS;
+} /* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_INFO_H
+#define PHPDBG_INFO_H
+
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_INFO(name) PHPDBG_COMMAND(info_##name)
+
+PHPDBG_INFO(files);
+PHPDBG_INFO(break);
+PHPDBG_INFO(classes);
+PHPDBG_INFO(funcs);
+PHPDBG_INFO(error);
+PHPDBG_INFO(vars);
+PHPDBG_INFO(literal);
+PHPDBG_INFO(memory);
+
+static const phpdbg_command_t phpdbg_info_commands[] = {
+ PHPDBG_COMMAND_D_EX(break, "show breakpoints", 'b', info_break, NULL, 0),
+ PHPDBG_COMMAND_D_EX(files, "show included files", 'F', info_files, NULL, 0),
+ PHPDBG_COMMAND_D_EX(classes, "show loaded classes", 'c', info_classes, NULL, 0),
+ PHPDBG_COMMAND_D_EX(funcs, "show loaded classes", 'f', info_funcs, NULL, 0),
+ PHPDBG_COMMAND_D_EX(error, "show last error", 'e', info_error, NULL, 0),
+ PHPDBG_COMMAND_D_EX(vars, "show active variables", 'v', info_vars, NULL, 0),
+ PHPDBG_COMMAND_D_EX(literal, "show active literal constants", 'l', info_literal, NULL, 0),
+ PHPDBG_COMMAND_D_EX(memory, "show memory manager stats", 'm', info_memory, NULL, 0),
+ PHPDBG_END_COMMAND
+};
+
+#endif /* PHPDBG_INFO_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#ifndef _WIN32
+# include <sys/mman.h>
+# include <unistd.h>
+#endif
+#include <fcntl.h>
+#include "phpdbg.h"
+#include "phpdbg_list.h"
+#include "phpdbg_utils.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+PHPDBG_LIST(lines) /* {{{ */
+{
+ if (!PHPDBG_G(exec) && !zend_is_executing(TSRMLS_C)) {
+ phpdbg_error("Not executing, and execution context not set");
+ return SUCCESS;
+ }
+
+ switch (param->type) {
+ case NUMERIC_PARAM:
+ case EMPTY_PARAM:
+ phpdbg_list_file(phpdbg_current_file(TSRMLS_C),
+ param->type == EMPTY_PARAM ? 0 : (param->num < 0 ? 1 - param->num : param->num),
+ (param->type != EMPTY_PARAM && param->num < 0 ? param->num : 0) + zend_get_executed_lineno(TSRMLS_C),
+ 0 TSRMLS_CC);
+ break;
+ case FILE_PARAM:
+ phpdbg_list_file(param->file.name, param->file.line, 0, 0 TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_LIST(func) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM:
+ phpdbg_list_function_byname(
+ param->str, param->len TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_LIST(method) /* {{{ */
+{
+ switch (param->type) {
+ case METHOD_PARAM: {
+ zend_class_entry **ce;
+
+ if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
+ zend_function *function;
+ char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
+
+ if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) {
+ phpdbg_list_function(function TSRMLS_CC);
+ } else {
+ phpdbg_error("Could not find %s::%s", param->method.class, param->method.name);
+ }
+
+ efree(lcname);
+ } else {
+ phpdbg_error("Could not find the class %s", param->method.class);
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_LIST(class) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM: {
+ zend_class_entry **ce;
+
+ if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
+ if ((*ce)->type == ZEND_USER_CLASS) {
+ if ((*ce)->info.user.filename) {
+ phpdbg_list_file(
+ (*ce)->info.user.filename,
+ (*ce)->info.user.line_end - (*ce)->info.user.line_start + 1,
+ (*ce)->info.user.line_start, 0 TSRMLS_CC
+ );
+ } else {
+ phpdbg_error("The source of the requested class (%s) cannot be found", (*ce)->name);
+ }
+ } else {
+ phpdbg_error("The class requested (%s) is not user defined", (*ce)->name);
+ }
+ } else {
+ phpdbg_error("The requested class (%s) could not be found", param->str);
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+void phpdbg_list_file(const char *filename, long count, long offset, int highlight TSRMLS_DC) /* {{{ */
+{
+ unsigned char *mem, *pos, *last_pos, *end_pos;
+ struct stat st;
+#ifndef _WIN32
+ int fd;
+#else
+ HANDLE fd, map;
+#endif
+ int all_content = (count == 0);
+ int line = 0, displayed = 0;
+
+ if (VCWD_STAT(filename, &st) == FAILURE) {
+ phpdbg_error("Failed to stat file %s", filename);
+ return;
+ }
+
+#ifndef _WIN32
+ if ((fd = VCWD_OPEN(filename, O_RDONLY)) == FAILURE) {
+ phpdbg_error("Failed to open file %s to list", filename);
+ return;
+ }
+
+ pos = last_pos = mem = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ end_pos = mem + st.st_size;
+#else
+
+ fd = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (fd == INVALID_HANDLE_VALUE) {
+ phpdbg_error("Failed to open file!");
+ return;
+ }
+
+ map = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (map == NULL) {
+ phpdbg_error("Failed to map file!");
+ CloseHandle(fd);
+ return;
+ }
+
+ pos = last_pos = mem = (char*) MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
+ if (mem == NULL) {
+ phpdbg_error("Failed to map file in memory");
+ CloseHandle(map);
+ CloseHandle(fd);
+ return;
+ }
+ end_pos = mem + st.st_size;
+#endif
+ while (1) {
+ if (pos == end_pos) {
+ break;
+ }
+
+ pos = memchr(last_pos, '\n', end_pos - last_pos);
+
+ if (!pos) {
+ /* No more line breaks */
+ pos = end_pos;
+ }
+
+ ++line;
+
+ if (!offset || offset <= line) {
+ /* Without offset, or offset reached */
+ if (!highlight) {
+ phpdbg_writeln("%05u: %.*s", line, (int)(pos - last_pos), last_pos);
+ } else {
+ if (highlight != line) {
+ phpdbg_writeln(" %05u: %.*s", line, (int)(pos - last_pos), last_pos);
+ } else {
+ phpdbg_writeln(">%05u: %.*s", line, (int)(pos - last_pos), last_pos);
+ }
+ }
+ ++displayed;
+ }
+
+ last_pos = pos + 1;
+
+ if (!all_content && displayed == count) {
+ /* Reached max line to display */
+ break;
+ }
+ }
+
+#ifndef _WIN32
+ munmap(mem, st.st_size);
+ close(fd);
+#else
+ UnmapViewOfFile(mem);
+ CloseHandle(map);
+ CloseHandle(fd);
+#endif
+} /* }}} */
+
+void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */
+{
+ const zend_op_array *ops;
+
+ if (fbc->type != ZEND_USER_FUNCTION) {
+ phpdbg_error("The function requested (%s) is not user defined", fbc->common.function_name);
+ return;
+ }
+
+ ops = (zend_op_array*)fbc;
+
+ phpdbg_list_file(ops->filename,
+ ops->line_end - ops->line_start + 1, ops->line_start, 0 TSRMLS_CC);
+} /* }}} */
+
+void phpdbg_list_function_byname(const char *str, size_t len TSRMLS_DC) /* {{{ */
+{
+ HashTable *func_table = EG(function_table);
+ zend_function* fbc;
+ char *func_name = (char*) str;
+ size_t func_name_len = len;
+
+ /* search active scope if begins with period */
+ if (func_name[0] == '.') {
+ if (EG(scope)) {
+ func_name++;
+ func_name_len--;
+
+ func_table = &EG(scope)->function_table;
+ } else {
+ phpdbg_error("No active class");
+ return;
+ }
+ } else if (!EG(function_table)) {
+ phpdbg_error("No function table loaded");
+ return;
+ } else {
+ func_table = EG(function_table);
+ }
+
+ /* use lowercase names, case insensitive */
+ func_name = zend_str_tolower_dup(func_name, func_name_len);
+
+ if (zend_hash_find(func_table, func_name, func_name_len+1, (void**)&fbc) == SUCCESS) {
+ phpdbg_list_function(fbc TSRMLS_CC);
+ } else {
+ phpdbg_error("Function %s not found", func_name);
+ }
+
+ efree(func_name);
+} /* }}} */
+
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_LIST_H
+#define PHPDBG_LIST_H
+
+#include "TSRM.h"
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_LIST(name) PHPDBG_COMMAND(list_##name)
+#define PHPDBG_LIST_HANDLER(name) PHPDBG_COMMAND_HANDLER(list_##name)
+
+PHPDBG_LIST(lines);
+PHPDBG_LIST(class);
+PHPDBG_LIST(method);
+PHPDBG_LIST(func);
+
+void phpdbg_list_function_byname(const char *, size_t TSRMLS_DC);
+void phpdbg_list_function(const zend_function* TSRMLS_DC);
+void phpdbg_list_file(const char*, long, long, int TSRMLS_DC);
+
+static const phpdbg_command_t phpdbg_list_commands[] = {
+ PHPDBG_COMMAND_D_EX(lines, "lists the specified lines", 'l', list_lines, NULL, 1),
+ PHPDBG_COMMAND_D_EX(class, "lists the specified class", 'c', list_class, NULL, 1),
+ PHPDBG_COMMAND_D_EX(method, "lists the specified method", 'm', list_method, NULL, 1),
+ PHPDBG_COMMAND_D_EX(func, "lists the specified function", 'f', list_func, NULL, 1),
+ PHPDBG_END_COMMAND
+};
+
+#endif /* PHPDBG_LIST_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "zend_vm_opcodes.h"
+#include "zend_compile.h"
+#include "phpdbg_opcode.h"
+#include "phpdbg_utils.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+static inline zend_uint phpdbg_decode_literal(zend_op_array *ops, zend_literal *literal TSRMLS_DC) /* {{{ */
+{
+ int iter = 0;
+
+ while (iter < ops->last_literal) {
+ if (literal == &ops->literals[iter]) {
+ return iter;
+ }
+ iter++;
+ }
+
+ return 0;
+} /* }}} */
+
+static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, zend_uint type, HashTable *vars TSRMLS_DC) /* {{{ */
+{
+ char *decode = NULL;
+
+ switch (type &~ EXT_TYPE_UNUSED) {
+ case IS_CV:
+ asprintf(&decode, "$%s", ops->vars[op->var].name);
+ break;
+
+ case IS_VAR:
+ case IS_TMP_VAR: {
+ zend_ulong id = 0, *pid = NULL;
+ if (zend_hash_index_find(vars, (zend_ulong) ops->vars - op->var, (void**) &pid) != SUCCESS) {
+ id = zend_hash_num_elements(vars);
+ zend_hash_index_update(
+ vars, (zend_ulong) ops->vars - op->var,
+ (void**) &id,
+ sizeof(zend_ulong), NULL);
+ } else id = *pid;
+ asprintf(&decode, "@%lu", id);
+ } break;
+
+ case IS_CONST:
+ asprintf(&decode, "C%u", phpdbg_decode_literal(ops, op->literal TSRMLS_CC));
+ break;
+
+ case IS_UNUSED:
+ asprintf(&decode, "<unused>");
+ break;
+ }
+ return decode;
+} /* }}} */
+
+char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC) /*{{{ */
+{
+ char *decode[4] = {NULL, NULL, NULL, NULL};
+
+ switch (op->opcode) {
+ case ZEND_JMP:
+#ifdef ZEND_GOTO
+ case ZEND_GOTO:
+#endif
+#ifdef ZEND_FAST_CALL
+ case ZEND_FAST_CALL:
+#endif
+ asprintf(&decode[1], "J%ld", op->op1.jmp_addr - ops->opcodes);
+ goto format;
+
+ case ZEND_JMPZNZ:
+ decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
+ asprintf(
+ &decode[2], "J%u or J%lu", op->op2.opline_num, op->extended_value);
+ goto result;
+
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+
+#ifdef ZEND_JMP_SET
+ case ZEND_JMP_SET:
+#endif
+#ifdef ZEND_JMP_SET_VAR
+ case ZEND_JMP_SET_VAR:
+#endif
+ decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
+ asprintf(
+ &decode[2], "J%ld", op->op2.jmp_addr - ops->opcodes);
+ goto result;
+
+ case ZEND_RECV_INIT:
+ goto result;
+
+ default: {
+ decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
+ decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type, vars TSRMLS_CC);
+result:
+ decode[3] = phpdbg_decode_op(ops, &op->result, op->result_type, vars TSRMLS_CC);
+format:
+ asprintf(
+ &decode[0],
+ "%-20s %-20s %-20s",
+ decode[1] ? decode[1] : "",
+ decode[2] ? decode[2] : "",
+ decode[3] ? decode[3] : "");
+ }
+ }
+
+ if (decode[1])
+ free(decode[1]);
+ if (decode[2])
+ free(decode[2]);
+ if (decode[3])
+ free(decode[3]);
+
+ return decode[0];
+} /* }}} */
+
+void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC) /* {{{ */
+{
+ /* force out a line while stepping so the user knows what is happening */
+ if (ignore_flags ||
+ (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) ||
+ (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ||
+ (PHPDBG_G(oplog)))) {
+
+ zend_op *opline = execute_data->opline;
+ char *decode = phpdbg_decode_opline(execute_data->op_array, opline, vars TSRMLS_CC);
+
+ if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) {
+ /* output line info */
+ phpdbg_notice("L%-5u %16p %-30s %s %s",
+ opline->lineno,
+ opline,
+ phpdbg_decode_opcode(opline->opcode),
+ decode,
+ execute_data->op_array->filename ? execute_data->op_array->filename : "unknown");
+ }
+
+ if (!ignore_flags && PHPDBG_G(oplog)) {
+ phpdbg_log_ex(PHPDBG_G(oplog), "L%-5u %16p %-30s %s %s",
+ opline->lineno,
+ opline,
+ phpdbg_decode_opcode(opline->opcode),
+ decode,
+ execute_data->op_array->filename ? execute_data->op_array->filename : "unknown");
+ }
+
+ if (decode) {
+ free(decode);
+ }
+ }
+} /* }}} */
+
+void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC) /* {{{ */
+{
+ phpdbg_print_opline_ex(execute_data, NULL, ignore_flags TSRMLS_CC);
+} /* }}} */
+
+const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
+{
+#define CASE(s) case s: return #s
+ switch (opcode) {
+ CASE(ZEND_NOP);
+ CASE(ZEND_ADD);
+ CASE(ZEND_SUB);
+ CASE(ZEND_MUL);
+ CASE(ZEND_DIV);
+ CASE(ZEND_MOD);
+ CASE(ZEND_SL);
+ CASE(ZEND_SR);
+ CASE(ZEND_CONCAT);
+ CASE(ZEND_BW_OR);
+ CASE(ZEND_BW_AND);
+ CASE(ZEND_BW_XOR);
+ CASE(ZEND_BW_NOT);
+ CASE(ZEND_BOOL_NOT);
+ CASE(ZEND_BOOL_XOR);
+ CASE(ZEND_IS_IDENTICAL);
+ CASE(ZEND_IS_NOT_IDENTICAL);
+ CASE(ZEND_IS_EQUAL);
+ CASE(ZEND_IS_NOT_EQUAL);
+ CASE(ZEND_IS_SMALLER);
+ CASE(ZEND_IS_SMALLER_OR_EQUAL);
+ CASE(ZEND_CAST);
+ CASE(ZEND_QM_ASSIGN);
+ CASE(ZEND_ASSIGN_ADD);
+ CASE(ZEND_ASSIGN_SUB);
+ CASE(ZEND_ASSIGN_MUL);
+ CASE(ZEND_ASSIGN_DIV);
+ CASE(ZEND_ASSIGN_MOD);
+ CASE(ZEND_ASSIGN_SL);
+ CASE(ZEND_ASSIGN_SR);
+ CASE(ZEND_ASSIGN_CONCAT);
+ CASE(ZEND_ASSIGN_BW_OR);
+ CASE(ZEND_ASSIGN_BW_AND);
+ CASE(ZEND_ASSIGN_BW_XOR);
+ CASE(ZEND_PRE_INC);
+ CASE(ZEND_PRE_DEC);
+ CASE(ZEND_POST_INC);
+ CASE(ZEND_POST_DEC);
+ CASE(ZEND_ASSIGN);
+ CASE(ZEND_ASSIGN_REF);
+ CASE(ZEND_ECHO);
+ CASE(ZEND_PRINT);
+ CASE(ZEND_JMP);
+ CASE(ZEND_JMPZ);
+ CASE(ZEND_JMPNZ);
+ CASE(ZEND_JMPZNZ);
+ CASE(ZEND_JMPZ_EX);
+ CASE(ZEND_JMPNZ_EX);
+ CASE(ZEND_CASE);
+ CASE(ZEND_SWITCH_FREE);
+ CASE(ZEND_BRK);
+ CASE(ZEND_CONT);
+ CASE(ZEND_BOOL);
+ CASE(ZEND_INIT_STRING);
+ CASE(ZEND_ADD_CHAR);
+ CASE(ZEND_ADD_STRING);
+ CASE(ZEND_ADD_VAR);
+ CASE(ZEND_BEGIN_SILENCE);
+ CASE(ZEND_END_SILENCE);
+ CASE(ZEND_INIT_FCALL_BY_NAME);
+ CASE(ZEND_DO_FCALL);
+ CASE(ZEND_DO_FCALL_BY_NAME);
+ CASE(ZEND_RETURN);
+ CASE(ZEND_RECV);
+ CASE(ZEND_RECV_INIT);
+ CASE(ZEND_SEND_VAL);
+ CASE(ZEND_SEND_VAR);
+ CASE(ZEND_SEND_REF);
+ CASE(ZEND_NEW);
+ CASE(ZEND_INIT_NS_FCALL_BY_NAME);
+ CASE(ZEND_FREE);
+ CASE(ZEND_INIT_ARRAY);
+ CASE(ZEND_ADD_ARRAY_ELEMENT);
+ CASE(ZEND_INCLUDE_OR_EVAL);
+ CASE(ZEND_UNSET_VAR);
+ CASE(ZEND_UNSET_DIM);
+ CASE(ZEND_UNSET_OBJ);
+ CASE(ZEND_FE_RESET);
+ CASE(ZEND_FE_FETCH);
+ CASE(ZEND_EXIT);
+ CASE(ZEND_FETCH_R);
+ CASE(ZEND_FETCH_DIM_R);
+ CASE(ZEND_FETCH_OBJ_R);
+ CASE(ZEND_FETCH_W);
+ CASE(ZEND_FETCH_DIM_W);
+ CASE(ZEND_FETCH_OBJ_W);
+ CASE(ZEND_FETCH_RW);
+ CASE(ZEND_FETCH_DIM_RW);
+ CASE(ZEND_FETCH_OBJ_RW);
+ CASE(ZEND_FETCH_IS);
+ CASE(ZEND_FETCH_DIM_IS);
+ CASE(ZEND_FETCH_OBJ_IS);
+ CASE(ZEND_FETCH_FUNC_ARG);
+ CASE(ZEND_FETCH_DIM_FUNC_ARG);
+ CASE(ZEND_FETCH_OBJ_FUNC_ARG);
+ CASE(ZEND_FETCH_UNSET);
+ CASE(ZEND_FETCH_DIM_UNSET);
+ CASE(ZEND_FETCH_OBJ_UNSET);
+ CASE(ZEND_FETCH_DIM_TMP_VAR);
+ CASE(ZEND_FETCH_CONSTANT);
+ CASE(ZEND_GOTO);
+ CASE(ZEND_EXT_STMT);
+ CASE(ZEND_EXT_FCALL_BEGIN);
+ CASE(ZEND_EXT_FCALL_END);
+ CASE(ZEND_EXT_NOP);
+ CASE(ZEND_TICKS);
+ CASE(ZEND_SEND_VAR_NO_REF);
+ CASE(ZEND_CATCH);
+ CASE(ZEND_THROW);
+ CASE(ZEND_FETCH_CLASS);
+ CASE(ZEND_CLONE);
+ CASE(ZEND_RETURN_BY_REF);
+ CASE(ZEND_INIT_METHOD_CALL);
+ CASE(ZEND_INIT_STATIC_METHOD_CALL);
+ CASE(ZEND_ISSET_ISEMPTY_VAR);
+ CASE(ZEND_ISSET_ISEMPTY_DIM_OBJ);
+ CASE(ZEND_PRE_INC_OBJ);
+ CASE(ZEND_PRE_DEC_OBJ);
+ CASE(ZEND_POST_INC_OBJ);
+ CASE(ZEND_POST_DEC_OBJ);
+ CASE(ZEND_ASSIGN_OBJ);
+ CASE(ZEND_INSTANCEOF);
+ CASE(ZEND_DECLARE_CLASS);
+ CASE(ZEND_DECLARE_INHERITED_CLASS);
+ CASE(ZEND_DECLARE_FUNCTION);
+ CASE(ZEND_RAISE_ABSTRACT_ERROR);
+ CASE(ZEND_DECLARE_CONST);
+ CASE(ZEND_ADD_INTERFACE);
+ CASE(ZEND_DECLARE_INHERITED_CLASS_DELAYED);
+ CASE(ZEND_VERIFY_ABSTRACT_CLASS);
+ CASE(ZEND_ASSIGN_DIM);
+ CASE(ZEND_ISSET_ISEMPTY_PROP_OBJ);
+ CASE(ZEND_HANDLE_EXCEPTION);
+ CASE(ZEND_USER_OPCODE);
+#ifdef ZEND_JMP_SET
+ CASE(ZEND_JMP_SET);
+#endif
+ CASE(ZEND_DECLARE_LAMBDA_FUNCTION);
+#ifdef ZEND_ADD_TRAIT
+ CASE(ZEND_ADD_TRAIT);
+#endif
+#ifdef ZEND_BIND_TRAITS
+ CASE(ZEND_BIND_TRAITS);
+#endif
+#ifdef ZEND_SEPARATE
+ CASE(ZEND_SEPARATE);
+#endif
+#ifdef ZEND_QM_ASSIGN_VAR
+ CASE(ZEND_QM_ASSIGN_VAR);
+#endif
+#ifdef ZEND_JMP_SET_VAR
+ CASE(ZEND_JMP_SET_VAR);
+#endif
+#ifdef ZEND_DISCARD_EXCEPTION
+ CASE(ZEND_DISCARD_EXCEPTION);
+#endif
+#ifdef ZEND_YIELD
+ CASE(ZEND_YIELD);
+#endif
+#ifdef ZEND_GENERATOR_RETURN
+ CASE(ZEND_GENERATOR_RETURN);
+#endif
+#ifdef ZEND_FAST_CALL
+ CASE(ZEND_FAST_CALL);
+#endif
+#ifdef ZEND_FAST_RET
+ CASE(ZEND_FAST_RET);
+#endif
+#ifdef ZEND_RECV_VARIADIC
+ CASE(ZEND_RECV_VARIADIC);
+#endif
+ CASE(ZEND_OP_DATA);
+ default:
+ return "UNKNOWN";
+ }
+} /* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_OPCODE_H
+#define PHPDBG_OPCODE_H
+
+#include "zend_types.h"
+
+const char *phpdbg_decode_opcode(zend_uchar);
+char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC);
+void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC);
+void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC);
+
+#endif /* PHPDBG_OPCODE_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_print.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_opcode.h"
+#include "phpdbg_prompt.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+PHPDBG_PRINT(opline) /* {{{ */
+{
+ if (EG(in_execution) && EG(current_execute_data)) {
+ phpdbg_print_opline(EG(current_execute_data), 1 TSRMLS_CC);
+ } else {
+ phpdbg_error("Not Executing!");
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+static inline void phpdbg_print_function_helper(zend_function *method TSRMLS_DC) /* {{{ */
+{
+ switch (method->type) {
+ case ZEND_USER_FUNCTION: {
+ zend_op_array* op_array = &(method->op_array);
+ HashTable vars;
+
+ if (op_array) {
+ zend_op *opline = &(op_array->opcodes[0]);
+ zend_uint opcode = 0,
+ end = op_array->last-1;
+
+ if (method->common.scope) {
+ phpdbg_writeln("\tL%d-%d %s::%s() %s",
+ op_array->line_start, op_array->line_end,
+ method->common.scope->name,
+ method->common.function_name,
+ op_array->filename ? op_array->filename : "unknown");
+ } else {
+ phpdbg_writeln("\tL%d-%d %s() %s",
+ method->common.function_name ? op_array->line_start : 0,
+ method->common.function_name ? op_array->line_end : 0,
+ method->common.function_name ? method->common.function_name : "{main}",
+ op_array->filename ? op_array->filename : "unknown");
+ }
+
+ zend_hash_init(&vars, op_array->last, NULL, NULL, 0);
+ do {
+ char *decode = phpdbg_decode_opline(op_array, opline, &vars TSRMLS_CC);
+ if (decode != NULL) {
+ phpdbg_writeln("\t\tL%u\t%p %-30s %s",
+ opline->lineno,
+ opline,
+ phpdbg_decode_opcode(opline->opcode),
+ decode);
+ free(decode);
+ } else {
+ phpdbg_error("\tFailed to decode opline %16p", opline);
+ }
+ opline++;
+ } while (++opcode < end);
+ zend_hash_destroy(&vars);
+ }
+ } break;
+
+ default: {
+ if (method->common.scope) {
+ phpdbg_writeln("\tInternal %s::%s()", method->common.scope->name, method->common.function_name);
+ } else {
+ phpdbg_writeln("\tInternal %s()", method->common.function_name);
+ }
+ }
+ }
+} /* }}} */
+
+PHPDBG_PRINT(exec) /* {{{ */
+{
+ if (PHPDBG_G(exec)) {
+ if (!PHPDBG_G(ops)) {
+ phpdbg_compile(TSRMLS_C);
+ }
+
+ if (PHPDBG_G(ops)) {
+ phpdbg_notice("Context %s", PHPDBG_G(exec));
+
+ phpdbg_print_function_helper((zend_function*) PHPDBG_G(ops) TSRMLS_CC);
+ }
+ } else {
+ phpdbg_error("No execution context set");
+ }
+
+return SUCCESS;
+} /* }}} */
+
+PHPDBG_PRINT(stack) /* {{{ */
+{
+ zend_op_array *ops = EG(active_op_array);
+
+ if (EG(in_execution) && ops) {
+ if (ops->function_name) {
+ if (ops->scope) {
+ phpdbg_notice("Stack in %s::%s()", ops->scope->name, ops->function_name);
+ } else {
+ phpdbg_notice("Stack in %s()", ops->function_name);
+ }
+ } else {
+ if (ops->filename) {
+ phpdbg_notice("Stack in %s", ops->filename);
+ } else {
+ phpdbg_notice("Stack @ %p", ops);
+ }
+ }
+ phpdbg_print_function_helper((zend_function*) ops TSRMLS_CC);
+ } else {
+ phpdbg_error("Not Executing!");
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_PRINT(class) /* {{{ */
+{
+ zend_class_entry **ce;
+
+ switch (param->type) {
+ case STR_PARAM: {
+ if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
+ phpdbg_notice("%s %s: %s",
+ ((*ce)->type == ZEND_USER_CLASS) ?
+ "User" : "Internal",
+ ((*ce)->ce_flags & ZEND_ACC_INTERFACE) ?
+ "Interface" :
+ ((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ?
+ "Abstract Class" :
+ "Class",
+ (*ce)->name);
+
+ phpdbg_writeln("Methods (%d):", zend_hash_num_elements(&(*ce)->function_table));
+ if (zend_hash_num_elements(&(*ce)->function_table)) {
+ HashPosition position;
+ zend_function *method;
+
+ for (zend_hash_internal_pointer_reset_ex(&(*ce)->function_table, &position);
+ zend_hash_get_current_data_ex(&(*ce)->function_table, (void**) &method, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&(*ce)->function_table, &position)) {
+ phpdbg_print_function_helper(method TSRMLS_CC);
+ }
+ }
+ } else {
+ phpdbg_error("The class %s could not be found", param->str);
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_PRINT(method) /* {{{ */
+{
+ switch (param->type) {
+ case METHOD_PARAM: {
+ zend_class_entry **ce;
+
+ if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
+ zend_function *fbc;
+ char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
+
+ if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
+ phpdbg_notice("%s Method %s",
+ (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
+ fbc->common.function_name);
+
+ phpdbg_print_function_helper(fbc TSRMLS_CC);
+ } else {
+ phpdbg_error("The method %s could not be found", param->method.name);
+ }
+
+ efree(lcname);
+ } else {
+ phpdbg_error("The class %s could not be found", param->method.class);
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_PRINT(func) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM: {
+ HashTable *func_table = EG(function_table);
+ zend_function* fbc;
+ const char *func_name = param->str;
+ size_t func_name_len = param->len;
+ char *lcname;
+ /* search active scope if begins with period */
+ if (func_name[0] == '.') {
+ if (EG(scope)) {
+ func_name++;
+ func_name_len--;
+
+ func_table = &EG(scope)->function_table;
+ } else {
+ phpdbg_error("No active class");
+ return SUCCESS;
+ }
+ } else if (!EG(function_table)) {
+ phpdbg_error("No function table loaded");
+ return SUCCESS;
+ } else {
+ func_table = EG(function_table);
+ }
+
+ lcname = zend_str_tolower_dup(func_name, func_name_len);
+
+ if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
+ phpdbg_notice("%s %s %s",
+ (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
+ (fbc->common.scope) ? "Method" : "Function",
+ fbc->common.function_name);
+
+ phpdbg_print_function_helper(fbc TSRMLS_CC);
+ } else {
+ phpdbg_error("The function %s could not be found", func_name);
+ }
+
+ efree(lcname);
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_PRINT_H
+#define PHPDBG_PRINT_H
+
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_PRINT(name) PHPDBG_COMMAND(print_##name)
+
+/**
+ * Printer Forward Declarations
+ */
+PHPDBG_PRINT(exec);
+PHPDBG_PRINT(opline);
+PHPDBG_PRINT(class);
+PHPDBG_PRINT(method);
+PHPDBG_PRINT(func);
+PHPDBG_PRINT(stack);
+
+/**
+ * Commands
+ */
+static const phpdbg_command_t phpdbg_print_commands[] = {
+ PHPDBG_COMMAND_D_EX(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0),
+ PHPDBG_COMMAND_D_EX(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0),
+ PHPDBG_COMMAND_D_EX(class, "print out the instructions in the specified class", 'c', print_class, NULL, 1),
+ PHPDBG_COMMAND_D_EX(method, "print out the instructions in the specified method", 'm', print_method, NULL, 1),
+ PHPDBG_COMMAND_D_EX(func, "print out the instructions in the specified function", 'f', print_func, NULL, 1),
+ PHPDBG_COMMAND_D_EX(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0),
+ PHPDBG_END_COMMAND
+};
+
+#endif /* PHPDBG_PRINT_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "zend.h"
+#include "zend_compile.h"
+#include "phpdbg.h"
+#include "phpdbg_help.h"
+#include "phpdbg_print.h"
+#include "phpdbg_info.h"
+#include "phpdbg_break.h"
+#include "phpdbg_bp.h"
+#include "phpdbg_opcode.h"
+#include "phpdbg_list.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_prompt.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_set.h"
+#include "phpdbg_frame.h"
+
+/* {{{ command declarations */
+const phpdbg_command_t phpdbg_prompt_commands[] = {
+ PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, 1),
+ PHPDBG_COMMAND_D(compile, "attempt compilation", 'c', NULL, 0),
+ PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 1),
+ PHPDBG_COMMAND_D(next, "continue execution", 'n', NULL, 0),
+ PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, 0),
+ PHPDBG_COMMAND_D(eval, "evaluate some code", 'E', NULL, 1),
+ PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0),
+ PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0),
+ PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0),
+ PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 2),
+ PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, 1),
+ PHPDBG_COMMAND_D(back, "show trace", 't', NULL, 0),
+ PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, 1),
+ PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, 2),
+ PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, 1),
+ PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0),
+ PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0),
+ PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, 2),
+ PHPDBG_COMMAND_D(quiet, "silence some output", 'Q', NULL, 1),
+ PHPDBG_COMMAND_D(aliases, "show alias list", 'a', NULL, 0),
+ PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, 1),
+ PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, 1),
+ PHPDBG_COMMAND_D(source, "execute a phpdbginit", '.', NULL, 1),
+ PHPDBG_COMMAND_D(shell, "shell a command", '-', NULL, 1),
+ PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0),
+ PHPDBG_END_COMMAND
+}; /* }}} */
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+static inline int phpdbg_call_register(phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+{
+ phpdbg_input_t *function = input->argv[0];
+
+ if (zend_hash_exists(
+ &PHPDBG_G(registered), function->string, function->length+1)) {
+
+ zval fname, *fretval;
+ zend_fcall_info fci;
+
+ ZVAL_STRINGL(&fname, function->string, function->length, 1);
+
+ memset(&fci, 0, sizeof(zend_fcall_info));
+
+ fci.size = sizeof(zend_fcall_info);
+ fci.function_table = &PHPDBG_G(registered);
+ fci.function_name = &fname;
+ fci.symbol_table = EG(active_symbol_table);
+ fci.object_ptr = NULL;
+ fci.retval_ptr_ptr = &fretval;
+ fci.no_separation = 1;
+
+ if (input->argc > 1) {
+ int param;
+ zval params;
+
+ array_init(¶ms);
+
+ for (param = 0; param < (input->argc-1); param++) {
+ add_next_index_stringl(
+ ¶ms,
+ input->argv[param+1]->string,
+ input->argv[param+1]->length, 1);
+
+ phpdbg_debug(
+ "created param[%d] from argv[%d]: %s",
+ param, param+1, input->argv[param+1]->string);
+ }
+
+ zend_fcall_info_args(&fci, ¶ms TSRMLS_CC);
+ } else {
+ fci.params = NULL;
+ fci.param_count = 0;
+ }
+
+ phpdbg_debug(
+ "created %d params from %d arguments",
+ fci.param_count, input->argc);
+
+ zend_call_function(&fci, NULL TSRMLS_CC);
+
+ if (fretval) {
+ zend_print_zval_r(
+ fretval, 0 TSRMLS_CC);
+ phpdbg_writeln(EMPTY);
+ }
+
+ zval_dtor(&fname);
+
+ return SUCCESS;
+ }
+
+ return FAILURE;
+} /* }}} */
+
+void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init TSRMLS_DC) /* {{{ */
+{
+ struct stat sb;
+
+ if (init_file && VCWD_STAT(init_file, &sb) != -1) {
+ FILE *fp = fopen(init_file, "r");
+ if (fp) {
+ int line = 1;
+
+ char cmd[PHPDBG_MAX_CMD];
+ size_t cmd_len = 0L;
+ char *code = NULL;
+ size_t code_len = 0L;
+ zend_bool in_code = 0;
+
+ while (fgets(cmd, PHPDBG_MAX_CMD, fp) != NULL) {
+ cmd_len = strlen(cmd)-1;
+
+ while (cmd_len > 0L && isspace(cmd[cmd_len-1]))
+ cmd_len--;
+
+ cmd[cmd_len] = '\0';
+
+ if (*cmd && cmd_len > 0L && cmd[0] != '#') {
+ if (cmd_len == 2) {
+ if (memcmp(cmd, "<:", sizeof("<:")-1) == SUCCESS) {
+ in_code = 1;
+ goto next_line;
+ } else {
+ if (memcmp(cmd, ":>", sizeof(":>")-1) == SUCCESS) {
+ in_code = 0;
+ code[code_len] = '\0';
+ {
+ zend_eval_stringl(
+ code, code_len, NULL, "phpdbginit code" TSRMLS_CC);
+ }
+ free(code);
+ code = NULL;
+ goto next_line;
+ }
+ }
+ }
+
+ if (in_code) {
+ if (code == NULL) {
+ code = malloc(cmd_len + 1);
+ } else code = realloc(code, code_len + cmd_len + 1);
+
+ if (code) {
+ memcpy(
+ &code[code_len], cmd, cmd_len);
+ code_len += cmd_len;
+ }
+ goto next_line;
+ }
+
+ {
+ phpdbg_input_t *input = phpdbg_read_input(cmd TSRMLS_CC);
+ switch (phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) {
+ case FAILURE:
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) {
+ phpdbg_error("Unrecognized command in %s:%d: %s!", init_file, line, input->string);
+ }
+ }
+ break;
+ }
+ phpdbg_destroy_input(&input TSRMLS_CC);
+ }
+
+ }
+next_line:
+ line++;
+ }
+
+ if (code) {
+ free(code);
+ }
+
+ fclose(fp);
+ } else {
+ phpdbg_error(
+ "Failed to open %s for initialization", init_file);
+ }
+
+ if (free_init) {
+ free(init_file);
+ }
+ }
+} /* }}} */
+
+void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TSRMLS_DC) /* {{{ */
+{
+ if (!init_file && use_default) {
+ char *scan_dir = getenv("PHP_INI_SCAN_DIR");
+ int i;
+
+ phpdbg_try_file_init(PHPDBG_STRL(PHP_CONFIG_FILE_PATH "/" PHPDBG_INIT_FILENAME), 0 TSRMLS_CC);
+
+ if (!scan_dir) {
+ scan_dir = PHP_CONFIG_FILE_SCAN_DIR;
+ }
+ while (*scan_dir != 0) {
+ i = 0;
+ while (scan_dir[i] != ':') {
+ if (scan_dir[i++] == 0) {
+ i = -1;
+ break;
+ }
+ }
+ if (i != -1) {
+ scan_dir[i] = 0;
+ }
+
+ asprintf(
+ &init_file, "%s/%s", scan_dir, PHPDBG_INIT_FILENAME);
+ phpdbg_try_file_init(init_file, strlen(init_file), 1 TSRMLS_CC);
+ if (i == -1) {
+ break;
+ }
+ scan_dir += i + 1;
+ }
+
+ phpdbg_try_file_init(PHPDBG_STRL(PHPDBG_INIT_FILENAME), 0 TSRMLS_CC);
+ } else {
+ phpdbg_try_file_init(init_file, init_file_len, 1 TSRMLS_CC);
+ }
+}
+
+PHPDBG_COMMAND(exec) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM: {
+ struct stat sb;
+
+ if (VCWD_STAT(param->str, &sb) != FAILURE) {
+ if (sb.st_mode & (S_IFREG|S_IFLNK)) {
+ char *res = phpdbg_resolve_path(param->str TSRMLS_CC);
+ size_t res_len = strlen(res);
+
+ if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) {
+
+ if (PHPDBG_G(exec)) {
+ phpdbg_notice("Unsetting old execution context: %s", PHPDBG_G(exec));
+ efree(PHPDBG_G(exec));
+ PHPDBG_G(exec) = NULL;
+ PHPDBG_G(exec_len) = 0L;
+ }
+
+ if (PHPDBG_G(ops)) {
+ phpdbg_notice("Destroying compiled opcodes");
+ phpdbg_clean(0 TSRMLS_CC);
+ }
+
+ PHPDBG_G(exec) = res;
+ PHPDBG_G(exec_len) = res_len;
+
+ phpdbg_notice("Set execution context: %s", PHPDBG_G(exec));
+ } else {
+ phpdbg_notice("Execution context not changed");
+ }
+ } else {
+ phpdbg_error("Cannot use %s as execution context, not a valid file or symlink", param->str);
+ }
+ } else {
+ phpdbg_error("Cannot stat %s, ensure the file exists", param->str);
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+int phpdbg_compile(TSRMLS_D) /* {{{ */
+{
+ zend_file_handle fh;
+
+ if (EG(in_execution)) {
+ phpdbg_error("Cannot compile while in execution");
+ return FAILURE;
+ }
+
+ phpdbg_notice("Attempting compilation of %s", PHPDBG_G(exec));
+
+ if (php_stream_open_for_zend_ex(PHPDBG_G(exec), &fh,
+ USE_PATH|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC) == SUCCESS) {
+
+ PHPDBG_G(ops) = zend_compile_file(&fh, ZEND_INCLUDE TSRMLS_CC);
+ zend_destroy_file_handle(&fh TSRMLS_CC);
+
+ phpdbg_notice("Success");
+ return SUCCESS;
+ } else {
+ phpdbg_error("Could not open file %s", PHPDBG_G(exec));
+ }
+
+ return FAILURE;
+} /* }}} */
+
+PHPDBG_COMMAND(compile) /* {{{ */
+{
+ if (!PHPDBG_G(exec)) {
+ phpdbg_error("No execution context");
+ return SUCCESS;
+ }
+
+ if (!EG(in_execution)) {
+ if (PHPDBG_G(ops)) {
+ phpdbg_error("Destroying previously compiled opcodes");
+ phpdbg_clean(0 TSRMLS_CC);
+ }
+ }
+
+ phpdbg_compile(TSRMLS_C);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(step) /* {{{ */
+{
+ switch (param->type) {
+ case EMPTY_PARAM:
+ case NUMERIC_PARAM: {
+ if (param->type == NUMERIC_PARAM && param->num) {
+ PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
+ } else {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING;
+ }
+
+ phpdbg_notice("Stepping %s",
+ (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off");
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(next) /* {{{ */
+{
+ return PHPDBG_NEXT;
+} /* }}} */
+
+PHPDBG_COMMAND(until) /* {{{ */
+{
+ if (!EG(in_execution)) {
+ phpdbg_error("Not executing");
+ return SUCCESS;
+ }
+
+ PHPDBG_G(flags) |= PHPDBG_IN_UNTIL;
+ {
+ zend_uint next = 0,
+ self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes);
+ zend_op *opline = &EG(active_op_array)->opcodes[self];
+
+ for (next = self; next < EG(active_op_array)->last; next++) {
+ if (EG(active_op_array)->opcodes[next].lineno != opline->lineno) {
+ zend_hash_index_update(
+ &PHPDBG_G(seek),
+ (zend_ulong) &EG(active_op_array)->opcodes[next],
+ &EG(active_op_array)->opcodes[next],
+ sizeof(zend_op), NULL);
+ break;
+ }
+ }
+ }
+
+ return PHPDBG_UNTIL;
+} /* }}} */
+
+PHPDBG_COMMAND(finish) /* {{{ */
+{
+ if (!EG(in_execution)) {
+ phpdbg_error("Not executing");
+ return SUCCESS;
+ }
+
+ PHPDBG_G(flags) |= PHPDBG_IN_FINISH;
+ {
+ zend_uint next = 0,
+ self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes);
+
+ for (next = self; next < EG(active_op_array)->last; next++) {
+ switch (EG(active_op_array)->opcodes[next].opcode) {
+ case ZEND_RETURN:
+ case ZEND_THROW:
+ case ZEND_EXIT:
+#ifdef ZEND_YIELD
+ case ZEND_YIELD:
+#endif
+ zend_hash_index_update(
+ &PHPDBG_G(seek),
+ (zend_ulong) &EG(active_op_array)->opcodes[next],
+ &EG(active_op_array)->opcodes[next],
+ sizeof(zend_op), NULL);
+ break;
+ }
+ }
+ }
+
+ return PHPDBG_FINISH;
+} /* }}} */
+
+PHPDBG_COMMAND(leave) /* {{{ */
+{
+ if (!EG(in_execution)) {
+ phpdbg_error("Not executing");
+ return SUCCESS;
+ }
+
+ PHPDBG_G(flags) |= PHPDBG_IN_LEAVE;
+ {
+ zend_uint next = 0,
+ self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes);
+
+ for (next = self; next < EG(active_op_array)->last; next++) {
+ switch (EG(active_op_array)->opcodes[next].opcode) {
+ case ZEND_RETURN:
+ case ZEND_THROW:
+ case ZEND_EXIT:
+#ifdef ZEND_YIELD
+ case ZEND_YIELD:
+#endif
+ zend_hash_index_update(
+ &PHPDBG_G(seek),
+ (zend_ulong) &EG(active_op_array)->opcodes[next],
+ &EG(active_op_array)->opcodes[next],
+ sizeof(zend_op), NULL);
+ break;
+ }
+ }
+ }
+
+ return PHPDBG_LEAVE;
+} /* }}} */
+
+PHPDBG_COMMAND(frame) /* {{{ */
+{
+ switch (param->type) {
+ case NUMERIC_PARAM:
+ phpdbg_switch_frame(param->num TSRMLS_CC);
+ break;
+
+ case EMPTY_PARAM:
+ phpdbg_notice("Currently in frame #%d", PHPDBG_G(frame).num);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+static inline void phpdbg_handle_exception(TSRMLS_D) /* }}} */
+{
+ zend_fcall_info fci;
+
+ zval fname,
+ *trace,
+ exception;
+
+ /* get filename and linenumber before unsetting exception */
+ const char *filename = zend_get_executed_filename(TSRMLS_C);
+ zend_uint lineno = zend_get_executed_lineno(TSRMLS_C);
+
+ /* copy exception */
+ exception = *EG(exception);
+ zval_copy_ctor(&exception);
+ EG(exception) = NULL;
+
+ phpdbg_error(
+ "Uncaught %s!",
+ Z_OBJCE(exception)->name);
+
+ /* call __toString */
+ ZVAL_STRINGL(&fname, "__tostring", sizeof("__tostring")-1, 1);
+ fci.size = sizeof(fci);
+ fci.function_table = &Z_OBJCE(exception)->function_table;
+ fci.function_name = &fname;
+ fci.symbol_table = NULL;
+ fci.object_ptr = &exception;
+ fci.retval_ptr_ptr = &trace;
+ fci.param_count = 0;
+ fci.params = NULL;
+ fci.no_separation = 1;
+ zend_call_function(&fci, NULL TSRMLS_CC);
+
+ if (trace) {
+ phpdbg_writeln(
+ "Uncaught %s", Z_STRVAL_P(trace));
+ /* remember to dtor trace */
+ zval_ptr_dtor(&trace);
+ }
+
+ /* output useful information about address */
+ phpdbg_writeln(
+ "Stacked entered at %p in %s on line %u",
+ EG(active_op_array)->opcodes, filename, lineno);
+
+ zval_dtor(&fname);
+ zval_dtor(&exception);
+} /* }}} */
+
+PHPDBG_COMMAND(run) /* {{{ */
+{
+ if (EG(in_execution)) {
+ phpdbg_error("Cannot start another execution while one is in progress");
+ return SUCCESS;
+ }
+
+ if (PHPDBG_G(ops) || PHPDBG_G(exec)) {
+ zend_op **orig_opline = EG(opline_ptr);
+ zend_op_array *orig_op_array = EG(active_op_array);
+ zval **orig_retval_ptr = EG(return_value_ptr_ptr);
+
+ if (!PHPDBG_G(ops)) {
+ if (phpdbg_compile(TSRMLS_C) == FAILURE) {
+ phpdbg_error("Failed to compile %s, cannot run", PHPDBG_G(exec));
+ goto out;
+ }
+ }
+
+ EG(active_op_array) = PHPDBG_G(ops);
+ EG(return_value_ptr_ptr) = &PHPDBG_G(retval);
+ if (!EG(active_symbol_table)) {
+ zend_rebuild_symbol_table(TSRMLS_C);
+ }
+
+ /* clean seek state */
+ PHPDBG_G(flags) &= ~PHPDBG_SEEK_MASK;
+ zend_hash_clean(
+ &PHPDBG_G(seek));
+
+ /* reset hit counters */
+ phpdbg_reset_breakpoints(TSRMLS_C);
+
+ zend_try {
+ php_output_activate(TSRMLS_C);
+ PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE;
+ zend_execute(EG(active_op_array) TSRMLS_CC);
+ PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE;
+ php_output_deactivate(TSRMLS_C);
+ } zend_catch {
+ EG(active_op_array) = orig_op_array;
+ EG(opline_ptr) = orig_opline;
+ EG(return_value_ptr_ptr) = orig_retval_ptr;
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ phpdbg_error("Caught exit/error from VM");
+ goto out;
+ }
+ } zend_end_try();
+
+ if (EG(exception)) {
+ phpdbg_handle_exception(TSRMLS_C);
+ }
+
+ EG(active_op_array) = orig_op_array;
+ EG(opline_ptr) = orig_opline;
+ EG(return_value_ptr_ptr) = orig_retval_ptr;
+
+ } else {
+ phpdbg_error("Nothing to execute!");
+ }
+
+out:
+ PHPDBG_FRAME(num) = 0;
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(eval) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM: {
+ zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)==PHPDBG_IS_STEPPING);
+ zval retval;
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
+ PHPDBG_G(flags) &= ~ PHPDBG_IS_STEPPING;
+ }
+
+ /* disable stepping while eval() in progress */
+ PHPDBG_G(flags) |= PHPDBG_IN_EVAL;
+ zend_try {
+ if (zend_eval_stringl(param->str, param->len,
+ &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) {
+ zend_print_zval_r(
+ &retval, 0 TSRMLS_CC);
+ phpdbg_writeln(EMPTY);
+ zval_dtor(&retval);
+ }
+ } zend_end_try();
+ PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL;
+
+ /* switch stepping back on */
+ if (stepping &&
+ !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
+ }
+
+ CG(unclean_shutdown) = 0;
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(back) /* {{{ */
+{
+ if (!EG(in_execution)) {
+ phpdbg_error("Not executing!");
+ return SUCCESS;
+ }
+
+ switch (param->type) {
+ case EMPTY_PARAM:
+ case NUMERIC_PARAM:
+ phpdbg_dump_backtrace(
+ (param->type == NUMERIC_PARAM) ? param->num : 0 TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(print) /* {{{ */
+{
+ switch (param->type) {
+ case EMPTY_PARAM: {
+ phpdbg_writeln(SEPARATE);
+ phpdbg_notice("Execution Context Information");
+#ifdef HAVE_LIBREADLINE
+ phpdbg_writeln("Readline\tyes");
+#else
+ phpdbg_writeln("Readline\tno");
+#endif
+
+ phpdbg_writeln("Exec\t\t%s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none");
+ phpdbg_writeln("Compiled\t%s", PHPDBG_G(ops) ? "yes" : "no");
+ phpdbg_writeln("Stepping\t%s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off");
+ phpdbg_writeln("Quietness\t%s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off");
+ phpdbg_writeln("Oplog\t\t%s", PHPDBG_G(oplog) ? "on" : "off");
+
+ if (PHPDBG_G(ops)) {
+ phpdbg_writeln("Opcodes\t\t%d", PHPDBG_G(ops)->last);
+
+ if (PHPDBG_G(ops)->last_var) {
+ phpdbg_writeln("Variables\t%d", PHPDBG_G(ops)->last_var-1);
+ } else {
+ phpdbg_writeln("Variables\tNone");
+ }
+ }
+
+ phpdbg_writeln("Executing\t%s", EG(in_execution) ? "yes" : "no");
+ if (EG(in_execution)) {
+ phpdbg_writeln("VM Return\t%d", PHPDBG_G(vmret));
+ }
+
+ phpdbg_writeln("Classes\t\t%d", zend_hash_num_elements(EG(class_table)));
+ phpdbg_writeln("Functions\t%d", zend_hash_num_elements(EG(function_table)));
+ phpdbg_writeln("Constants\t%d", zend_hash_num_elements(EG(zend_constants)));
+ phpdbg_writeln("Included\t%d", zend_hash_num_elements(&EG(included_files)));
+
+ phpdbg_writeln(SEPARATE);
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(info) /* {{{ */
+{
+ phpdbg_error(
+ "No information command selected!");
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(set) /* {{{ */
+{
+ phpdbg_error(
+ "No information command selected!");
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(break) /* {{{ */
+{
+ switch (param->type) {
+ case EMPTY_PARAM:
+ phpdbg_set_breakpoint_file(
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC);
+ break;
+ case ADDR_PARAM:
+ phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC);
+ break;
+ case NUMERIC_PARAM:
+ if (PHPDBG_G(exec)) {
+ phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC);
+ } else {
+ phpdbg_error("Execution context not set!");
+ }
+ break;
+ case METHOD_PARAM:
+ phpdbg_set_breakpoint_method(param->method.class, param->method.name TSRMLS_CC);
+ break;
+ case NUMERIC_METHOD_PARAM:
+ phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC);
+ break;
+ case NUMERIC_FUNCTION_PARAM:
+ phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC);
+ break;
+ case FILE_PARAM:
+ phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC);
+ break;
+ case STR_PARAM:
+ phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(shell) /* {{{ */
+{
+ /* don't allow this to loop, ever ... */
+ switch (param->type) {
+ case STR_PARAM: {
+ FILE *fd = NULL;
+ if ((fd=VCWD_POPEN((char*)param->str, "w"))) {
+ /* do something perhaps ?? do we want input ?? */
+ fclose(fd);
+ } else {
+ phpdbg_error(
+ "Failed to execute %s", param->str);
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(source) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM: {
+ if (input->argc > 2) {
+ if (phpdbg_argv_is(1, "export")) {
+ FILE *h = VCWD_FOPEN(input->argv[2]->string, "w+");
+ if (h) {
+ phpdbg_export_breakpoints(h TSRMLS_CC);
+ fclose(h);
+ } else phpdbg_error("Failed to open %s", input->argv[1]->string);
+ } else {
+ phpdbg_error(
+ "Incorrect usage of source command, see help");
+ }
+ } else {
+ struct stat sb;
+ if (VCWD_STAT(param->str, &sb) != -1) {
+ phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC);
+ } else phpdbg_error("Cannot stat %s", param->str);
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(register) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM: {
+ zend_function *function;
+ char *lcname = zend_str_tolower_dup(param->str, param->len);
+ size_t lcname_len = strlen(lcname);
+
+ if (!zend_hash_exists(&PHPDBG_G(registered), lcname, lcname_len+1)) {
+ if (zend_hash_find(EG(function_table), lcname, lcname_len+1, (void**) &function) == SUCCESS) {
+ zend_hash_update(
+ &PHPDBG_G(registered), lcname, lcname_len+1, (void*)&function, sizeof(zend_function), NULL);
+ function_add_ref(function);
+
+ phpdbg_notice(
+ "Registered %s", lcname);
+ } else {
+ phpdbg_error("The requested function (%s) could not be found", param->str);
+ }
+ } else {
+ phpdbg_error(
+ "The requested name (%s) is already in use", lcname);
+ }
+
+ efree(lcname);
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(quit) /* {{{ */
+{
+ /* don't allow this to loop, ever ... */
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+
+ phpdbg_destroy_input((phpdbg_input_t**)&input TSRMLS_CC);
+
+ PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
+ zend_bailout();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(clean) /* {{{ */
+{
+ if (EG(in_execution)) {
+ phpdbg_error("Cannot clean environment while executing");
+ return SUCCESS;
+ }
+
+ phpdbg_notice("Cleaning Execution Environment");
+
+ phpdbg_writeln("Classes\t\t\t%d", zend_hash_num_elements(EG(class_table)));
+ phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(EG(function_table)));
+ phpdbg_writeln("Constants\t\t%d", zend_hash_num_elements(EG(zend_constants)));
+ phpdbg_writeln("Includes\t\t%d", zend_hash_num_elements(&EG(included_files)));
+
+ phpdbg_clean(1 TSRMLS_CC);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(clear) /* {{{ */
+{
+ phpdbg_notice("Clearing Breakpoints");
+
+ phpdbg_writeln("File\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]));
+ phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]));
+ phpdbg_writeln("Methods\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]));
+ phpdbg_writeln("Oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]));
+ phpdbg_writeln("File oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]));
+ phpdbg_writeln("Function oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]));
+ phpdbg_writeln("Method oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]));
+ phpdbg_writeln("Conditionals\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]));
+
+ phpdbg_clear_breakpoints(TSRMLS_C);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(aliases) /* {{{ */
+{
+ const phpdbg_command_t *prompt_command = phpdbg_prompt_commands;
+
+ phpdbg_help_header();
+ phpdbg_writeln("Below are the aliased, short versions of all supported commands");
+ while (prompt_command && prompt_command->name) {
+ if (prompt_command->alias) {
+ if (prompt_command->subs) {
+ const phpdbg_command_t *sub_command = prompt_command->subs;
+ phpdbg_writeln(EMPTY);
+ phpdbg_writeln(" %c -> %9s", prompt_command->alias, prompt_command->name);
+ while (sub_command && sub_command->name) {
+ if (sub_command->alias) {
+ phpdbg_writeln(" |-------- %c -> %15s\t%s", sub_command->alias,
+ sub_command->name, sub_command->tip);
+ }
+ ++sub_command;
+ }
+ phpdbg_writeln(EMPTY);
+ } else {
+ phpdbg_writeln(" %c -> %9s\t\t\t%s", prompt_command->alias,
+ prompt_command->name, prompt_command->tip);
+ }
+ }
+
+ ++prompt_command;
+ }
+ phpdbg_help_footer();
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(help) /* {{{ */
+{
+ switch (param->type) {
+ case EMPTY_PARAM: {
+ const phpdbg_command_t *prompt_command = phpdbg_prompt_commands;
+ const phpdbg_command_t *help_command = phpdbg_help_commands;
+
+ phpdbg_help_header();
+ phpdbg_writeln("To get help regarding a specific command type \"help command\"");
+
+ phpdbg_notice("Commands");
+
+ while (prompt_command && prompt_command->name) {
+ phpdbg_writeln(
+ " %10s\t%s", prompt_command->name, prompt_command->tip);
+ ++prompt_command;
+ }
+
+ phpdbg_notice("Help Commands");
+
+ while (help_command && help_command->name) {
+ phpdbg_writeln(" %10s\t%s", help_command->name, help_command->tip);
+ ++help_command;
+ }
+
+ phpdbg_help_footer();
+ } break;
+
+ default: {
+ phpdbg_error(
+ "No help can be found for the subject \"%s\"", param->str);
+ }
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(quiet) /* {{{ */
+{
+ switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (param->num) {
+ PHPDBG_G(flags) |= PHPDBG_IS_QUIET;
+ } else {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_QUIET;
+ }
+ phpdbg_notice("Quietness %s",
+ (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "enabled" : "disabled");
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(list) /* {{{ */
+{
+ switch (param->type) {
+ case NUMERIC_PARAM:
+ case EMPTY_PARAM:
+ return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS);
+
+ case FILE_PARAM:
+ return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS);
+
+ case STR_PARAM:
+ phpdbg_list_function_byname(param->str, param->len TSRMLS_CC);
+ break;
+
+ case METHOD_PARAM:
+ return PHPDBG_LIST_HANDLER(method)(PHPDBG_COMMAND_ARGS);
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+int phpdbg_interactive(TSRMLS_D) /* {{{ */
+{
+ int ret = SUCCESS;
+ phpdbg_input_t *input;
+
+ PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
+
+ input = phpdbg_read_input(NULL TSRMLS_CC);
+
+ if (input && input->length > 0L) {
+ do {
+ switch (ret = phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) {
+ case FAILURE:
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) {
+ phpdbg_error("Failed to execute %s!", input->string);
+ }
+ }
+ break;
+
+ case PHPDBG_LEAVE:
+ case PHPDBG_FINISH:
+ case PHPDBG_UNTIL:
+ case PHPDBG_NEXT: {
+ if (!EG(in_execution)) {
+ phpdbg_error("Not running");
+ }
+ goto out;
+ }
+ }
+
+ phpdbg_destroy_input(&input TSRMLS_CC);
+ } while ((input = phpdbg_read_input(NULL TSRMLS_CC)) && (input->length > 0L));
+
+ if (input && !input->length)
+ goto last;
+
+ } else {
+last:
+ if (PHPDBG_G(lcmd)) {
+ ret = PHPDBG_G(lcmd)->handler(
+ &PHPDBG_G(lparam), input TSRMLS_CC);
+ goto out;
+ }
+ }
+
+out:
+ phpdbg_destroy_input(&input TSRMLS_CC);
+
+ if (EG(in_execution)) {
+ phpdbg_restore_frame(TSRMLS_C);
+ }
+
+ PHPDBG_G(flags) &= ~PHPDBG_IS_INTERACTIVE;
+
+ return ret;
+} /* }}} */
+
+void phpdbg_clean(zend_bool full TSRMLS_DC) /* {{{ */
+{
+ /* this is implicitly required */
+ if (PHPDBG_G(ops)) {
+ destroy_op_array(PHPDBG_G(ops) TSRMLS_CC);
+ efree(PHPDBG_G(ops));
+ PHPDBG_G(ops) = NULL;
+ }
+
+ if (full) {
+ PHPDBG_G(flags) |= PHPDBG_IS_CLEANING;
+
+ zend_bailout();
+ }
+} /* }}} */
+
+static inline zend_execute_data *phpdbg_create_execute_data(zend_op_array *op_array, zend_bool nested TSRMLS_DC) /* {{{ */
+{
+#if PHP_VERSION_ID >= 50500
+ return zend_create_execute_data_from_op_array(op_array, nested TSRMLS_CC);
+#else
+
+#undef EX
+#define EX(element) execute_data->element
+#undef EX_CV
+#define EX_CV(var) EX(CVs)[var]
+#undef EX_CVs
+#define EX_CVs() EX(CVs)
+#undef EX_T
+#define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset))
+#undef EX_Ts
+#define EX_Ts() EX(Ts)
+
+ zend_execute_data *execute_data = (zend_execute_data *)zend_vm_stack_alloc(
+ ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) +
+ ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) +
+ ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC);
+
+ EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)));
+ memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var);
+ EX(Ts) = (temp_variable *)(((char*)EX(CVs)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)));
+ EX(fbc) = NULL;
+ EX(called_scope) = NULL;
+ EX(object) = NULL;
+ EX(old_error_reporting) = NULL;
+ EX(op_array) = op_array;
+ EX(symbol_table) = EG(active_symbol_table);
+ EX(prev_execute_data) = EG(current_execute_data);
+ EG(current_execute_data) = execute_data;
+ EX(nested) = nested;
+
+ if (!op_array->run_time_cache && op_array->last_cache_slot) {
+ op_array->run_time_cache = ecalloc(op_array->last_cache_slot, sizeof(void*));
+ }
+
+ if (op_array->this_var != -1 && EG(This)) {
+ Z_ADDREF_P(EG(This)); /* For $this pointer */
+ if (!EG(active_symbol_table)) {
+ EX_CV(op_array->this_var) = (zval**)EX_CVs() + (op_array->last_var + op_array->this_var);
+ *EX_CV(op_array->this_var) = EG(This);
+ } else {
+ if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), (void**)&EX_CV(op_array->this_var))==FAILURE) {
+ Z_DELREF_P(EG(This));
+ }
+ }
+ }
+
+ EX(opline) = UNEXPECTED((op_array->fn_flags & ZEND_ACC_INTERACTIVE) != 0) && EG(start_op) ? EG(start_op) : op_array->opcodes;
+ EG(opline_ptr) = &EX(opline);
+
+ EX(function_state).function = (zend_function *) op_array;
+ EX(function_state).arguments = NULL;
+
+ return execute_data;
+#endif
+} /* }}} */
+
+#if PHP_VERSION_ID >= 50500
+void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC) /* {{{ */
+{
+#else
+void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC) /* {{{ */
+{
+ long long flags = 0;
+ zend_ulong address = 0L;
+ zend_execute_data *execute_data;
+ zend_bool nested = 0;
+#endif
+ zend_bool original_in_execution = EG(in_execution);
+ HashTable vars;
+
+#if PHP_VERSION_ID < 50500
+ if (EG(exception)) {
+ return;
+ }
+#endif
+
+ EG(in_execution) = 1;
+
+#if PHP_VERSION_ID >= 50500
+ if (0) {
+zend_vm_enter:
+ execute_data = phpdbg_create_execute_data(EG(active_op_array), 1 TSRMLS_CC);
+ }
+ zend_hash_init(&vars, EG(active_op_array)->last, NULL, NULL, 0);
+#else
+zend_vm_enter:
+ execute_data = phpdbg_create_execute_data(op_array, nested TSRMLS_CC);
+ nested = 1;
+ zend_hash_init(&vars, EG(active_op_array)->last, NULL, NULL, 0);
+#endif
+
+ while (1) {
+
+ if ((PHPDBG_G(flags) & PHPDBG_BP_RESOLVE_MASK)) {
+ /* resolve nth opline breakpoints */
+ phpdbg_resolve_op_array_breaks(EG(active_op_array) TSRMLS_CC);
+ }
+
+#ifdef ZEND_WIN32
+ if (EG(timed_out)) {
+ zend_timeout(0);
+ }
+#endif
+
+#define DO_INTERACTIVE() do { \
+ if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \
+ phpdbg_list_file( \
+ zend_get_executed_filename(TSRMLS_C), \
+ 3, \
+ zend_get_executed_lineno(TSRMLS_C)-1, \
+ zend_get_executed_lineno(TSRMLS_C) \
+ TSRMLS_CC \
+ ); \
+ } \
+ \
+ do { \
+ switch (phpdbg_interactive(TSRMLS_C)) { \
+ case PHPDBG_LEAVE: \
+ case PHPDBG_FINISH: \
+ case PHPDBG_UNTIL: \
+ case PHPDBG_NEXT:{ \
+ goto next; \
+ } \
+ } \
+ } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); \
+} while (0)
+
+ /* allow conditional breakpoints and
+ initialization to access the vm uninterrupted */
+ if ((PHPDBG_G(flags) & PHPDBG_IN_COND_BP) ||
+ (PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) {
+ /* skip possible breakpoints */
+ goto next;
+ }
+
+ /* perform seek operation */
+ if (PHPDBG_G(flags) & PHPDBG_SEEK_MASK) {
+ /* current address */
+ zend_ulong address = (zend_ulong) execute_data->opline;
+
+ /* run to next line */
+ if (PHPDBG_G(flags) & PHPDBG_IN_UNTIL) {
+ if (zend_hash_index_exists(&PHPDBG_G(seek), address)) {
+ PHPDBG_G(flags) &= ~PHPDBG_IN_UNTIL;
+ zend_hash_clean(
+ &PHPDBG_G(seek));
+ } else {
+ /* skip possible breakpoints */
+ goto next;
+ }
+ }
+
+ /* run to finish */
+ if (PHPDBG_G(flags) & PHPDBG_IN_FINISH) {
+ if (zend_hash_index_exists(&PHPDBG_G(seek), address)) {
+ PHPDBG_G(flags) &= ~PHPDBG_IN_FINISH;
+ zend_hash_clean(
+ &PHPDBG_G(seek));
+ }
+ /* skip possible breakpoints */
+ goto next;
+ }
+
+ /* break for leave */
+ if (PHPDBG_G(flags) & PHPDBG_IN_LEAVE) {
+ if (zend_hash_index_exists(&PHPDBG_G(seek), address)) {
+ PHPDBG_G(flags) &= ~PHPDBG_IN_LEAVE;
+ zend_hash_clean(
+ &PHPDBG_G(seek));
+ phpdbg_notice(
+ "Breaking for leave at %s:%u",
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C)
+ );
+ DO_INTERACTIVE();
+ } else {
+ /* skip possible breakpoints */
+ goto next;
+ }
+ }
+ }
+
+ /* not while in conditionals */
+ phpdbg_print_opline_ex(
+ execute_data, &vars, 0 TSRMLS_CC);
+
+ /* search for breakpoints */
+ {
+ phpdbg_breakbase_t *brake;
+
+ if ((PHPDBG_G(flags) & PHPDBG_BP_MASK) &&
+ (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC))) {
+ phpdbg_hit_breakpoint(
+ brake, 1 TSRMLS_CC);
+ DO_INTERACTIVE();
+ }
+ }
+
+ if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) {
+ DO_INTERACTIVE();
+ }
+
+next:
+ if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
+ phpdbg_writeln(EMPTY);
+ phpdbg_notice("Program received signal SIGINT");
+ PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED;
+ DO_INTERACTIVE();
+ }
+
+ PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC);
+
+ if (PHPDBG_G(vmret) > 0) {
+ switch (PHPDBG_G(vmret)) {
+ case 1:
+ EG(in_execution) = original_in_execution;
+ zend_hash_destroy(&vars);
+ return;
+ case 2:
+#if PHP_VERSION_ID < 50500
+ op_array = EG(active_op_array);
+#endif
+ zend_hash_destroy(&vars);
+ goto zend_vm_enter;
+ break;
+ case 3:
+ execute_data = EG(current_execute_data);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen");
+} /* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_PROMPT_H
+#define PHPDBG_PROMPT_H
+
+/* {{{ */
+void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TSRMLS_DC);
+void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init TSRMLS_DC);
+int phpdbg_interactive(TSRMLS_D);
+int phpdbg_compile(TSRMLS_D);
+void phpdbg_clean(zend_bool full TSRMLS_DC); /* }}} */
+
+/* {{{ phpdbg command handlers */
+PHPDBG_COMMAND(exec);
+PHPDBG_COMMAND(compile);
+PHPDBG_COMMAND(step);
+PHPDBG_COMMAND(next);
+PHPDBG_COMMAND(run);
+PHPDBG_COMMAND(eval);
+PHPDBG_COMMAND(until);
+PHPDBG_COMMAND(finish);
+PHPDBG_COMMAND(leave);
+PHPDBG_COMMAND(frame);
+PHPDBG_COMMAND(print);
+PHPDBG_COMMAND(break);
+PHPDBG_COMMAND(back);
+PHPDBG_COMMAND(list);
+PHPDBG_COMMAND(info);
+PHPDBG_COMMAND(clean);
+PHPDBG_COMMAND(clear);
+PHPDBG_COMMAND(help);
+PHPDBG_COMMAND(quiet);
+PHPDBG_COMMAND(aliases);
+PHPDBG_COMMAND(shell);
+PHPDBG_COMMAND(set);
+PHPDBG_COMMAND(source);
+PHPDBG_COMMAND(register);
+PHPDBG_COMMAND(quit); /* }}} */
+
+/* {{{ prompt commands */
+extern const phpdbg_command_t phpdbg_prompt_commands[]; /* }}} */
+
+/* {{{ */
+#if PHP_VERSION_ID >= 50500
+void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC);
+#else
+void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC);
+#endif /* }}} */
+
+#endif /* PHPDBG_PROMPT_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_set.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_bp.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+PHPDBG_SET(prompt) /* {{{ */
+{
+ switch (param->type) {
+ case EMPTY_PARAM:
+ phpdbg_writeln("%s", phpdbg_get_prompt(TSRMLS_C));
+ break;
+
+ case STR_PARAM:
+ phpdbg_set_prompt(param->str TSRMLS_CC);
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(break) /* {{{ */
+{
+ switch (param->type) {
+ case EMPTY_PARAM:
+ phpdbg_writeln("%s",
+ PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED ? "on" : "off");
+ break;
+
+ case STR_PARAM:
+ if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
+ phpdbg_enable_breakpoints(TSRMLS_C);
+ } else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
+ phpdbg_disable_breakpoints(TSRMLS_C);
+ }
+ break;
+
+ case NUMERIC_PARAM: {
+ if (input->argc > 2) {
+ if (phpdbg_argv_is(2, "on")) {
+ phpdbg_enable_breakpoint(param->num TSRMLS_CC);
+ } else if (phpdbg_argv_is(2, "off")) {
+ phpdbg_disable_breakpoint(param->num TSRMLS_CC);
+ }
+ } else {
+ phpdbg_breakbase_t *brake = phpdbg_find_breakbase(param->num TSRMLS_CC);
+ if (brake) {
+ phpdbg_writeln(
+ "%s", brake->disabled ? "off" : "on");
+ } else {
+ phpdbg_error("Failed to find breakpoint #%lx", param->num);
+ }
+ }
+ } break;
+
+ default:
+ phpdbg_error(
+ "set break used incorrectly: set break [id] <on|off>");
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+#ifndef _WIN32
+PHPDBG_SET(color) /* {{{ */
+{
+ if ((param->type == STR_PARAM) && (input->argc == 3)) {
+ const phpdbg_color_t *color = phpdbg_get_color(
+ input->argv[2]->string, input->argv[2]->length TSRMLS_CC);
+ int element = PHPDBG_COLOR_INVALID;
+
+ /* @TODO(anyone) make this consistent with other set commands */
+ if (color) {
+ if (phpdbg_argv_is(1, "prompt")) {
+ phpdbg_notice(
+ "setting prompt color to %s (%s)", color->name, color->code);
+ element = PHPDBG_COLOR_PROMPT;
+ if (PHPDBG_G(prompt)[1]) {
+ free(PHPDBG_G(prompt)[1]);
+ PHPDBG_G(prompt)[1]=NULL;
+ }
+ } else if (phpdbg_argv_is(1, "error")) {
+ phpdbg_notice(
+ "setting error color to %s (%s)", color->name, color->code);
+ element = PHPDBG_COLOR_ERROR;
+
+ } else if (phpdbg_argv_is(1, "notice")) {
+ phpdbg_notice(
+ "setting notice color to %s (%s)", color->name, color->code);
+ element = PHPDBG_COLOR_NOTICE;
+
+ } else goto usage;
+
+ /* set color for element */
+ phpdbg_set_color(element, color TSRMLS_CC);
+ } else {
+ phpdbg_error(
+ "Failed to find the requested color (%s)", input->argv[2]->string);
+ }
+ } else {
+usage:
+ phpdbg_error(
+ "set color used incorrectly: set color <prompt|error|notice> <color>");
+ }
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(colors) /* {{{ */
+{
+ switch (param->type) {
+ case EMPTY_PARAM: {
+ phpdbg_writeln(
+ "%s", PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "on" : "off");
+ goto done;
+ }
+
+ case STR_PARAM: {
+ if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
+ PHPDBG_G(flags) |= PHPDBG_IS_COLOURED;
+ goto done;
+ } else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_COLOURED;
+ goto done;
+ }
+ }
+
+ default:
+ phpdbg_error(
+ "set colors used incorrectly: set colors <on|off>");
+ }
+
+done:
+ return SUCCESS;
+} /* }}} */
+#endif
+
+PHPDBG_SET(oplog) /* {{{ */
+{
+ switch (param->type) {
+ case EMPTY_PARAM:
+ phpdbg_notice(
+ "Oplog %s", PHPDBG_G(oplog) ? "enabled" : "disabled");
+ break;
+
+ case NUMERIC_PARAM: switch (param->num) {
+ case 1:
+ phpdbg_error(
+ "An output file must be provided to enable oplog");
+ break;
+
+ case 0: {
+ if (PHPDBG_G(oplog)) {
+ phpdbg_notice("Disabling oplog");
+ fclose(
+ PHPDBG_G(oplog));
+ } else {
+ phpdbg_error("Oplog is not enabled!");
+ }
+ } break;
+ } break;
+
+ case STR_PARAM: {
+ /* open oplog */
+ FILE *old = PHPDBG_G(oplog);
+
+ PHPDBG_G(oplog) = fopen(param->str, "w+");
+ if (!PHPDBG_G(oplog)) {
+ phpdbg_error("Failed to open %s for oplog", param->str);
+ PHPDBG_G(oplog) = old;
+ } else {
+ if (old) {
+ phpdbg_notice("Closing previously open oplog");
+ fclose(old);
+ }
+ phpdbg_notice("Successfully opened oplog %s", param->str);
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_SET_H
+#define PHPDBG_SET_H
+
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_SET(name) PHPDBG_COMMAND(set_##name)
+
+PHPDBG_SET(prompt);
+#ifndef _WIN32
+PHPDBG_SET(color);
+PHPDBG_SET(colors);
+#endif
+PHPDBG_SET(oplog);
+PHPDBG_SET(break);
+
+static const phpdbg_command_t phpdbg_set_commands[] = {
+ PHPDBG_COMMAND_D_EX(prompt, "usage: set prompt <string>", 'p', set_prompt, NULL, 0),
+#ifndef _WIN32
+ PHPDBG_COMMAND_D_EX(color, "usage: set color <element> <color>", 'c', set_color, NULL, 1),
+ PHPDBG_COMMAND_D_EX(colors, "usage: set colors <on|off>", 'C', set_colors, NULL, 1),
+#endif
+ PHPDBG_COMMAND_D_EX(oplog, "usage: set oplog <output>", 'O', set_oplog, NULL, 0),
+ PHPDBG_COMMAND_D_EX(break, "usage: set break [id] <on|off>", 'b', set_break, NULL, 0),
+ PHPDBG_END_COMMAND
+};
+
+#endif /* PHPDBG_SET_H */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "zend.h"
+#include "php.h"
+#include "spprintf.h"
+#include "phpdbg.h"
+#include "phpdbg_opcode.h"
+#include "phpdbg_utils.h"
+
+#ifdef _WIN32
+# include "win32/time.h"
+#endif
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+/* {{{ color structures */
+const static phpdbg_color_t colors[] = {
+ PHPDBG_COLOR_D("none", "0;0"),
+
+ PHPDBG_COLOR_D("white", "0;64"),
+ PHPDBG_COLOR_D("white-bold", "1;64"),
+ PHPDBG_COLOR_D("white-underline", "4;64"),
+ PHPDBG_COLOR_D("red", "0;31"),
+ PHPDBG_COLOR_D("red-bold", "1;31"),
+ PHPDBG_COLOR_D("red-underline", "4;31"),
+ PHPDBG_COLOR_D("green", "0;32"),
+ PHPDBG_COLOR_D("green-bold", "1;32"),
+ PHPDBG_COLOR_D("green-underline", "4;32"),
+ PHPDBG_COLOR_D("yellow", "0;33"),
+ PHPDBG_COLOR_D("yellow-bold", "1;33"),
+ PHPDBG_COLOR_D("yellow-underline", "4;33"),
+ PHPDBG_COLOR_D("blue", "0;34"),
+ PHPDBG_COLOR_D("blue-bold", "1;34"),
+ PHPDBG_COLOR_D("blue-underline", "4;34"),
+ PHPDBG_COLOR_D("purple", "0;35"),
+ PHPDBG_COLOR_D("purple-bold", "1;35"),
+ PHPDBG_COLOR_D("purple-underline", "4;35"),
+ PHPDBG_COLOR_D("cyan", "0;36"),
+ PHPDBG_COLOR_D("cyan-bold", "1;36"),
+ PHPDBG_COLOR_D("cyan-underline", "4;36"),
+ PHPDBG_COLOR_D("black", "0;30"),
+ PHPDBG_COLOR_D("black-bold", "1;30"),
+ PHPDBG_COLOR_D("black-underline", "4;30"),
+ PHPDBG_COLOR_END
+}; /* }}} */
+
+PHPDBG_API int phpdbg_is_numeric(const char *str) /* {{{ */
+{
+ if (!str)
+ return 0;
+
+ for (; *str; str++) {
+ if (isspace(*str) || *str == '-') {
+ continue;
+ }
+ return isdigit(*str);
+ }
+ return 0;
+} /* }}} */
+
+PHPDBG_API int phpdbg_is_empty(const char *str) /* {{{ */
+{
+ if (!str)
+ return 1;
+
+ for (; *str; str++) {
+ if (isspace(*str)) {
+ continue;
+ }
+ return 0;
+ }
+ return 1;
+} /* }}} */
+
+PHPDBG_API int phpdbg_is_addr(const char *str) /* {{{ */
+{
+ return str[0] && str[1] && memcmp(str, "0x", 2) == 0;
+} /* }}} */
+
+PHPDBG_API int phpdbg_is_class_method(const char *str, size_t len, char **class, char **method) /* {{{ */
+{
+ char *sep = NULL;
+
+ if (strstr(str, "#") != NULL)
+ return 0;
+
+ if (strstr(str, " ") != NULL)
+ return 0;
+
+ sep = strstr(str, "::");
+
+ if (!sep || sep == str || sep+2 == str+len-1) {
+ return 0;
+ }
+
+ if (class != NULL) {
+
+ if (str[0] == '\\') {
+ str++;
+ len--;
+ }
+
+ *class = estrndup(str, sep - str);
+ (*class)[sep - str] = 0;
+ }
+
+ if (method != NULL) {
+ *method = estrndup(sep+2, str + len - (sep + 2));
+ }
+
+ return 1;
+} /* }}} */
+
+PHPDBG_API char *phpdbg_resolve_path(const char *path TSRMLS_DC) /* {{{ */
+{
+ char resolved_name[MAXPATHLEN];
+
+ if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
+ return NULL;
+ }
+
+ return estrdup(resolved_name);
+} /* }}} */
+
+PHPDBG_API const char *phpdbg_current_file(TSRMLS_D) /* {{{ */
+{
+ const char *file = zend_get_executed_filename(TSRMLS_C);
+
+ if (memcmp(file, "[no active file]", sizeof("[no active file]")) == 0) {
+ return PHPDBG_G(exec);
+ }
+
+ return file;
+} /* }}} */
+
+PHPDBG_API const zend_function *phpdbg_get_function(const char *fname, const char *cname TSRMLS_DC) /* {{{ */
+{
+ zend_function *func = NULL;
+ size_t fname_len = strlen(fname);
+ char *lcname = zend_str_tolower_dup(fname, fname_len);
+
+ if (cname) {
+ zend_class_entry **ce;
+ size_t cname_len = strlen(cname);
+ char *lc_cname = zend_str_tolower_dup(cname, cname_len);
+ int ret = zend_lookup_class(lc_cname, cname_len, &ce TSRMLS_CC);
+
+ efree(lc_cname);
+
+ if (ret == SUCCESS) {
+ zend_hash_find(&(*ce)->function_table, lcname, fname_len+1,
+ (void**)&func);
+ }
+ } else {
+ zend_hash_find(EG(function_table), lcname, fname_len+1,
+ (void**)&func);
+ }
+
+ efree(lcname);
+ return func;
+} /* }}} */
+
+PHPDBG_API char *phpdbg_trim(const char *str, size_t len, size_t *new_len) /* {{{ */
+{
+ const char *p = str;
+ char *new = NULL;
+
+ while (p && isspace(*p)) {
+ ++p;
+ --len;
+ }
+
+ while (*p && isspace(*(p + len -1))) {
+ --len;
+ }
+
+ if (len == 0) {
+ new = estrndup("", sizeof(""));
+ *new_len = 0;
+ } else {
+ new = estrndup(p, len);
+ *(new + len) = '\0';
+
+ if (new_len) {
+ *new_len = len;
+ }
+ }
+
+ return new;
+
+} /* }}} */
+
+PHPDBG_API int phpdbg_print(int type TSRMLS_DC, FILE *fp, const char *format, ...) /* {{{ */
+{
+ int rc = 0;
+ char *buffer = NULL;
+ va_list args;
+
+ if (format != NULL && strlen(format) > 0L) {
+ va_start(args, format);
+ vspprintf(&buffer, 0, format, args);
+ va_end(args);
+ }
+
+ /* TODO(anyone) colours */
+
+ switch (type) {
+ case P_ERROR:
+ if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
+ rc = fprintf(fp,
+ "\033[%sm[%s]\033[0m\n",
+ PHPDBG_G(colors)[PHPDBG_COLOR_ERROR]->code, buffer);
+ } else {
+ rc = fprintf(fp, "[%s]\n", buffer);
+ }
+ break;
+
+ case P_NOTICE:
+ if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
+ rc = fprintf(fp,
+ "\033[%sm[%s]\033[0m\n",
+ PHPDBG_G(colors)[PHPDBG_COLOR_NOTICE]->code, buffer);
+ } else {
+ rc = fprintf(fp, "[%s]\n", buffer);
+ }
+ break;
+
+ case P_WRITELN: {
+ if (buffer) {
+ rc = fprintf(fp, "%s\n", buffer);
+ } else {
+ rc = fprintf(fp, "\n");
+ }
+ } break;
+
+ case P_WRITE:
+ if (buffer) {
+ rc = fprintf(fp, "%s", buffer);
+ }
+ break;
+
+ /* no formatting on logging output */
+ case P_LOG:
+ if (buffer) {
+ struct timeval tp;
+ if (gettimeofday(&tp, NULL) == SUCCESS) {
+ rc = fprintf(fp, "[%ld %.8F]: %s\n", tp.tv_sec, tp.tv_usec / 1000000.00, buffer);
+ } else {
+ rc = FAILURE;
+ }
+ }
+ break;
+ }
+
+ if (buffer) {
+ efree(buffer);
+ }
+
+ return rc;
+} /* }}} */
+
+PHPDBG_API int phpdbg_rlog(FILE *fp, const char *fmt, ...) { /* {{{ */
+ int rc = 0;
+
+ va_list args;
+ struct timeval tp;
+
+ va_start(args, fmt);
+ if (gettimeofday(&tp, NULL) == SUCCESS) {
+ char friendly[100];
+ char *format = NULL, *buffer = NULL;
+
+ strftime(friendly, 100, "%a %b %d %T.%%04d %Y", localtime(&tp.tv_sec));
+ asprintf(
+ &buffer, friendly, tp.tv_usec/1000);
+ asprintf(
+ &format, "[%s]: %s\n", buffer, fmt);
+ rc = vfprintf(
+ fp, format, args);
+
+ free(format);
+ free(buffer);
+ }
+ va_end(args);
+
+ return rc;
+} /* }}} */
+
+PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length TSRMLS_DC) /* {{{ */
+{
+ const phpdbg_color_t *color = colors;
+
+ while (color && color->name) {
+ if (name_length == color->name_length &&
+ memcmp(name, color->name, name_length) == SUCCESS) {
+ phpdbg_debug(
+ "phpdbg_get_color(%s, %lu): %s", name, name_length, color->code);
+ return color;
+ }
+ ++color;
+ }
+
+ phpdbg_debug(
+ "phpdbg_get_color(%s, %lu): failed", name, name_length);
+
+ return NULL;
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_color(int element, const phpdbg_color_t *color TSRMLS_DC) /* {{{ */
+{
+ PHPDBG_G(colors)[element] = color;
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_color_ex(int element, const char *name, size_t name_length TSRMLS_DC) /* {{{ */
+{
+ const phpdbg_color_t *color = phpdbg_get_color(name, name_length TSRMLS_CC);
+
+ if (color) {
+ phpdbg_set_color(element, color TSRMLS_CC);
+ } else PHPDBG_G(colors)[element] = colors;
+} /* }}} */
+
+PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D) /* {{{ */
+{
+ return colors;
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_prompt(const char *prompt TSRMLS_DC) /* {{{ */
+{
+ /* free formatted prompt */
+ if (PHPDBG_G(prompt)[1]) {
+ free(PHPDBG_G(prompt)[1]);
+ PHPDBG_G(prompt)[1] = NULL;
+ }
+ /* free old prompt */
+ if (PHPDBG_G(prompt)[0]) {
+ free(PHPDBG_G(prompt)[0]);
+ PHPDBG_G(prompt)[0] = NULL;
+ }
+
+ /* copy new prompt */
+ PHPDBG_G(prompt)[0] = strdup(prompt);
+} /* }}} */
+
+PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D) /* {{{ */
+{
+ /* find cached prompt */
+ if (PHPDBG_G(prompt)[1]) {
+ return PHPDBG_G(prompt)[1];
+ }
+
+ /* create cached prompt */
+ if ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED)) {
+ asprintf(
+ &PHPDBG_G(prompt)[1], "\033[%sm%s\033[0m ",
+ PHPDBG_G(colors)[PHPDBG_COLOR_PROMPT]->code,
+ PHPDBG_G(prompt)[0]);
+ } else {
+ asprintf(
+ &PHPDBG_G(prompt)[1], "%s ",
+ PHPDBG_G(prompt)[0]);
+ }
+
+ return PHPDBG_G(prompt)[1];
+} /* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_UTILS_H
+#define PHPDBG_UTILS_H
+
+/**
+ * Input scan functions
+ */
+PHPDBG_API int phpdbg_is_numeric(const char*);
+PHPDBG_API int phpdbg_is_empty(const char*);
+PHPDBG_API int phpdbg_is_addr(const char*);
+PHPDBG_API int phpdbg_is_class_method(const char*, size_t, char**, char**);
+PHPDBG_API const char *phpdbg_current_file(TSRMLS_D);
+PHPDBG_API char *phpdbg_resolve_path(const char* TSRMLS_DC);
+PHPDBG_API char *phpdbg_trim(const char*, size_t, size_t*);
+PHPDBG_API const zend_function *phpdbg_get_function(const char*, const char* TSRMLS_DC);
+
+/**
+ * Error/notice/formatting helpers
+ */
+enum {
+ P_ERROR = 1,
+ P_NOTICE,
+ P_WRITELN,
+ P_WRITE,
+ P_LOG
+};
+
+#ifdef ZTS
+PHPDBG_API int phpdbg_print(int TSRMLS_DC, FILE*, const char*, ...) PHP_ATTRIBUTE_FORMAT(printf, 4, 5);
+#else
+PHPDBG_API int phpdbg_print(int TSRMLS_DC, FILE*, const char*, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4);
+#endif
+
+PHPDBG_API int phpdbg_rlog(FILE *stream, const char *fmt, ...);
+
+#define phpdbg_error(fmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
+#define phpdbg_notice(fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
+#define phpdbg_writeln(fmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
+#define phpdbg_write(fmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
+#define phpdbg_log(fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
+
+#define phpdbg_error_ex(out, fmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, out, fmt, ##__VA_ARGS__)
+#define phpdbg_notice_ex(out, fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, out, fmt, ##__VA_ARGS__)
+#define phpdbg_writeln_ex(out, fmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, out, fmt, ##__VA_ARGS__)
+#define phpdbg_write_ex(out, fmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, out, fmt, ##__VA_ARGS__)
+#define phpdbg_log_ex(out, fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, out, fmt, ##__VA_ARGS__)
+
+#if PHPDBG_DEBUG
+# define phpdbg_debug(fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDERR], fmt, ##__VA_ARGS__)
+#else
+# define phpdbg_debug(fmt, ...)
+#endif
+
+/* {{{ For writing blank lines */
+#define EMPTY NULL /* }}} */
+
+/* {{{ For prompt lines */
+#define PROMPT "phpdbg>" /* }}} */
+
+/* {{{ For separation */
+#define SEPARATE "------------------------------------------------" /* }}} */
+
+/* {{{ Color Management */
+#define PHPDBG_COLOR_LEN 12
+#define PHPDBG_COLOR_D(color, code) \
+ {color, sizeof(color)-1, code}
+#define PHPDBG_COLOR_END \
+ {NULL, 0L, {0}}
+
+#define PHPDBG_COLOR_INVALID -1
+#define PHPDBG_COLOR_PROMPT 0
+#define PHPDBG_COLOR_ERROR 1
+#define PHPDBG_COLOR_NOTICE 2
+#define PHPDBG_COLORS 3
+
+typedef struct _phpdbg_color_t {
+ char *name;
+ size_t name_length;
+ const char code[PHPDBG_COLOR_LEN];
+} phpdbg_color_t;
+
+PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length TSRMLS_DC);
+PHPDBG_API void phpdbg_set_color(int element, const phpdbg_color_t *color TSRMLS_DC);
+PHPDBG_API void phpdbg_set_color_ex(int element, const char *name, size_t name_length TSRMLS_DC);
+PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D); /* }}} */
+
+/* {{{ Prompt Management */
+PHPDBG_API void phpdbg_set_prompt(const char* TSRMLS_DC);
+PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D); /* }}} */
+
+#endif /* PHPDBG_UTILS_H */
--- /dev/null
+<?php
+if (isset($include)) {
+ include (sprintf("%s/web-bootstrap.php", dirname(__FILE__)));
+}
+
+$stdout = fopen("php://stdout", "w+");
+
+class phpdbg {
+ public function isGreat($greeting = null) {
+ printf(
+ "%s: %s\n", __METHOD__, $greeting);
+ return $this;
+ }
+}
+
+function test($x, $y = 0) {
+ $var = $x + 1;
+ $var += 2;
+ $var <<= 3;
+
+ $foo = function () {
+ echo "bar!\n";
+ };
+
+ $foo();
+
+ yield $var;
+}
+
+$dbg = new phpdbg();
+
+var_dump(
+ $dbg->isGreat("PHP Rocks!!"));
+
+foreach (test(1,2) as $gen)
+ continue;
+
+echo "it works!\n";
+
+if (isset($dump))
+ var_dump($_SERVER);
+
+function phpdbg_test_ob()
+{
+ echo 'Start';
+ ob_start();
+ echo 'Hello';
+ $b = ob_get_clean();
+ echo 'End';
+ echo $b;
+}
--- /dev/null
+#######################################################
+# name: basic
+# purpose: check basic functionality of phpdbg console
+# expect: TEST::EXACT
+# options: -rr
+#######################################################
+# [Nothing to execute!]
+#######################################################
--- /dev/null
+#################################################
+# name: set
+# purpose: tests for set commands
+# expect: TEST::CISTRING
+# options: -rr
+#################################################
+# setting prompt color
+# setting error color
+# setting notice color
+# Failed to find breakpoint #0
+# oplog disabled
+# not enabled
+# opened oplog test.log
+# nothing
+#################################################
+set color prompt none
+set color error none
+set color notice none
+set prompt promot>
+set break 0
+set oplog
+set oplog 0
+set oplog test.log
--- /dev/null
+#################################################
+# name: info
+# purpose: test info commands
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#[User Classes (%d)]
+#User Class test (3)
+#|---- in phpdbginit code on line %d
+#################################################
+<:
+class test {
+ public function testMethod(){}
+ private function testPrivateMethod(){}
+ protected function testProtectedMethod(){}
+}
+:>
+info classes
+q
--- /dev/null
+#################################################
+# name: print
+# purpose: test print commands
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#[User Class: test]
+#Methods (3):
+#L%d-%d test::testMethod() %s
+# L%d %s ZEND_RETURN C%d <unused> <unused>
+# L%d-%d test::testPrivateMethod() %s
+# L%d %s ZEND_RETURN C%d <unused> <unused>
+# L%d-%d test::testProtectedMethod() %s
+# L%d %s ZEND_RETURN C%d <unused> <unused>
+#[User Method testMethod]
+# L%d-%d test::testMethod() %s
+# L%d %s ZEND_RETURN C%d <unused> <unused>
+#################################################
+<:
+class test {
+ public function testMethod(){}
+ private function testPrivateMethod(){}
+ protected function testProtectedMethod(){}
+}
+:>
+print class test
+print method test::testMethod
+q
--- /dev/null
+#################################################
+# name: register
+# purpose: test registration functions
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#[Registered test_function]
+#array(5) {
+# [0]=>
+# string(1) "1"
+# [1]=>
+# string(1) "2"
+# [2]=>
+# string(1) "3"
+# [3]=>
+# string(1) "4"
+# [4]=>
+# string(1) "5"
+#}
+#################################################
+<:
+function test_function() {
+ var_dump(func_get_args());
+}
+:>
+R test_function
+test_function 1 2 3 4 5
+q
--- /dev/null
+#################################################
+# name: clean
+# purpose: test cleaning environment
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#[Cleaning Execution Environment]
+#Classes %d
+#Functions %d
+#Constants %d
+#Includes %d
+#[Nothing to execute!]
+#################################################
+clean
+quit
--- /dev/null
+#################################################
+# name: clear
+# purpose: test clearing breakpoints
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#[Clearing Breakpoints]
+#File%w%d
+#Functions%w%d
+#Methods%w%d
+#Oplines%w%d
+#File oplines%w%d
+#Function oplines%w%d
+#Method oplines%w%d
+#Conditionals%w%d
+#################################################
+clear
+quit
--- /dev/null
+#################################################
+# name: compile
+# purpose: test compiling code
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#[Attempting compilation of %s]
+#[Success]
+#Hello World
+#################################################
+<:
+define('OUT',
+ tempnam(null, "phpdbg"));
+file_put_contents(OUT, "<?php echo \"Hello World\"; ?>");
+phpdbg_exec(OUT);
+:>
+compile
+run
+quit
--- /dev/null
+<?php
+namespace phpdbg\testing {
+
+ /*
+ * Workaround ...
+ */
+ if (!defined('DIR_SEP'))
+ define('DIR_SEP', '\\' . DIRECTORY_SEPARATOR);
+
+ /**
+ * TestConfigurationExceptions are thrown
+ * when the configuration prohibits tests executing
+ *
+ * @package phpdbg
+ * @subpackage testing
+ */
+ class TestConfigurationException extends \Exception {
+
+ /**
+ *
+ * @param array Tests confguration
+ * @param message Exception message
+ * @param ... formatting parameters
+ */
+ public function __construct() {
+ $argv = func_get_args();
+
+ if (count($argv)) {
+
+ $this->config = array_shift($argv);
+ $this->message = vsprintf(
+ array_shift($argv), $argv);
+ }
+ }
+ }
+
+ /**
+ *
+ * @package phpdbg
+ * @subpackage testing
+ */
+ class TestsConfiguration implements \ArrayAccess {
+
+ /**
+ *
+ * @param array basic configuration
+ * @param array argv
+ */
+ public function __construct($config, $cmd) {
+ $this->options = $config;
+ while (($key = array_shift($cmd))) {
+ switch (substr($key, 0, 1)) {
+ case '-': switch(substr($key, 1, 1)) {
+ case '-': {
+ $arg = substr($key, 2);
+ if (($e=strpos($arg, '=')) !== false) {
+ $key = substr($arg, 0, $e);
+ $value = substr($arg, $e+1);
+ } else {
+ $key = $arg;
+ $value = array_shift($cmd);
+ }
+
+ if (isset($key) && isset($value)) {
+ switch ($key) {
+ case 'phpdbg':
+ case 'width':
+ $this->options[$key] = $value;
+ break;
+
+ default: {
+ if (isset($config[$key])) {
+ if (is_array($config[$key])) {
+ $this->options[$key][] = $value;
+ } else {
+ $this->options[$key] = array($config[$key], $value);
+ }
+ } else {
+ $this->options[$key] = $value;
+ }
+ }
+ }
+
+ }
+ } break;
+
+ default:
+ $this->flags[] = substr($key, 1);
+ } break;
+ }
+ }
+
+ if (!is_executable($this->options['phpdbg'])) {
+ throw new TestConfigurationException(
+ $this->options, 'phpdbg could not be found at the specified path (%s)', $this->options['phpdbg']);
+ } else $this->options['phpdbg'] = realpath($this->options['phpdbg']);
+
+ $this->options['width'] = (integer) $this->options['width'];
+
+ /* display properly, all the time */
+ if ($this->options['width'] < 50) {
+ $this->options['width'] = 50;
+ }
+
+ /* calculate column widths */
+ $this->options['lwidth'] = ceil($this->options['width'] / 3);
+ $this->options['rwidth'] = ceil($this->options['width'] - $this->options['lwidth']) - 5;
+ }
+
+ public function hasFlag($flag) {
+ return in_array(
+ $flag, $this->flags);
+ }
+
+ public function offsetExists($offset) { return isset($this->options[$offset]); }
+ public function offsetGet($offset) { return $this->options[$offset]; }
+ public function offsetUnset($offset) { unset($this->options[$offset]); }
+ public function offsetSet($offset, $data) { $this->options[$offset] = $data; }
+
+ protected $options = array();
+ protected $flags = array();
+ }
+
+ /**
+ * Tests is the console programming API for the test suite
+ *
+ * @package phpdbg
+ * @subpackage testing
+ */
+ class Tests {
+
+ /**
+ * Construct the console object
+ *
+ * @param array basic configuration
+ * @param array command line
+ */
+ public function __construct(TestsConfiguration &$config) {
+ $this->config = &$config;
+
+ if ($this->config->hasFlag('help') ||
+ $this->config->hasFlag('h')) {
+ $this->showUsage();
+ exit;
+ }
+ }
+
+ /**
+ * Find valid paths as specified by configuration
+ *
+ */
+ public function findPaths($in = null) {
+ $paths = array();
+ $where = ($in != null) ? array($in) : $this->config['path'];
+
+ foreach ($where as &$path) {
+ if ($path) {
+ if (is_dir($path)) {
+ $paths[] = $path;
+ foreach (scandir($path) as $child) {
+ if ($child != '.' && $child != '..') {
+ $paths = array_merge(
+ $paths, $this->findPaths("$path/$child"));
+ }
+ }
+ }
+ }
+ }
+
+ return $paths;
+ }
+
+ /**
+ *
+ * @param string the path to log
+ */
+ public function logPath($path) {
+ printf(
+ '%s [%s]%s',
+ str_repeat(
+ '-', $this->config['width'] - strlen($path)),
+ $path, PHP_EOL);
+ }
+
+ /**
+ *
+ * @param string the path to log
+ */
+ public function logPathStats($path) {
+ if (!isset($this->stats[$path])) {
+ return;
+ }
+
+ $total = array_sum($this->stats[$path]);
+
+ if ($total) {
+ @$this->totals[true] += $this->stats[$path][true];
+ @$this->totals[false] += $this->stats[$path][false];
+
+ $stats = @sprintf(
+ "%d/%d %%%d",
+ $this->stats[$path][true],
+ $this->stats[$path][false],
+ (100 / $total) * $this->stats[$path][true]);
+
+ printf(
+ '%s [%s]%s',
+ str_repeat(
+ ' ', $this->config['width'] - strlen($stats)),
+ $stats, PHP_EOL);
+
+ printf("%s%s", str_repeat('-', $this->config['width']+3), PHP_EOL);
+ printf("%s", PHP_EOL);
+ }
+ }
+
+ /**
+ *
+ */
+ public function logStats() {
+ $total = array_sum($this->totals);
+ $stats = @sprintf(
+ "%d/%d %%%d",
+ $this->totals[true],
+ $this->totals[false],
+ (100 / $total) * $this->totals[true]);
+ printf(
+ '%s [%s]%s',
+ str_repeat(
+ ' ', $this->config['width'] - strlen($stats)),
+ $stats, PHP_EOL);
+
+ }
+
+ /**
+ *
+ */
+ protected function showUsage() {
+ printf('usage: php %s [flags] [options]%s', $this->config['exec'], PHP_EOL);
+ printf('[options]:%s', PHP_EOL);
+ printf("\t--path\t\tadd a path to scan outside of tests directory%s", PHP_EOL);
+ printf("\t--width\t\tset line width%s", PHP_EOL);
+ printf("\t--options\toptions to pass to phpdbg%s", PHP_EOL);
+ printf("\t--phpdbg\tpath to phpdbg binary%s", PHP_EOL);
+ printf('[flags]:%s', PHP_EOL);
+ printf("\t-nodiff\t\tdo not write diffs on failure%s", PHP_EOL);
+ printf("\t-nolog\t\tdo not write logs on failure%s", PHP_EOL);
+ printf('[examples]:%s', PHP_EOL);
+ printf("\tphp %s --phpdbg=/usr/local/bin/phpdbg --path=/usr/src/phpdbg/tests --options -n%s",
+ $this->config['exec'], PHP_EOL);
+
+ }
+
+ /**
+ * Find valid tests at the specified path (assumed valid)
+ *
+ * @param string a valid path
+ */
+ public function findTests($path) {
+ $tests = array();
+
+ foreach (scandir($path) as $file) {
+ if ($file == '.' || $file == '..')
+ continue;
+
+ $test = sprintf('%s/%s', $path, $file);
+
+ if (preg_match('~\.test$~', $test)) {
+ yield new Test($this->config, $test);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param Test the test to log
+ */
+ public function logTest($path, Test $test) {
+ @$this->stats[$path][($result=$test->getResult())]++;
+
+ printf(
+ "%-{$this->config['lwidth']}s %-{$this->config['rwidth']}s [%s]%s",
+ $test->name,
+ $test->purpose,
+ $result ? "PASS" : "FAIL",
+ PHP_EOL);
+ }
+
+ protected $config;
+ }
+
+ class Test {
+ /*
+ * Expect exact line for line match
+ */
+ const EXACT = 0x00000001;
+
+ /*
+ * Expect strpos() !== false
+ */
+ const STRING = 0x00000010;
+
+ /*
+ * Expect stripos() !== false
+ */
+ const CISTRING = 0x00000100;
+
+ /*
+ * Formatted output
+ */
+ const FORMAT = 0x00001000;
+
+ /**
+ * Format specifiers
+ */
+ private static $format = array(
+ 'search' => array(
+ '%e',
+ '%s',
+ '%S',
+ '%a',
+ '%A',
+ '%w',
+ '%i',
+ '%d',
+ '%x',
+ '%f',
+ '%c',
+ '%t',
+ '%T'
+ ),
+ 'replace' => array(
+ DIR_SEP,
+ '[^\r\n]+',
+ '[^\r\n]*',
+ '.+',
+ '.*',
+ '\s*',
+ '[+-]?\d+',
+ '\d+',
+ '[0-9a-fA-F]+',
+ '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?',
+ '.',
+ '\t',
+ '\t+'
+ )
+ );
+
+ /**
+ * Constructs a new Test object given a specilized phpdbginit file
+ *
+ * @param array configuration
+ * @param string file
+ */
+ public function __construct(TestsConfiguration &$config, &$file) {
+ if (($handle = fopen($file, 'r'))) {
+ while (($line = fgets($handle))) {
+ $trim = trim($line);
+
+ switch (substr($trim, 0, 1)) {
+ case '#': if (($chunks = array_map('trim', preg_split('~:~', substr($trim, 1), 2)))) {
+ if (property_exists($this, $chunks[0])) {
+ switch ($chunks[0]) {
+ case 'expect': {
+ if ($chunks[1]) {
+ switch (strtoupper($chunks[1])) {
+ case 'TEST::EXACT':
+ case 'EXACT': { $this->expect = TEST::EXACT; } break;
+
+ case 'TEST::STRING':
+ case 'STRING': { $this->expect = TEST::STRING; } break;
+
+ case 'TEST::CISTRING':
+ case 'CISTRING': { $this->expect = TEST::CISTRING; } break;
+
+ case 'TEST::FORMAT':
+ case 'FORMAT': { $this->expect = TEST::FORMAT; } break;
+
+ default:
+ throw new TestConfigurationException(
+ $this->config, "unknown type of expectation (%s)", $chunks[1]);
+ }
+ }
+ } break;
+
+ default: {
+ $this->$chunks[0] = $chunks[1];
+ }
+ }
+ } else switch(substr($trim, 1, 1)) {
+ case '#': { /* do nothing */ } break;
+
+ default: {
+ $line = preg_replace(
+ "~(\r\n)~", "\n", substr($trim, 1));
+
+ $line = trim($line);
+
+ switch ($this->expect) {
+ case TEST::FORMAT:
+ $this->match[] = str_replace(
+ self::$format['search'],
+ self::$format['replace'], preg_quote($line));
+ break;
+
+ default: $this->match[] = $line;
+ }
+ }
+ }
+ } break;
+
+ default:
+ break 2;
+ }
+ }
+ fclose($handle);
+
+ $this->config = &$config;
+ $this->file = &$file;
+ }
+ }
+
+ /**
+ * Obvious!!
+ *
+ */
+ public function getResult() {
+ $options = sprintf(
+ '-i%s -qb', $this->file);
+
+ if ($this->options) {
+ $options = sprintf(
+ '%s %s %s',
+ $options,
+ $this->config['options'],
+ $this->options
+ );
+ } else {
+ $options = sprintf(
+ '%s %s', $options, $this->config['options']
+ );
+ }
+
+ $result = `{$this->config['phpdbg']} {$options}`;
+
+ if ($result) {
+ foreach (preg_split('~(\r|\n)~', $result) as $num => $line) {
+ if (!$line && !isset($this->match[$num]))
+ continue;
+
+ switch ($this->expect) {
+ case TEST::EXACT: {
+ if (strcmp($line, $this->match[$num]) !== 0) {
+ $this->diff['wants'][$num] = &$this->match[$num];
+ $this->diff['gets'][$num] = $line;
+ }
+ } continue 2;
+
+ case TEST::STRING: {
+ if (strpos($line, $this->match[$num]) === false) {
+ $this->diff['wants'][$num] = &$this->match[$num];
+ $this->diff['gets'][$num] = $line;
+ }
+ } continue 2;
+
+ case TEST::CISTRING: {
+ if (stripos($line, $this->match[$num]) === false) {
+ $this->diff['wants'][$num] = &$this->match[$num];
+ $this->diff['gets'][$num] = $line;
+ }
+ } continue 2;
+
+ case TEST::FORMAT: {
+ $line = trim($line);
+ if (!preg_match("/^{$this->match[$num]}\$/s", $line)) {
+ $this->diff['wants'][$num] = &$this->match[$num];
+ $this->diff['gets'][$num] = $line;
+ }
+ } continue 2;
+ }
+ }
+ }
+
+ $this->writeLog($result);
+ $this->writeDiff();
+
+ return (count($this->diff) == 0);
+ }
+
+ /**
+ * Write diff to disk if configuration allows it
+ *
+ */
+ protected function writeDiff() {
+ $diff = sprintf(
+ '%s/%s.diff',
+ dirname($this->file), basename($this->file));
+
+ if (count($this->diff['wants'])) {
+ if (!in_array('nodiff', $this->config['flags'])) {
+ if (($diff = fopen($diff, 'w+'))) {
+
+ foreach ($this->diff['wants'] as $line => $want) {
+ $got = $this->diff['gets'][$line];
+
+ fprintf(
+ $diff, '(%d) -%s%s', $line+1, $want, PHP_EOL);
+ fprintf(
+ $diff, '(%d) +%s%s', $line+1, $got, PHP_EOL);
+ }
+
+ fclose($diff);
+ }
+ }
+ } else unlink($diff);
+ }
+
+ /**
+ * Write log to disk if configuration allows it
+ *
+ */
+ protected function writeLog(&$result = null) {
+ $log = sprintf(
+ '%s/%s.log',
+ dirname($this->file), basename($this->file));
+
+ if (count($this->diff) && $result) {
+ if (!in_array('nolog', $this->config['flags'])) {
+ @file_put_contents(
+ $log, $result);
+ }
+ } else unlink($log);
+ }
+
+ public $name;
+ public $purpose;
+ public $file;
+ public $options;
+ public $expect;
+
+ protected $match;
+ protected $diff;
+ protected $stats;
+ protected $totals;
+ }
+}
+
+namespace {
+ use \phpdbg\Testing\Test;
+ use \phpdbg\Testing\Tests;
+ use \phpdbg\Testing\TestsConfiguration;
+
+ $cwd = dirname(__FILE__);
+ $cmd = $_SERVER['argv'];
+ {
+ $config = new TestsConfiguration(array(
+ 'exec' => realpath(array_shift($cmd)),
+ 'phpdbg' => realpath(sprintf(
+ '%s/../phpdbg', $cwd
+ )),
+ 'path' => array(
+ realpath(dirname(__FILE__))
+ ),
+ 'flags' => array(),
+ 'width' => 75
+ ), $cmd);
+
+ $tests = new Tests($config);
+
+ foreach ($tests->findPaths() as $path) {
+ $tests->logPath($path);
+
+ foreach ($tests->findTests($path) as $test) {
+ $tests->logTest($path, $test);
+ }
+
+ $tests->logPathStats($path);
+ }
+
+ $tests->logStats();
+ }
+}
+?>
--- /dev/null
+#!/usr/bin/env sh
+git clone https://github.com/php/php-src
+cd php-src/sapi
+git clone https://github.com/krakjoe/phpdbg.git
+cd ../
+./buildconf --force
+./configure --disable-all --enable-phpdbg --enable-maintainer-zts
+make
+make test-phpdbg
--- /dev/null
+<?php
+/**
+ * The following file shows how to bootstrap phpdbg so that you can mock specific server environments
+ *
+ * eval include("web-bootstrap.php")
+ * exec index.php
+ * compile
+ * break ...
+ * run
+ */
+if (!defined('PHPDBG_BOOTSTRAPPED'))
+{
+ /* define these once */
+ define("PHPDBG_BOOTPATH", "/opt/php-zts/htdocs");
+ define("PHPDBG_BOOTSTRAP", "index.php");
+ define("PHPDBG_BOOTSTRAPPED", sprintf("/%s", PHPDBG_BOOTSTRAP));
+}
+
+/*
+ * Superglobals are JIT, phpdbg will not over-write whatever you set during bootstrap
+ */
+
+$_SERVER = array
+(
+ 'HTTP_HOST' => 'localhost',
+ 'HTTP_CONNECTION' => 'keep-alive',
+ 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+ 'HTTP_USER_AGENT' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.65 Safari/537.36',
+ 'HTTP_ACCEPT_ENCODING' => 'gzip,deflate,sdch',
+ 'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8',
+ 'HTTP_COOKIE' => 'tz=Europe%2FLondon; __utma=1.347100075.1384196523.1384196523.1384196523.1; __utmc=1; __utmz=1.1384196523.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)',
+ 'PATH' => '/usr/local/bin:/usr/bin:/bin',
+ 'SERVER_SIGNATURE' => '<address>Apache/2.4.6 (Ubuntu) Server at phpdbg.com Port 80</address>',
+ 'SERVER_SOFTWARE' => 'Apache/2.4.6 (Ubuntu)',
+ 'SERVER_NAME' => 'localhost',
+ 'SERVER_ADDR' => '127.0.0.1',
+ 'SERVER_PORT' => '80',
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'DOCUMENT_ROOT' => PHPDBG_BOOTPATH,
+ 'REQUEST_SCHEME' => 'http',
+ 'CONTEXT_PREFIX' => '',
+ 'CONTEXT_DOCUMENT_ROOT' => PHPDBG_BOOTPATH,
+ 'SERVER_ADMIN' => '[no address given]',
+ 'SCRIPT_FILENAME' => sprintf(
+ '%s/%s', PHPDBG_BOOTPATH, PHPDBG_BOOTSTRAP
+ ),
+ 'REMOTE_PORT' => '47931',
+ 'GATEWAY_INTERFACE' => 'CGI/1.1',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'REQUEST_URI' => PHPDBG_BOOTSTRAPPED,
+ 'SCRIPT_NAME' => PHPDBG_BOOTSTRAPPED,
+ 'PHP_SELF' => PHPDBG_BOOTSTRAPPED,
+ 'REQUEST_TIME' => time(),
+);
+
+$_GET = array();
+$_REQUEST = array();
+$_POST = array();
+$_COOKIE = array();
+$_FILES = array();
+
+chdir(PHPDBG_BOOTPATH);